Thinking in Milda

Every design decision in the language follows from a handful of rules. Learn these ten and the rest of the docs reads as consequences rather than as facts to memorise. Each rule comes with the few lines of .milda that show it.

#1. The system is the source. Code is one of its outputs.

One document describes the system, and everything else is produced from it: the React package, the Figma file, the docs site, the files an agent reads. You never edit an output; you change the document and regenerate. This is what keeps four surfaces from drifting into four systems, and it is why the generated code can be yours without strings: it carries nothing the document does not already say.

milda
milda 0.10
prelude 0.0.2

meta { name "Acme" }

context-group ColorScheme { Light Dark }

foundations {
  layer Base {
    token violet/600 : color = hex(7c3aed)
    token violet/500 : color = hex(8b5cf6)
    token white : color = hex(ffffff)
    token gray/900 : color = hex(0f172a)
  }
  layer Semantic {
    token accent : color = alias by ColorScheme { default -> violet.600; Dark -> violet.500 }
    token on-accent : color = alias by ColorScheme { default -> white }
    token surface : color = alias by ColorScheme { default -> white; Dark -> gray.900 }
    token space/sm : spacing = 8px
    token space/lg : spacing = 16px
    token radius/sm : radius = 6px
  }
}

component Button "The primary action." {
  archetype Button

  props {
    label : string = "Get started"
    disabled : boolean = false
  }

  events { click }

  part Button: container {
    emits { click on activate }
    bind state disabled disabled
    layout { stack row; gap space.sm; align center }
    fill accent
    ink on-accent
    corner radius.sm
    inset space.sm space.lg
    state hovered { fill violet.500 }
    state disabled { opacity 50 }
  }

  part label: text {
    slot of content default
    content bind label
  }
}

#2. Say what a thing is, never what it is made of.

A part has a kind: container, text, input, icon, control. It never has a tag. div, UIView and a Figma frame are three answers to the same question, and the question is the target's to answer. The same holds for everything platform-shaped: there is no CSS property, no event name of any framework, no pseudo-class anywhere in the file.

milda
part Field: container {
  layout { stack column; gap space.sm }
}
part Field.label: text {
  content "Email"
}
part Field.control: input {
  value string
}
part Field.icon: icon {
  present when icon
}

The payoff is that a target can be added without the document changing, and the cost is a vocabulary that is smaller than any one platform's. Where the small vocabulary cannot say something, rule 9 applies.

#3. A value has a name, and the name has a layer.

Components never paint with a literal. A facet points at a token, the token lives in a layer, and a semantic token aliases a base one. A restyle is then a change in foundations rather than a sweep through components, and a dark scheme is one more alias rather than a second stylesheet.

milda
context-group ColorScheme { Light Dark }

foundations {
  layer Base {
    token violet/600 : color = hex(7c3aed)
    token violet/500 : color = hex(8b5cf6)
  }
  layer Semantic {
    token accent : color = alias by ColorScheme { default -> violet.600; Dark -> violet.500 }
  }
}

component Button {
  archetype Button
  part Button: container {
    fill accent
  }
}

#4. Context is a dimension, not a copy.

Light and dark, compact and comfortable, web and iOS, two brands: each is a context group, and a token answers per context with an alias. Several active dimensions resolve by chaining single-dimension aliases through the layers, never by a composite key like web+dark, which would explode.

A prop can be typed by a context group. That one rule is what variant, size and a per-instance accent colour are: the dimension's value comes from the instance instead of from the environment, and the same token aliasing does the rest. There is no conditional style logic to write.

milda
context-group Variant { Primary Secondary }

foundations {
  token accent : color = hex(7c3aed)
  token surface : color = hex(ffffff)
  token button/fill : color = alias by Variant { Primary -> accent; Secondary -> surface; default -> accent }
}

component Button {
  archetype Button
  props {
    variant : context Variant = Primary
  }
  part Button: container {
    fill button.fill
  }
}

#5. A state comes from a behaviour, not from a platform.

, , selected, open: each is emitted by a behaviour, a formal state machine the archetype composes. Paint keys on those states. It never keys on :hover, because a wheel on iOS has no hover and a screen reader has no pointer, and the same state active block must mean the same highlight on all three.

milda
part option: item {
  fill surface
  state active { fill surface.raised }
  state selected { ink accent }
  state disabled { opacity 50 }
}

#6. The contract is the identity.

A component's props and events are what consumers code against, and they are what makes it the component it is. A select that is a popover listbox on the web and a spinning wheel on iOS is one component, because “one of a known set” in and a change event out is one contract. The contract is also what versioning reads: a release diffs it against the previous release and bumps the version by what changed, so a breaking change is caught by the compiler, not by a consumer.

milda
archetype SingleSelect
props {
  options : array<string> required
  value : string bind selected
  placeholder : string = "Choose"
}
events {
  change from S.change
}

#7. Build from what exists: archetypes and instances.

A component starts from an archetype, a blueprint from the prelude with a correct contract, a published anatomy and the behaviours already composed. Pick BooleanInput and the toggle, the keyboard handling and the accessible naming arrive with it; your work is the contract, the tokens and the states. Larger things are built from smaller ones the same way: a part can be an instance of another component, with props fixed or passed through.

milda
component Checkbox {
  archetype BooleanInput
  props {
    checked : boolean = false
    label : string required
  }
  part Checkbox: container {
    layout { stack row; gap space.sm; align center }
  }
  part label: text {
    content bind label
  }
}
milda
component Badge {
  archetype Badge
  props { label : string = "New"; tone : enum(neutral, success) = neutral }
  part Badge: container {
    fill accent-subtle
    ink accent
  }
  part label: text {
    content bind label
  }
}

component Card {
  props { tone : enum(neutral, success) = neutral }
  part Card: container {
    layout { stack column; gap space.md }
  }
  part Card.Status: instance {
    instance Badge {
      label = "Beta"
      tone = bind tone
    }
  }
  part Card.Body: text {
    slot of content default
  }
}

#8. The file keeps nothing it cannot explain.

Every reference in a .milda file is by name: a part by its path, a token by its path, a component by its identifier. Nothing points at a database id, so a file resolves on its own, and a reader that is not ours can read it. Reading a file and writing it back gives the same document, and that round trip is a tested fixed point. A comment is the one thing that does not survive; anything that should has a statement of its own.

milda
part label: text {
  content "Email"
}
part hint: text {
  content "We never share it."
}
part control: input {
  a11y {
    labelled-by label
    described-by hint
  }
}

#9. Extend through seams, never by patching output.

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 with the target so every other reader skips it honestly. Editing generated code is never the answer: the next release overwrites it, and nothing else learns what you changed.

milda
part chart: foreign {
  foreign web-react "Sparkline" "<Sparkline data={props.points} />"
}

#10. Say how it is meant to be used.

A system that only lists what it offers leaves the hardest part, composition, to be rediscovered by every consumer. Milda carries that knowledge too, in two layers. An interface constraint is decided from one instance and lives in the props block, where the generated types can enforce it. A usage rule is decided over how instances are put together, and ships to agents and people with its reason.

milda
context-group Variant { Primary Secondary }

component Button {
  archetype Button
  props {
    variant : context Variant = Secondary
    loading : boolean = false
    loadingLabel : string required when loading "Read out while the spinner replaces the label."
  }
}

rule one-primary-action error {
  max 1 Button where variant = Primary within archetype Form
  because "A form has one way forward."
}
Built

Interface constraints are typed by the React target; usage rules ship in AGENTS.md, DESIGN.md and design-system.json and are edited in the Studio.proposal 0052

One rule behind the ten
Push every platform-specific decision to the latest possible moment: token resolution or generation. Roles over values, tokens over literals, states over pseudo-classes, aliasing over style blocks, seams over patches: each is the same choice made once more.

Now see the rules at work in order: the tour builds one system a lesson at a time.