Coming from CSS

If you think in CSS, this page is the dictionary. For each property you would reach for, it gives the word a .milda file uses, the exact spelling, why the word is different, and the corner cases. Where the language has no word, it says so and says what to write instead.

#Four things to know first

Every entry below is a consequence of these, so they are worth thirty seconds.

  • There are no CSS properties in the file. A part has - fill, ink, border, corner, inset - and each target maps them to its own primitives. The web target maps them to CSS; Figma maps them to Variable bindings. The name is chosen for what the thing is, never for how one platform draws it.
  • Values are tokens. A facet points at a typed token (space.sm, accent), not at a literal. That is why dark mode is one alias in foundations rather than a second stylesheet, and why there is no #7c3aed anywhere in a component.
  • States are named, not observed. A pseudo-class is how a browser notices an interaction. The file names the interaction state (hovered, pressed, checked) and each target observes it its own way.
  • Layout is propose/choose, not flexbox. A container proposes room, each child says what it wants (fit, fill, a weight, a fixed token, a ratio), the container places it. Flexbox can express that, and the web generator writes the flexbox for you.

#The lookup

What you would write in CSS, and what you write in Milda. Each row links to its entry.

In CSSIn MildaWhere
paddinginsetFacet
padding-left, padding-inline-start, padding-inlineinset.inline-start, inset.inlineFacet
marginnothing - gap, bleed, or alignmentNot in the language
gapgapLayout
border, border-width, border-color, border-styleborderFacet
border-radiuscornerFacet
box-sizing, display: block, overflownothing - there is one boxNot in the language
background, background-colorfillFacet
colorinkFacet
box-shadowelevationFacet
opacityopacityFacet
outline, outline-offset (the focus ring)ringFacet
font (the shorthand), font-size, line-heighttextFacet
font-weightweightFacet
font-familyfontFacet
font-style: italicslantFacet
text-overflow: ellipsis, -webkit-line-clamptext.clampRead, not yet drawn
white-space, text-wraptext.wrapRead, not yet drawn
text-alignnothing - the container places the textNot in the language
display: flex, flex-directionlayout { stack row | column | layers }Layout
justify-contentdistributeLayout
align-itemsalignLayout
flex-wrapwrapLayout
width, heightsize <width> <height>Layout
flex: 1, flex-grow, flex-shrink, flex-basisfill, or weight(n)Layout
min-width, max-width, min-height, max-heightmin(token) max(token) after an intentLayout
aspect-ratioratio(w:h) as an axis intentLayout
display: grid, grid-template-columnsgrid - reservedRead, not yet drawn
position: absolute, top/leftposition overlay, inside a layers stackLayout
position: sticky, position: fixedposition pinned <edge>Layout
negative margin (margin: 0 -16px)position bleed <edge>Layout
z-indexnothing - the order of the partsNot in the language
:hover, :active, :focus, :focus-visible, :disabled, :checkedstate hovered | pressed | focused | focus-visible | disabled | checked { }Condition
[data-variant="secondary"], .button--secondary (BEM)when variant = secondary { }Condition
.card:hover .icon (styling a descendant on an ancestor’s state)when ancestor <part> <state> { }Condition
@media (prefers-color-scheme: dark), .dark, [data-theme]context-group ColorScheme + alias by ColorScheme on the tokenFoundations
--space-sm: 8px; var(--space-sm)token space/sm : spacing = 8px, referenced as space.smFoundations
px, rem, em, %, vwpx (density-independent) and scaled()Foundations
#7c3aed, rgb(), hsl(), oklch(), color()hex(), rgb(), srgb(), p3(), oklch(), hsl()Foundations
calc(), color-mix()computeFoundations
currentColor, inheritan unstated border colour; an unstated facetFacet
!important, specificity, the cascadenothing - a part states its own facetsNot in the language
transitionmotion { transition <facets> duration <token> easing <token> }Motion
@keyframes, animation, transformnothing yetNot in the language
display: none, visibility: hiddenpresent when <condition>Condition
cursor, user-select, pointer-eventsnothing - the behaviour decidesNot in the language
Whatever is not herea slot, a per-target option, or foreign contentNot in the language

#The box

The space inside a part, its edge, and its corners. The box model has three layers in CSS; a part has one.

padding

Facet
In Milda

Padding is the name of a layer in the CSS box model. What a designer means by it is a geometry: the content sits in from the part’s edge by some distance. inset names that distance, in the word iOS, Figma and layout engines already use, and says nothing about a box model that only the web has. The value is a spacing token, so a density change is a foundations change.

milda
part Demo.card: container {
  inset space.md
}
part Demo.button: container {
  inset space.sm space.lg
}
Corner cases
  • One value is all four edges. Two values are block then inline - vertical then horizontal - the same order as the two-value CSS shorthand. There is no three- or four-value form; a single axis or edge has its own statement, see the next entry.
  • In the Studio the Style tab has one Inset control with three modes - the same value everywhere, one per axis, one per edge - which write exactly the statements this page describes.
  • Inside a layout { } block, inset is the same facet - either place is fine, not both.
  • A child that must reach past the parent’s inset (a full-bleed image in a padded card) does not get a negative margin; it says position bleed, see margin below.
The React target writes
padding when the four edges agree, padding-block / padding-inline when each axis is symmetric, the four logical longhands otherwise.
Read more
/language/styling
In Mildainset.inline-start, inset.inline

A leading icon wants less room on its own side; a nested row wants its indent on the start edge only. Those are paddings on one edge or one axis, and each is its own statement beside the bare inset. The edges are LOGICAL - start and end along the line, start and end across it - because a design system that says left means the wrong side in Arabic, and one that says inline-start means the leading side in every language.

milda
part Demo.chip: container {
  inset.block space.xs
  inset.inline space.md
}
part Demo.button: container {
  inset space.sm space.lg
  inset.inline-start space.md
}
Corner cases
  • The axes are inset.block and inset.inline; the edges are inset.block-start, inset.block-end, inset.inline-start and inset.inline-end.
  • The more specific statement wins on its edge: inset space.sm space.lg with inset.inline-start space.md is small top and bottom, large at the end, medium at the start.
  • A hover or a when block overrides exactly the edges it states, the way a CSS rule does: inset space.lg under state hovered reaches all four edges, whatever the base said per edge.
  • inset.left and the other physical words are read as the logical edge they name left to right, with a warning, and never written back.
The React target writes
padding-inline-start and the other logical longhands; padding-inline / padding-block when an axis is symmetric.
Read more
/language/styling

margin

Not in the language
In Mildanothing - gap, bleed, or alignment

A margin is a child reaching outside its own box and dictating the parent’s layout. In the propose/choose model the parent owns the space between its children, so that space is the parent’s gap; a child that must extend past the parent’s padding says position bleed; and margin: auto was always an alignment, so it is distribute or align on the parent. Each of the three means the same thing on every platform, which no margin does.

milda
part Demo.card: container {
  layout { stack column; gap space.md }
  inset space.lg
}
part Demo.card.cover: content {
  layout { position bleed inline block-start }
}
Corner cases
  • There is no margin collapsing, because there is no margin.
  • To push one child to the far end of a row, give the row distribute between, or give a spacer sibling size fill.
The React target writes
gap on the container; a bleed is a negative logical margin computed from the parent’s inset.
Read more
/language/layout

gap

Layout
In Milda

The same word, because CSS got this one right: the space between children belongs to the container. It takes a spacing token.

milda
part Demo.row: container {
  layout { stack row; gap space.sm; align center }
}
Corner cases
  • One value: there is no separate row-gap and column-gap.
  • It only does something on a part with a stack; a part without one is not a layout container.
  • gap may also be written as a facet line outside the layout { } block. Both land in the same place.
The React target writes
gap.
Read more
/language/layout
In Milda

One facet, up to three values in any order: a colour token, a width token, and solid, dashed or dotted. Which slot a word fills is decided by the type of the token it names, so the order never matters. A width token has the type border, which is a length type like spacing: a hairline is a token, not a number.

milda
part Demo.input: input {
  border line hairline solid
}
part Demo.divider: container {
  border.block-end line hairline
}
part Demo.tab: container {
  border.inline-start hairline
}
Corner cases
  • Any one of the three states a border. border accent alone is a 1-unit solid edge in that colour; border hairline alone is a hairline whose colour follows the ink.
  • An unstated colour follows the ink. On the web that is currentColor; in Figma it is a binding to the ink variable. It is the same rule everywhere, stated once.
  • One edge at a time: border.top .right .bottom .left are physical, border.inline-start .inline-end .block-start .block-end are logical and survive a right-to-left locale. Per-edge borders are a thing Figma cannot draw, and the Figma target says so rather than guessing.
  • A word that is not a declared token - transparent, or a typo - is placed by position: colour first, then width. So border transparent hairline reads as you expect, and border hairlne is a border with no width.
The React target writes
border-style, border-width and border-color (or the per-side longhands), never the border shorthand.
Read more
/language/styling
In Milda

A radius is how CSS draws a rounded corner; the design decision is the corner. The value is a radius token, and pill is the radius that fully rounds the shorter side - a word rather than 9999px, so every target can draw a pill without knowing the box’s size.

milda
token radius/sm : radius = 6px
token radius/pill : radius = pill
Corner cases
  • One value for all four corners. A single corner, or the elliptical two-radius form, has no spelling.
  • A literal such as 50% is passed through to the web as written, and means nothing to a target without a box to take a percentage of. Point at a token.
The React target writes
border-radius.
Read more
/language/styling
In Mildanothing - there is one box

A part has one size, and its inset is inside it; there is no content-box to choose. display is a target decision: a part is a container, an item, a text, and the target picks the element and how it flows. Overflow handling is a realization detail that differs by platform (a scroll view is a view on iOS and a property on the web), and the language does not state it yet.

Read more
/thinking

#Colour and depth

Every colour and shadow is a token. A facet never carries a colour literal, which is what makes dark mode one alias rather than a second stylesheet.

In Milda

fill is what every drawing API calls the paint inside a shape. “Background” implies there is a foreground drawn over it in the same box, which a text part or an icon does not have. The value is a colour token.

milda
part Demo.button: container {
  fill accent
  state hovered { fill accent-hover }
}
Corner cases
  • A facet names a token; it does not take a colour literal. fill hex(7c3aed) is not a fill, because the literal belongs in a token declaration, where it can be aliased and themed.
  • transparent is the one colour word a facet accepts directly. A document that declares a token by that name wins, as with every bare word.
  • Background images and gradients are not a fill. An image is a content part with a source; a gradient token type exists in foundations and reaches the theme, not a facet.
The React target writes
background, with the token’s custom property as the value.
Read more
/language/styling

color

Facet
In Milda

color in CSS means text colour, inherits down the tree, and is the fallback for borders and SVG through currentColor. ink says the foreground outright: text and icon colour, and the colour a border takes when it states none. The web’s inheritance is one target’s realization of that; the facet does not depend on it.

milda
part Demo.label: text {
  ink text
  when ancestor Demo hovered { ink accent }
}
Corner cases
  • A container has no ink of its own to offer in the Studio; the text and icon parts inside it do. In the text you may state it on a container and the web will inherit it, but the portable place is the part that draws.
  • An icon draws in the ink: a glyph has no colour of its own.
The React target writes
color.
Read more
/language/styling
In Milda

A shadow is how the web draws depth. Android draws depth as a tonal step, iOS draws it with a different shadow or not at all. The facet states the intent - this part is raised - and points at an elevation token, which aliases by platform to a shadow token on the web and to whatever the other platforms do. The components never know.

milda
context-group Platform { Web iOS Android }

foundations {
  token shadow/card : shadow = raw "0 4px 12px -2px rgba(15,23,42,0.12)"
  token tonal/1 : tonal = 1
  token elevation/card : elevation = alias by Platform { default -> shadow.card; Android -> tonal.1 }
}
Corner cases
  • A shadow token is written as a raw string, because a shadow’s shape is the web’s. That marks it as partial: a target that cannot read the string has nothing to fall back on, which is exactly why the facet points at an elevation rather than at the shadow.
  • Inset shadows and multiple layered shadows are what the raw string says they are; the language has no vocabulary for them.
The React target writes
box-shadow.
Read more
/language/tokens

opacity

Facet
In Milda

The same word and the same meaning: how see-through the whole part is, from 0 to 1. It points at an opacity token so that a disabled look is one decision in foundations.

milda
foundations {
  token opacity/disabled : opacity = 0.5
}

component Demo {
  part Demo: container {
    state disabled { opacity opacity.disabled }
  }
}
Corner cases
  • A bare number is the value itself, as every target reads it: opacity 0.5. The Studio’s built-in scale names its steps by percentage (50, 75), and a file with its own foundations has no such token, so opacity 50 there is the literal fifty, which the browser clamps to fully opaque. Declare the token.
The React target writes
opacity.
Read more
/language/styling
In Milda

A focus ring is a thing every platform draws and each draws differently; outline is one way to draw it. The facet is the ring’s colour, and nothing else: the width and the offset are the target’s, so a ring looks native everywhere. It is usually stated under state focused or state focus-visible.

milda
part Demo.button: control {
  state focus-visible { ring accent }
}
Corner cases
  • A ring colour that fails to resolve falls back to the ink, so a focused part is never invisible.
  • A static target (a Figma frame) has no focus, and its coverage record says so: the ring is reported as not drawn rather than approximated.
The React target writes
outline: 2px solid <colour> and outline-offset: 2px.
Read more
/language/styling

#Text

A text style is one token with several fields, and a part names the role. The axes can be overridden one at a time.

In Milda

A text role - body, title, caption - is one decision with several consequences, so it is one typography token with family, size, weight and line height inside, and the part says text body. A font-size token works in the same slot when only the size is meant.

milda
foundations {
  token font/sans : font-family = { family: "Inter"; fallback: sans-serif; source: google; license: "OFL-1.1" }
  token body : typography = { family: font.sans; size: 15px; weight: 400; line-height: 1.5 }
  token size/sm : font-size = 13px
}

component Demo {
  part Demo: container {
  }
  part Demo.label: text {
    text body
  }
  part Demo.hint: text {
    text size.sm
  }
}
Corner cases
  • Line height and letter spacing are fields of the typography token, not facets of a part. A line height is a ratio (1.5), not a length.
  • text is the facet; it is not a keyword for the part’s content. The visible string is content "…" or content bind label.
The React target writes
The token’s fields as custom properties; font-size for a size token.
Read more
/language/styling
In Milda

The weight axis of the role, overridden on its own. It takes a font-weight token or a bare number.

milda
part Demo.title: text {
  text body
  weight weight.bold
}
Corner cases
  • A bare number such as 600 is looked up as a token first and taken as the literal only when no token is named that. A Tailwind import produces a token named 600, and in such a file weight 600 means that token.
The React target writes
font-weight.
Read more
/language/styling
In Milda

The family axis of the role, overridden on its own, pointing at a font-family token. The token carries the fallback, the source and the licence, because a generated package has to ship or load the font and say under what terms.

milda
part Demo.code: text {
  font font.mono
}
The React target writes
font-family, with the family and its fallback.
Read more
/language/tokens
In Milda

The one typography axis that has nothing to tokenise, so its value is a word: normal, italic or oblique.

milda
part Demo.quote: text {
  slant italic
}
The React target writes
font-style.
Read more
/language/styling
In Milda

Truncating to N lines is a decision about the text, not about the layout, so it is a facet: text.clamp 2. The reader accepts it today; the document does not carry it yet, and no target draws it. A file that states it is read with a note saying so, and the line survives a round trip only in the text.

milda
part Demo.title: text {
  text.clamp 2
}
Read more
/language/styling

white-space, text-wrap

Read, not yet drawn
In Milda

How text breaks: wrap, nowrap, pretty, balance. The same standing as text.clamp: read by the reader, not yet carried into the document or drawn.

milda
part Demo.title: text {
  text.wrap balance
}
Read more
/language/styling

text-align

Not in the language
In Mildanothing - the container places the text

A text part is placed by the stack it sits in, with align and distribute, like any other child. Centring a label is a layout decision of the parent, and stating it there is what keeps a text part the same object on every target.

Read more
/language/layout

#Layout

Layout is propose/choose, the model SwiftUI, Compose and Flutter already use. Flexbox is one way to realize it, and the web generator does the translating.

In Mildalayout { stack row | column | layers }

stack makes a part a container and names the axis its children flow along. layers is the third axis: children drawn on top of one another, the stack sizing to the largest, which is what position: absolute is usually used for. A part with no stack is not a container, and the generator emits no flex box for it.

milda
part Demo.toolbar: container {
  layout { stack row; gap space.sm; align center }
}
part Demo.form: container {
  layout { stack column; gap space.md }
}
Corner cases
  • row-reverse and column-reverse do not exist. Order is the order of the parts.
  • display: inline-flex, inline-block and the rest of the display values are the target’s choice of element and flow, not a thing the file says.
The React target writes
display: flex and flex-direction; a layers stack is display: grid with every child in the same cell.
Read more
/language/layout
In Milda

The main axis: how children are distributed along the direction of the stack. start, center, end, between, around, evenly - the CSS words without the space- prefix and without the flex- prefix, because neither prefix says anything on a platform that has no flexbox.

milda
part Demo.footer: container {
  layout { stack row; distribute between }
}
Corner cases
  • In a layers stack the children share no axis, so distribute places each one horizontally in the cell and align vertically.
The React target writes
justify-content.
Read more
/language/layout
In Milda

The cross axis: start, center, end, stretch, baseline.

milda
part Demo.row: container {
  layout { stack row; align baseline }
}
Corner cases
  • There is no align-self: a child cannot override its parent’s alignment. A child that needs to sit differently gets its own stack.
  • align-content (how wrapped lines pack) has no word.
The React target writes
align-items.
Read more
/language/layout

flex-wrap

Layout
In Milda

Whether children start a new line when out of room: nowrap, wrap, wrap-reverse.

milda
part Demo.tags: container {
  layout { stack row; wrap wrap; gap space.xs }
}
The React target writes
flex-wrap.
Read more
/language/layout
In Mildasize <width> <height>

A child does not have a width; it has an intent per axis, and the container decides. Five intents replace lengths and the grow/shrink/basis triple: fit (size to content, the default), fill (all the room offered), weight(n) (a share against the siblings), fixed(size.sidebar) (exactly a token’s length), ratio(16:9) (follow the other axis).

milda
part Demo.sidebar: container {
  layout { size fixed(size.sidebar) fill }
}
part Demo.main: container {
  layout { size fill fill }
}
part Demo.thumb: content {
  layout { size height fit }
}
Corner cases
  • The axes are positional, width then height. size fill is the width alone; size height fit names one axis when the width must stay unstated.
  • 100% is fill; auto is fit; a fixed pixel width is fixed(token). A size token is the length type for widths and heights, distinct from spacing.
The React target writes
fit-content, 100% across the main axis, a length for fixed; see flex below for fill along the main axis.
Read more
/language/layout
In Mildafill, or weight(n)

On the axis the siblings share, fill takes the leftover room and weight(2) takes twice the share of a weight(1) sibling. The grow/shrink/basis mechanics are the web’s way of computing that, and the generator writes them.

milda
part Demo.split: container {
  layout { stack row }
}
part Demo.split.list: container {
  layout { size weight(1) fill }
}
part Demo.split.detail: container {
  layout { size weight(2) fill }
}
Corner cases
  • The web gives a fill child min-width: 0 along the main axis, because a flex item’s minimum is its content and a long label would push its siblings out instead of truncating. Native layout engines do not have this gotcha; the generator carries the fix so the file does not have to.
  • Across the main axis fill is the whole span, and weight has nothing to weigh against, so it reads as fill there.
  • In a wrapping stack a fill child keeps its content as its basis, so lines still break where they should.
The React target writes
flex-grow: n, flex-shrink: 1, flex-basis: 0% (or auto when wrapping), plus min-width: 0.
Read more
/language/layout
In Mildamin(token) max(token) after an intent

A clamp on an intent: the axis never shrinks below the one nor grows past the other. The bounds are size tokens.

milda
part Demo.column: container {
  layout { size fill min(size.min) max(size.max) fit }
}
The React target writes
min-width and max-width (or the height pair).
Read more
/language/layout
In Mildaratio(w:h) as an axis intent

The axis that follows the other one is the axis that carries the ratio, so the ratio is an intent on that axis: size fill ratio(16:9) is a width that fills and a height that keeps 16:9.

milda
part Demo.video: content {
  layout { size fill ratio(16:9) }
}
Corner cases
  • Only one axis may carry a ratio; two would contradict each other, and the second is not emitted.
  • The key form ratio 16 9 is read as “the height follows the width” and never written.
The React target writes
aspect-ratio.
Read more
/language/layout
In Mildagrid - reserved

Grid tracks (fr, minmax, auto-fit) are web-leaning concepts with no faithful native equivalent, and a layout concept earns a word only when it means the same thing on every target. grid cols 2 is read and reported, and no target draws it until a second one can.

milda
part Demo.gallery: container {
  layout { grid cols 3 }
}
Read more
/language/layout

#Position

Four positions, on logical edges. Coordinates are not among them.

In Mildaposition overlay, inside a layers stack

An absolutely positioned child is placed by coordinates in a box its parent established. The portable half of that is “on top of its siblings, placed in the parent’s box”, and the stack’s align and distribute place it: a badge sits at the top-end of an avatar because the stack says so. The coordinates are what no other platform would read the same way.

milda
part Demo.avatar: container {
  layout { stack layers; align start; distribute end }
}
part Demo.avatar.badge: container {
  layout { position overlay }
}
Corner cases
  • An overlay is placed by the stack’s align (vertical) and distribute (horizontal). There is no top: 4px; an offset is an inset on the stack.
  • Later children draw on top. There is no z-index; the order of the parts is the stacking order.
The React target writes
position: absolute in a position: relative grid cell, aligned by the grid’s item properties.
Read more
/language/layout
In Mildaposition pinned <edge>

A pinned part stays at an edge of its scroll container: a toolbar at block-start, a bottom bar at block-end. The edges are logical so the layout survives a right-to-left locale.

milda
part Demo.header: container {
  layout { position pinned block-start }
}
Corner cases
  • fixed (pinned to the viewport) is not distinguished from sticky: a part pins to the nearest scroll container, which is what a toolbar means on every platform.
  • The physical words top, bottom, left and right are read as the logical edge they name left-to-right, and never written.
  • Figma cannot express a pin and reports it, rather than drawing a frame where the pin would have been.
The React target writes
position: sticky with the logical inset of the edge set to 0.
Read more
/language/layout
In Mildaposition bleed <edge>

The negative margin trick extends a child past its parent’s padding to the parent’s edge. bleed says that outright, on the edges named, and the generator computes the number from the parent’s inset - so a change to the padding token moves the bleed with it, which the hand-written negative margin never did.

milda
part Demo.card: container {
  layout { stack column; gap space.md }
  inset space.lg
}
part Demo.card.cover: content {
  layout { position bleed inline block-start }
}
Corner cases
  • A bleed on an edge where the parent has no inset does nothing, because there is nothing to cancel.
  • block, inline and all name both edges of an axis or all four.
The React target writes
margin-inline-start: calc(-1 * var(--ds-spacing-…)) and its siblings, one per bled edge.
Read more
/language/layout

z-index

Not in the language
In Mildanothing - the order of the parts

In a layers stack the later child is on top, as in every z-stack. A number that competes across unrelated stacking contexts has no portable meaning, and the problems it solves on the web (a popover above a sticky header) are the overlay realization’s job, not the file’s.

Read more
/language/layout

#States and conditions

A pseudo-class is how the web observes an interaction. The language names the interaction state and lets each target observe it its own way.

In Mildastate hovered | pressed | focused | focus-visible | disabled | checked { }

A state comes from a behaviour, not from a platform: a toggle emits checked, pressability emits pressed, a selection emits selected. The part styles the state by name, and the web generator picks the pseudo-class or data attribute that its realization of the behaviour exposes. pressed rather than active, because active is a state of its own (the current item of a collection).

milda
part Demo.button: control {
  fill accent
  state hovered { fill accent-hover }
  state pressed { fill accent-pressed }
  state focus-visible { ring accent }
  state disabled { opacity opacity.disabled }
}
Corner cases
  • A state the part’s behaviour cannot reach is a note, not a style: a state checked on a plain container never fires.
  • A state can come from a prop: bind state disabled disabled puts the part into the state while the prop is true.
  • :first-child, :last-child, :nth-child(odd) are the collection states first, last, odd, even.
The React target writes
:hover, :active, :focus, :focus-visible, :disabled, :checked; [data-selected], [data-open], [data-loading] for the states the element has no pseudo-class for.
Read more
/language/styling
In Mildawhen variant = secondary { }

A modifier class or a data attribute is a prop made visible to a selector. The file conditions on the prop itself, and the generator decides how the prop reaches the stylesheet.

milda
part Demo.button: container {
  fill accent
  when variant = ghost {
    fill transparent
    ink accent
  }
  state hovered when variant = ghost { fill accent-subtle }
}
Corner cases
  • Predicates combine: and, or, not, parentheses; =, !=, <, contains. A leaf with no operator means “is set”.
  • A when block may also carry a layout { }, for a prop that changes the structure (a horizontal or vertical list) and not only the paint.
  • A value that varies by prop everywhere (a size prop that drives padding, font and radius together) is not a stack of when blocks: declare the prop as a context dimension and alias the tokens by it.
The React target writes
A data-variant attribute on the element and an attribute selector in the rule.
Read more
/language/styling
In Mildawhen ancestor <part> <state> { }

The selector reads outward from the child; the predicate does too, naming the ancestor part and the state. Stating it on the child keeps the ancestor’s part free of knowledge about who styles against it.

milda
part Demo.icon: icon {
  ink text-muted
  when ancestor Demo hovered { ink accent }
}
The React target writes
A descendant selector: .demo:hover .icon.
Read more
/language/styling
In Mildacontext-group ColorScheme + alias by ColorScheme on the token

Dark mode is a dimension of the system, and a dimension lives in foundations: the token answers per context, and the components never mention it. A facet cannot be aliased (fill alias by … is an error) because the alias belongs where it can be shared.

milda
context-group ColorScheme { Light Dark }

foundations {
  layer Base {
    token white : color = hex(ffffff)
    token gray/900 : color = hex(0f172a)
  }
  layer Semantic {
    token surface : color = alias by ColorScheme { default -> white; Dark -> gray.900 }
  }
}
Corner cases
  • Several dimensions (scheme, density, platform, brand) resolve by chaining single-dimension aliases through the layers, never by a composite key.
  • A viewport breakpoint is not a context the language defines. A context group is a dimension the consuming app sets; what changes at a width is a layout decision the app makes about which component it places.
The React target writes
A [data-color-scheme="dark"] block in theme.css that redefines the custom properties.
Read more
/language/tokens

#Values and variables

A CSS custom property is a name for a value. A token is a name, a type and a place in a layer, and the type is what lets every target read it.

In Mildatoken space/sm : spacing = 8px, referenced as space.sm

A token is typed: spacing, radius, size, border, font-size, color, duration and the rest. The type is what a facet checks, what the Studio offers in a picker, and what Figma turns into a Variable of the right kind. It is declared with a slash path and referenced with a dotted one.

milda
foundations {
  layer Base {
    token space/sm : spacing = 8px
    token space/lg : spacing = 16px
  }
}

component Demo {
  part Demo: container {
    inset space.sm space.lg
  }
}
Corner cases
  • A length type rejects a bare number: 8px, never 8. The unit is what makes it a length.
  • Tokens live in layers that resolve downward: Base holds the literals, Semantic aliases them by role, and a component layer may alias those again.
The React target writes
One custom property per token in theme.css, --ds-<type>-<id>, and var(…) wherever a facet points at it.
Read more
/language/tokens
In Mildapx (density-independent) and scaled()

Two kinds of length, both portable. px is not a CSS pixel: it is the density-independent unit Android calls dp, and every target converts it. scaled(1.25) is a length relative to the reader’s text size, which is what rem is for on the web and sp on Android.

milda
token space/md : spacing = 12px
token size/text-md : font-size = scaled(1)
Corner cases
  • The reader accepts rem and sp as spellings of scaled() and dp as a spelling of px; the printer writes the canonical forms.
  • em, %, vw and vh have no neutral meaning and are not length units of the language. A percentage of a parent is fill or weight; a share of the viewport is the app’s decision.
Read more
/language/values
In Mildahex(), rgb(), srgb(), p3(), oklch(), hsl()

A colour literal names its space, so a wide-gamut colour survives a round trip and a target that cannot show it knows what it is approximating. hex(7c3aed) has no #, because the literal is a function like the others.

milda
token violet/600 : color = hex(7c3aed)
token violet/glow : color = oklch(0.62 0.19 300)
token scrim : color = srgb(0 0 0 0.4)
Corner cases
  • A trailing channel is alpha: hex(ffffffcc), srgb(1 1 1 0.8).
  • A colour literal goes in a token, never in a facet.
Read more
/language/values
In Milda

A derived value is a token computed from another by steps, and it follows its base when the base changes. Colour steps happen in a named space; length steps scale, offset and round.

milda
token accent : color = hex(7c3aed)
token accent-hover : color = compute accent in oklch { darken 10% }
token space/md : spacing = 12px
token space/lg : spacing = compute space.md { scale 1.5 }
Corner cases
  • The maths happens at generation time, so the generated CSS holds the result, not a calc(). That is what lets a target without calc() read it.
Read more
/language/values
In Mildaan unstated border colour; an unstated facet

There is no currentColor keyword because there is a rule instead: a border with no colour of its own follows the ink, everywhere. inherit is the absence of a facet - a part that states no ink takes whatever its target’s inheritance gives it, which on the web is the parent’s colour.

The React target writes
currentColor for the border; the property left unset for the absent facet.
Read more
/language/styling
In Mildanothing - a part states its own facets

There is no cascade to win. Each part states its facets; a state or when block changes them under its condition; nothing from outside the component reaches in. Consumers restyle through tokens and through the seams the component declares, never by out-specifying its rules.

Read more
/thinking

#Motion

A transition names what animates and points at timing tokens. Keyframe animation has no word.

In Mildamotion { transition <facets> duration <token> easing <token> }

The facets that animate between states, timed by duration and easing tokens. Motion adds no state; the behaviour states are its anchors.

milda
foundations {
  token fast : duration = 120ms
  token standard : easing = raw "cubic-bezier(0.2, 0, 0, 1)"
}

component Demo {
  part Demo: container {
    motion {
      transition fill ink duration fast easing standard
    }
  }
}
Corner cases
  • A transition without a duration is skipped.
  • An easing is a raw string, because a curve’s spelling is the web’s; a target that cannot read it falls back to its own.
The React target writes
transition on the facets’ properties.
Read more
/language/styling
In Mildanothing yet

Keyframe animation and transforms are realization work each platform does very differently, and the language has no words for them yet. A spinner or a slide-in is the archetype’s realization (a progress archetype spins; a dialog’s presentation animates) rather than a thing the file draws.

Read more
/language/styling

#Visibility and the rest

Properties that are not paint. Most are either a structural statement or a target decision.

In Mildapresent when <condition>

Hiding a part is a structural fact - it is not there - and the file says that with a presence condition on the part. A hidden part is not rendered at all, on every target, which visibility: hidden never meant.

milda
part Demo.icon: icon {
  present when icon
}
Read more
/language/parts
In Mildanothing - the behaviour decides

A pointer cursor is what the web shows over a pressable thing; the archetype knows the part is pressable and the web realization sets the cursor. The same for selectability and hit testing: they follow from the behaviour the part composes, not from a property.

Read more
/language/behaviors

Whatever is not here

Not in the language
In Mildaa slot, a per-target option, or foreign content

No vocabulary covers everything. When the model cannot say what you need, the answer is a seam declared in the document: a slot where consumers put their own content, a per-target option at release time, or, as a last resort, foreign content that carries one target’s code in quarantine, tagged so every other reader skips it honestly. Editing the generated CSS is never the answer: the next release overwrites it.

Read more
/thinking

#In the Studio

The Studio's Style tab offers the same facets under the same names - Fill, Ink, Corner, Inset, Border width, Border color, Elevation, Opacity, Focus ring - so this page reads for both front-ends. What the Studio adds is that it offers each part only the facets its kind can take, and every value is picked from the tokens the system declares rather than typed. Its Inset control has three modes, one value, one per axis and one per edge, and each writes the statements the padding entries above describe.

When you cannot find your property
Ask what the property was for. A negative margin was for a full-bleed image, and that is position bleed. A z-index was for a badge over an avatar, and that is a layers stack. A cursor: pointer was for telling people a thing is pressable, and the archetype already knows. The language names the intent; the property was one platform's way of getting there.

The words themselves are defined once in the glossary, and every statement a file can contain is in the language reference.