Facets & tokens
Style in the Milda Language is a small, abstract paint vocabulary - facets - bound to tokens, keyed by behavior-emitted states. There are no CSS properties in the model, and no per-platform style code anywhere: platform divergence lives entirely in how tokens resolve.
#What a facet is
A facet is an abstract paint slot on an anatomy part - a what to paint, never a how. “This part's fillis the surface token” is a statement every platform can honor; background-color is not. The vocabulary:
fill- surface/background.ink- text and icon foreground.border- edge: color + width (hairline/thin/thick) + style. Bare means all edges;border.inline-startand friends name one logical edge (physical edges are realization-only). iOS often realizes this as an overlay stroke.corner- corner radius, as a token role.inset- internal padding (block/inline).gap- spacing between children.text- a typography role (body, caption, title…) bundling size, weight, line, and tracking. On iOS, Dynamic Type resolves the role at the OS level. Sub-keys:text.clamp(truncate to N lines) andtext.wrap.font,weight,slant- orthogonal overrides of the role's family, weight, and style axis.elevation- depth. The classic divergent facet: web box-shadow · Android tonal elevation · iOS none/sheet.overlay- a state layer (the Material hover/press tint).opacity- transparency.ring- the focus indicator: desktop ring · mobile none · TV strong highlight.backdrop- scrim/blur behind a layer; iOS material/vibrancy.
#Facets bind to tokens
A facet's value is a token reference - fill points at surface/input, corner at radius/md. Tokens are typed (a color is a colorspace + channels, a dimension is a density-independent number, never a platform string) and organized in layers - base → semantic → component - as described in Foundations. Binding facets to tokens rather than raw values is what makes a restyle a foundations change, not a component sweep.
#Context dimensions
A token's active value can depend on context: platform, color scheme, density, brand. Each dimension is a context group, and a token can alias by one - alias by ColorScheme { Light → white; Dark → slate.800 }. Multiple active dimensions resolve by chaining single-dimension aliases through the token layers, most-specific wins, with a default fallback at each link - never composite keys like web+dark, which would explode combinatorially.
#Platform divergence is token aliasing, not style code
Elevation on the web is a box-shadow; on Android it's tonal elevation; on iOS it's usually nothing. The language does not handle this with per-platform style blocks. Instead, divergent facets (elevation, ring, backdrop) are abstract facet-token types, and a facet-token is an alias keyed by the Platform context group whose references may be cross-type:
token elevation/overlay : elevation = alias by Platform { web → shadow/lg ; android → tonal/3 ; ios → none ; default → shadow/md } surface { elevation elevation/overlay } # the component just references the token
The component says elevationonce; each platform's generator only has to know how to render the primitives (shadow → box-shadow, tonal → Material elevation) - mechanical work. The consequence is structural: there is no per-platform style code in the language at all. All style and value divergence collapses into token aliasing; only behavior/structure divergence (a wheel instead of a listbox) uses realization blocks.
#Prop-driven styling is the same mechanism
A context group's active value can also come from a prop- a prop typed by the group shadows the ambient value for that component's subtree. That single rule is what variant, size, per-instance accent color, and responsive props all are: token aliasing, with the dimension's value supplied per-instance instead of by the environment. Resolution precedence: prop-bound → ambient/theme → the group's default.
#State-driven styling
Facet values can be overridden per state- and states come from behaviors, never CSS pseudo-classes. A part's style block nests state overrides:
part option { fill surface.raised state active { fill surface.overlay } # browse's highlight state selected { ink accent } # selection's commit }
Because active is emitted by the browse atom and selected by selection, the same styling works whether the target realizes highlighting with mouse hover, keyboard roving, or a wheel's center band. The full state vocabulary is listed in the Behaviors reference.
Structure and spacing intents live one layer over: Layout model.