Modern CSS in 0.59¶
Hedron 0.59 upgrades the styling path without adding a second theme registry, CSS runtime,
client-side style injector, or mandatory Node toolchain. Python expresses finite semantic intent;
component styles.css remains the escape hatch for standards-based CSS.
The release has one styling authority:
Theme / DesignSystem → semantic markers and tokens → default CSS + scoped CSS → deterministic assets
Existing 0.58 constructors, classes, data-hedron-* markers, theme names, token aliases, and
default_styles=False remain valid. New 0.59 behavior is opt-in where it changes responsive
context or presentation scope.
Capability tiers¶
Do not read “modern CSS” as “every browser must implement every feature.” Hedron assigns each capability a tier:
| Tier | Meaning | Documentation rule |
|---|---|---|
| Required | Supported path with a tested native or static fallback | Safe for ordinary product UI |
| Progressive | Feature-detected enhancement with an independently usable fallback | Treat the fallback as the contract |
| Experimental | Opt-in, decorative, and excluded from unqualified support claims | Label it in product documentation |
| Deferred | Outside the 0.59 styling contract | Do not build adoption guidance around it |
What changed in 0.59¶
Scoped CSS compiler and cascade¶
Component stylesheets now target compiler format 2. The compiler preserves CSS grammar boundaries for selectors, declarations, descriptors, strings, comments, URLs, nesting, conditional rules, and safe unknown at-rules. It reads format-1 manifests and preserves v1 symbol hashes by default.
/* components/Callout/styles.css */
.root {
display: grid;
gap: var(--hedron-space-unit);
& > .title {
text-wrap: balance;
}
}
@supports (container-type: inline-size) {
.root {
container-type: inline-size;
}
}
Use local imports and registered local assets only. Quoted imports, url(), @font-face,
image-set(), traversal, remote URLs, ambiguous rewrites, unsafe globals, and malformed CSS are
resolved or rejected during the build. Production does not compile CSS at runtime.
The generated stylesheet has one deterministic layer order:
Use semantic props and tokens before increasing specificity. @scope, :scope, :is(),
:where(), :not(), and :has() are available according to their tier and fallback rules;
private selector theming is not part of the public contract.
Container-aware and intrinsic layout¶
Container can opt a boundary into inline-size queries. Its existing viewport behavior remains
the default.
from hedron import Container, Grid, Text
panel = Container(
Grid(Text("Details"), Text("Activity"), columns={"base": 1, "md": 2}),
query="inline-size",
name="workspace-panel",
)
The component emits validated data-hedron-container-query and
data-hedron-container-name markers. The 0.59 layout contract covers:
@container,cqi,cqb,cqmin, andcqmaxwith viewport/static fallbacks;- intrinsic sizing through
min(),max(),clamp(),minmax(),fit-content(), andaspect-ratio; - dynamic viewport units (
svh,lvh,dvh) and safe-area insets; - logical properties and values across LTR, RTL, mixed direction, and selected vertical writing;
subgridas a Progressive alignment enhancement over ordinaryGrid/FormGridtracks; and- complete-content behavior at narrow widths, zoom, and increased text spacing.
Style queries and nested container behavior remain feature-detected. They must not become hidden application state or change DOM order.
Tokens, themes, variants, color, and typography¶
Theme and DesignSystem remain the source of semantic values. 0.59 adds explicit finite theme
variants, additive output, modern color fallbacks, and stronger typography guidance.
from hedron import StyleScope, Text
StyleScope(
Text("Compact review mode", role="title"),
theme="aurora",
variant="dense",
color_mode="dark",
density="compact",
)
Variants emit data-hedron-variant and are validated as finite safe names. An unknown variant
fails closed; no arbitrary CSS selector or user value becomes a theme variant.
The color contract includes parsed absolute CSS Color 4 input, canonical sRGB fallbacks, optional
wide-gamut declarations, deterministic gamut mapping, contrast/focus checks, and provenance in the
brand plan. color-mix() is safe for generated presentation. Remote fonts and automatic remote
asset fetching remain outside the Supported path.
Typography roles cover fluid sizing, balanced and pretty wrapping, hyphenation, code and numeric content, long/international text, variable fonts, and explicit local font assets. Prefer roles and local fallback stacks over arbitrary font-size overrides.
light-dark() and color-scheme are Progressive enhancements. Explicit light/dark markers and
the existing system-preference rules remain the fallback. Selected bounded custom properties may
use @property; ordinary custom properties remain the fallback.
Overlays and motion¶
Native dialog/popover/top-layer semantics remain the authority. Popover exposes finite logical
placement and collision behavior:
from hedron import Popover, Text
Popover(
Text("Filters"),
label="Open filters",
placement="block-end",
collision="flip",
)
Supported static/details/native paths remain usable when popover support is absent. Anchor
positioning is a Progressive enhancement; the fallback is logical document flow or bounded
existing placement. Entry/exit transitions use @starting-style and discrete transitions only
when supported, otherwise the state changes immediately and remains stable.
View Transitions are an opt-in Progressive navigation/surface enhancement. They must preserve focus, title/history, HTMX swap semantics, and server state. Scroll-driven animations are Experimental and decorative only: never use them for authorization, validation, task progress, or other semantic state.
Media, preferences, and print¶
The default stylesheet includes first-party rules for print and preference conditions:
prefers-reduced-motionremoves nonessential motion;forced-colorspreserves usable contrast and focus;prefers-contrastandprefers-reduced-transparencyadjust presentation where supported;hoverandpointeravoid assuming a precise pointing device;- logical direction and writing-mode paths preserve content and focus; and
@media print, page breaks, links, forms, data, statuses, disclosures, and shell landmarks remain readable in source order.
The non-enhanced path must remain complete, keyboard-usable, and non-color-dependent. Styling cannot hide authoritative content or change semantics.
Typed controls and product surfaces¶
Button and LinkButton share the 0.59 size and width vocabulary and accept a bounded
attrs= seam for global, aria-*, data-*, approved HTMX, and popover/dialog-trigger
attributes:
from hedron import Button, LinkButton
Button(
"Save",
size="sm",
width="full",
attrs={"hx-post": "/save", "aria-describedby": "save-help"},
)
LinkButton("Review", "/review", size="sm", width="full", attrs={"data-track": "review"})
The seam rejects component-owned structural attributes, on*, inline style, hx-on*,
malformed ARIA/data names, and non-allowlisted HTMX attributes. The component still owns type,
disabled, href, class, and id.
The same tokens power composable AppShell chrome and provider-neutral workflow presentation: brand/account/footer/auth states, responsive pipeline connectors, explicit run states, logs, and compact history. These surfaces do not own authentication, transfer execution, polling, logs, or authorization; they render typed state supplied by the application.
0.59 feature matrix¶
| Area | Capability | Tier | Fallback or boundary |
|---|---|---|---|
| Compiler | Grammar-aware selectors, declarations, descriptors, strings, comments, URLs, custom identifiers | Required | Invalid or unsafe syntax is rejected diagnostically |
| Compiler | CSS nesting and nested conditional rules | Required | Browser-valid scoped CSS |
| Compiler | @media, @supports, @container, @scope, @starting-style, safe unknown at-rules |
Required | Preserve safe syntax; reject ambiguous/unsafe rewrites |
| Compiler | Local imports, font sources, image-set(), nested URLs |
Required | Resolve bounded local assets or reject; no remote imports |
| Cascade | @layer, :where(), :is(), specificity normalization |
Required | One deterministic layer output |
| Cascade | Native @scope / :scope |
Progressive | Public-marker selector boundary |
| Layout | Size/style container queries | Required / Progressive | Base layout and viewport maps |
| Layout | subgrid |
Progressive | Ordinary Grid/FormGrid tracks |
| Layout | Logical layout, writing modes, intrinsic and viewport sizing | Required | Documented physical exceptions and ordinary units |
| Theme | Modern color and finite theme variants | Required | Canonical sRGB and base theme tokens |
| Theme | light-dark(), color-scheme, selected @property tokens |
Progressive | Explicit mode selectors and ordinary custom properties |
| Content | Modern typography, wrapping, hyphenation, variable/local fonts | Required | System fallback stacks and ordinary wrapping |
| Overlay | Popover/top layer/dialog and logical placement | Required / Progressive | Details/static/native flow and bounded placement |
| Motion | Starting/discrete transitions and View Transitions | Progressive | Immediate stable state or ordinary navigation/swap |
| Motion | Scroll-driven animation | Experimental | Static complete presentation |
| Media | Preference media and print | Required | Non-motion, non-color, semantic source-order presentation |
| Performance | content-visibility and containment |
Progressive | Fully rendered content |
| Product surfaces | Typed controls, shell chrome, pipeline presentation | Required | Existing native/built-in compositions |
The machine-readable source of truth is
modern-css-inventory-059.toml. It records the
exact gate, fallback, and package disposition for every capability.
Tooling and review¶
Use static, redacted tooling during design review and CI:
hedron theme check
hedron --app app:app style explain --format human
hedron --app app:app style preview --output .artifacts/styling --mode all
hedron --app app:app style diff --base default --candidate aurora
hedron style check --zero-app-css PATH
explain, preview, and diff do not execute application callbacks or expose application data.
eject is reviewable and no-overwrite by default. Inspect source locations, winning layers,
variants, aliases, fallbacks, assets, and budgets before accepting a visual change.
For upgrades, run the 0.58-to-0.59 fixtures before changing a pin. Existing application CSS is not silently rewritten. See the upgrade guide, Presentation APIs, and Themes.
Explicit non-goals¶
0.59 does not add free-form CSS-in-Python, a utility-string framework, a second compiler/cascade/ theme registry, runtime style injection, mandatory Node, remote font auto-fetch, CSS masonry, paint/layout worklets, private-selector theming, visual DOM reordering, or styling-owned behavior.