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-start and 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) and text.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:

milda
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:

milda
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.

Why this design holds up
Every choice here - roles over values, tokens over literals, states over pseudo-classes, aliasing over style blocks - is the same choice: push the platform-specific decision to the latest possible moment (token resolution or generation) so the authored model stays portable.

Structure and spacing intents live one layer over: Layout model.