2. Styling¶
Styling is inline and typed — no stylesheets, no cascade, no specificity. A
Style is a Pydantic object each renderer translates to its target (CSS on the
web, Qt/Compose properties on native).
from tempest_core import Container, Style, Text, Widget
from tempest_core import Color, Edge
def card() -> Widget:
return Container(
key="card",
style=Style(
padding=Edge.all(16), # (1)!
gap=8.0,
background=Color.from_hex("#f5f5f5"), # (2)!
radius=12.0,
),
children=[Text(content="In a card", key="t")],
)
Edge.all(16)= 16px on all four sides. There's alsoEdge.symmetric( vertical=…, horizontal=…)andEdge(top=…, right=…, …).Color.from_hex("#f5f5f5")→Color(r, g, b, a). On the web it becomesrgba(...); the value crosses the boundary as{r, g, b, a}.
Implicit animation¶
Declare a Transition and a property change animates instead of snapping:
from tempest_core import Curve, Transition
Style(transition=Transition(duration_ms=300, curve=Curve.EASE_IN_OUT))
Theme = Style values
There's no magic theme engine: a theme is just a set of Color/Style your
view applies. Switching themes is the view producing different Styles.
HTML escape hatch (tag / attrs)¶
Every Widget carries two optional "renderer hint" fields — tag and attrs —
for the HTML/SSR renderer (tempestweb). They're ignored by the native renderers
(Qt/Compose), just like semantics/focusable already are.
from tempest_core import Container, Text, Widget
def navbar() -> Widget:
return Container(
key="nav",
tag="nav", # (1)!
attrs={"id": "top", "aria-label": "Primary"}, # (2)!
children=[
Text(
content="Home",
tag="a",
attrs={"href": "/", "hx-get": "/", "hx-target": "#main"}, # (3)!
),
],
)
tagoverrides the semantic HTML element emitted (<nav>instead of the default<div>);Nonelets the renderer pick its natural element.attrsis adict[str, str]of arbitrary HTML attributes (id,class,data-*,aria-*).- This is how HTMX attributes (
hx-*) reach the SSR output without a dedicated field on the core.
Why a typed escape hatch
The core is renderer-agnostic; it does not model every HTML tag/attribute.
tag/attrs flow through build() into the node's props (like any other
field), so the HTML renderer consumes them and the others ignore them — with
no special-casing.
Recap¶
Styleis typed and inline; no CSS cascade.Color.from_hex,Edge.all/symmetric,Transitioncover the basics.- Each renderer translates the same
Styleto its target. - See the API reference for everything.