Usage rules & constraints

A design system says what it offers: tokens, components, their props and slots. Milda also lets it say how those pieces are meant to be put together - one primary action per form, no badge inside a button, colours only from tokens - in a form a checker can evaluate, an agent can follow and a person can read.

One principle splits this into two layers: a rule never restates the contract, it says only what the contract cannot. What can be decided from a single instance of a component belongs to that component's interface. What needs to know where the instance sits, or how many there are, is a usage rule.

#Interface constraints

Written in a component's block. They are part of the contract: the generated types enforce them where a type can, and adding one is a breaking change in the contract diff.

milda
component IconButton {
  archetype Button
  props {
    loading      : boolean = false
    loadingLabel : string  required when loading   "Read out while the spinner replaces the label."
    label        : string
    labelledBy   : string
    href         : string
    target       : string
    one-of    label, labelledBy                    # exactly one
    exclusive href, target                         # at most one
  }
}
  • x : T required - always supplied.
  • x : T required when P - supplied whenever the condition P holds. The condition reads the component's other props only: a UI state or a theme has no value where the component is used.
  • one-of a, b - exactly one is supplied; any-of a, b - at least one; exclusive a, b - at most one.

“Supplied” means written at the use site. A declared default does not count: a constraint over a prop says the default is only a placeholder.

#What the React target emits

A component with a constraint types its props as a base interface intersected with one union per constraint, so the compiler rejects the misuse:

tsx
<IconButton label="Save" />                   // ok
<IconButton />                                // error: label or labelledBy
<IconButton label="Save" labelledBy="t" />    // error: not both
<IconButton label="Save" loading />           // error: loadingLabel is required
<IconButton label="Save" loading={busy} />    // error: busy may be true

A condition no type can say - an ordering like count > 0, or two props at once - leaves the prop optional, is listed in the target's report of what it could not translate, and is enforced by milda check instead.

Built

milda check evaluates every rule kind and the interface constraints over a consumer’s TSX and JSX, following its own components across files. Stylesheets and CSS-in-JS are not read yet.proposal 0053

#Usage rules

A top-level declaration, decided over the usage tree: the instances of the library's components as a consumer composes them. Every rule has an id (what a finding reports), a severity, and a mandatory reason, which the agent reads verbatim.

milda
rule one-primary-action error {
  max 1 Button where variant = Primary within archetype Form
  because "A form has one way forward. A second primary action makes the reader choose."
}

#The subject

  • A component, by the name it is declared with: Button.
  • An archetype: archetype Form - anything that realizes it, whether a library component or what the checker knows is native on a platform (a <form> on the web). This is how a rule stays platform-neutral without being useless.
  • Either, narrowed by its props: Button where variant = Primary. An unwritten prop takes its default.

#The statements

Every statement opens with one keyword of its own.

milda
max 1 Button where variant = Primary within archetype Form   # at most
min 1 Tab within Tabs                                         # at least - a required child
min 2 max 7 Tab within Tabs                                   # both bounds
never Badge within Button                                     # forbidden nesting, any depth
only Tab within Tabs, archetype ContextMenu                   # a required parent
tokens color, spacing                                         # values only from tokens
prefer Link for archetype Link                                # the component an intent goes through
  • A count is decided per scope: each subject instance counts for its nearest enclosing scope, so a nested form does not lend its button to the outer one.
  • tokens names token types the way the foundations block spells them, never style properties.
  • prefer is broken by anything that realizes the archetype and is not that component: a native equivalent, another library component, or one written in the consuming app.
What a partial view may say
A checker never sees the whole tree. A condition it cannot decide (variant={kind}) does not match, and what it cannot prove is not reported: a Tab returned from the app's own wrapper, used elsewhere, is not an only violation.

#Where they go

  • AGENTS.md states the interface constraints and the usage rules, errors first, with each rule's reason, by the identifiers the package exports.
  • DESIGN.md lists the constraints with each component's props, the rules that name a component under it, and the whole list at the end.
  • design-system.json carries them as data: a top-level rules array (always present), and requiredWhen / constraints on the components.
  • milda check evaluates both layers over the code that uses the system: each rule reports under its own id (rule:tab-placement) with its severity and its reason, and a broken interface constraint is an error. It follows the app's own components across files, so the wrapper above is judged where it is rendered.
  • The release lists added, removed and re-graded rules in its changes. They never move the version: a rule cannot break a build.

In the Studio, constraints are edited in the Interface panel - the prop editor's Required is Optional, Always or When…, and Between props holds the group constraints - and usage rules in Foundations → Manage → Usage rules. See also The .milda file for the surface version that introduced them.

These words, in the reference

  • A usage rule: how the system is meant to be composed, checked over the instances a consumer writes.
  • The prop must be given.
  • A constraint over a group of props.

Open a word for its entry, or read them all in the reference.

See it built in the tour11. Usage rules5. The contract