Lesson 12 of 14

Docs, examples, assets and icons

The system carries its own documentation, demo scenes, assets and icon library.

#The idea

A system is more than its components. It has demo scenes, an asset library, an icon library and documentation pages, and all four travel in the file, by name, so a system exported and imported elsewhere is the same system, docs and all.

#What this lesson adds

milda
example "Card with actions" for Card {
  part stage: instance {
    instance Card {
      tone = "success"
    }
  }
}

assets {
  folder brand
  svg "logo" in brand {
    mime "image/svg+xml"
    view-box "0 0 24 24"
    url "assets/logo.svg"
  }
}

icons {
  set lucide "Lucide" { provider lucide; license ISC }
  glyph lucide check { label "Done"; svg "<path d='M20 6 9 17l-5-5'/>" }
  custom "logo-mark" { asset logo; label "Acme" }
}

docs {
  site { name "Acme"; tagline "One system, every surface" }
  footer { copyright "Acme, 2026" }
  page "Getting started" in "Guides" {
    heading "Getting started" anchor top
    paragraph "Install the package, import the theme, and build from the components below."
    component-group Actions
  }
  page "Button" in "Components" {
    heading "Button" anchor top
    paragraph "Use one primary action per view."
    component Button
    component-props Button
    component-events Button
  }
  page "Card" in "Components" {
    heading "Card" anchor top
    component Card
    example "Card with actions"
  }
}

#What to notice

  • An example is a scene with no contract. example "Card with actions" for Card places components the way a page would, and names the component it demonstrates. A scene can carry its own variables and s, so a demo can open its own dialog.
  • An asset says where its bytes are. means beside the file, in a bundle that stays diffable; means inside the file, self-contained. Both are the same grammar.
  • The icon library resolves bare names, in order. A part's content "check" looks for a curated first, then in the first . A curated glyph carries its SVG body, so the generated code vendors it and nothing loads from a CDN at runtime. A glyph is drawn by an asset.
  • Docs are transclusions. component Button in a page is a live preview, component-props Button its props table, component-group Actions every component of the group. A docs page names what it shows, so it stays current as the system changes. Prose blocks are , , and the rest.

#What the generator makes of it

The Icon module now holds the check glyph as a data URI mask, and the docs site for the system, published at your organisation's milda.page address, renders the three pages with live components.

#Try it

Add a demo { open : boolean = false } block and a trigger with action activate toggle open to the example, placing the Dialog with open = bind open.

Words in this lesson

  • A demo scene: a composition shown in the docs and the preview, with no contract of its own.
  • Inside an example: the scene’s own variables, which its parts can bind and its actions can change.
  • A demo interaction, meaningful only inside an example: when the part is activated, flip or set one of the scene’s demo variables.
  • The asset library: fonts, images, SVGs, videos and animations, in folders.
  • The icon library.
  • The design system’s documentation site: its header, its footer, and its pages.

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

#The file at the end of this lesson

milda
milda 0.10
prelude 0.0.2

meta { name "Acme" }

context-group ColorScheme { Light Dark }

context-group Variant { Primary Secondary Ghost }

group Actions "Things a person presses to make something happen."
group Forms "Collecting input."
group Feedback "Telling a person what is happening."
group Layout "Surfaces that hold the rest."

foundations {
  layer Base {
    token violet/100 : color = hex(ede9fe)
    token violet/500 : color = hex(8b5cf6)
    token violet/600 : color = hex(7c3aed)
    token violet/700 : color = hex(6d28d9)
    token violet/900 : color = hex(4c1d95)
    token gray/50 : color = hex(f8fafc)
    token gray/200 : color = hex(e2e8f0)
    token gray/500 : color = hex(64748b)
    token gray/800 : color = hex(1e293b)
    token gray/900 : color = hex(0f172a)
    token green/500 : color = hex(22c55e)
    token amber/500 : color = hex(f59e0b)
    token white : color = hex(ffffff)
  }

  layer Semantic {
    token accent : color = alias by ColorScheme { default -> violet.600; Dark -> violet.500 }
    token accent-hover : color = compute accent in oklch { darken 10% }
    token accent-subtle : color = alias by ColorScheme { default -> violet.100; Dark -> violet.900 }
    token on-accent : color = alias by ColorScheme { default -> white }
    token surface : color = alias by ColorScheme { default -> white; Dark -> gray.900 }
    token text : color = alias by ColorScheme { default -> gray.900; Dark -> gray.50 }
    token text-muted : color = alias by ColorScheme { default -> gray.500 }
    token line : color = alias by ColorScheme { default -> gray.200; Dark -> gray.800 }
    token success : color = alias by ColorScheme { default -> green.500 }
    token warning : color = alias by ColorScheme { default -> amber.500 }

    token space/xs : spacing = 4px
    token space/sm : spacing = 8px
    token space/md : spacing = 12px
    token space/lg : spacing = 16px
    token space/xl : spacing = 24px

    token radius/sm : radius = 6px
    token radius/lg : radius = 12px
    token radius/pill : radius = pill

    token hairline : border = 1px

    token size/sm : font-size = 13px
    token size/md : font-size = 15px
    token size/lg : font-size = 18px
    token weight/medium : font-weight = 500
    token weight/bold : font-weight = 700

    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 title : typography = { family: font.sans; size: 18px; weight: 700; line-height: 1.3 }

    token shadow/card : elevation = raw "0 4px 12px -2px rgba(15,23,42,0.12)"
    token fast : duration = 120ms
    token standard : easing = raw "cubic-bezier(0.2, 0, 0, 1)"
  }
}

component Button "The primary action, in three emphases." {
  archetype Button
  group Actions

  props {
    variant : context Variant = Primary "Which emphasis the button carries."
    disabled : boolean = false
    loading : boolean = false "Shows a spinner in place of the label."
    loadingLabel : string required when loading "Read out while the spinner replaces the label."
    label : string = "Get started"
    icon : string "A glyph shown before the label, by name."
  }

  events {
    click
  }

  part Button: container {
    emits { click on activate }
    bind state disabled disabled
    bind state loading loading
    layout { stack row; gap space.sm; align center; distribute center }
    fill accent
    ink on-accent
    border accent hairline
    corner radius.sm
    inset space.sm space.lg
    when variant = Secondary {
      fill surface
      ink text
      border line hairline
    }
    when variant = Ghost {
      fill surface
      ink accent
      border surface hairline
    }
    state hovered {
      fill accent-hover
      border accent-hover hairline
    }
    state hovered when variant = Secondary {
      fill accent-subtle
    }
    state hovered when variant = Ghost {
      fill accent-subtle
      border accent-subtle hairline
    }
    state focus-visible {
      ring accent
    }
    state disabled {
      opacity 50
    }
    motion {
      transition fill ink duration fast easing standard
    }
  }

  part icon: icon {
    content bind icon
    ink on-accent
    when variant = Secondary {
      ink text
    }
    when variant = Ghost {
      ink accent
    }
  }

  part label: text {
    slot of content default
    content bind label
    text body
    weight weight.medium
  }
}

component IconButton "A button whose only visible content is an icon." {
  archetype Button
  group Actions

  props {
    icon : string required
    label : string "The accessible name, when nothing visible names the button."
    labelledBy : string "The part that names the button, when one does."
    one-of label, labelledBy
  }

  events {
    click
  }

  part IconButton: container {
    emits { click on activate }
    layout { stack row; align center; distribute center }
    fill surface
    ink text
    corner radius.sm
    inset space.sm
    state hovered {
      fill accent-subtle
      ink accent
    }
    state focus-visible {
      ring accent
    }
  }

  part icon: icon {
    content bind icon
    ink text
    when ancestor IconButton hovered {
      ink accent
    }
  }

  part label: text {
    content bind label
    presentation accessible-name
  }
}

component Badge "A small status pill." {
  archetype Badge
  group Feedback

  props {
    tone : enum(neutral, success, warning) = neutral
    label : string = "New"
  }

  part Badge: container {
    layout { stack row; align center }
    fill accent-subtle
    ink accent
    corner radius.pill
    inset space.xs space.sm
    when tone = success {
      fill success
      ink on-accent
    }
    when tone = warning {
      fill warning
      ink gray.900
    }
  }

  part label: text {
    slot of content default
    content bind label
    text size.sm
    weight weight.bold
  }
}

component Card "A surface composed from the components above." {
  group Layout
  props {
    tone : enum(neutral, success, warning) = neutral
  }

  part Card: container {
    layout { stack column; gap space.md; size fill fit }
    fill surface
    ink text
    border line hairline
    corner radius.lg
    inset space.xl
    elevation shadow.card
  }

  part Header: container {
    layout { stack row; gap space.sm; align center; distribute between }
  }

  part Header.Title: text {
    content "Project Aurora"
    text title
  }

  part Header.Status: instance {
    instance Badge {
      label = "Beta"
      tone = bind tone
    }
  }

  part Body: text {
    slot of content default
    ink text-muted
    text body
  }

  part Footer: container {
    layout { stack row; gap space.sm; distribute end }
  }

  part Footer.Primary: instance {
    instance Button {
      label = "Open"
    }
  }

  part Footer.Secondary: instance {
    instance Button {
      label = "Share"
      variant = "Secondary"
    }
  }
}

component TextField "A single-line input with an error state." {
  archetype TextField
  group Forms

  props {
    label : string required "What the field asks for. Also its accessible name."
    value : string
    placeholder : string = "you@example.com"
    invalid : boolean = false
    error : string "Shown under the control while invalid."
    disabled : boolean = false
  }

  events {
    valueChange
  }

  part TextField: container {
    bind state disabled disabled
    bind state error invalid
    layout { stack column; gap space.xs }
  }

  part label: text {
    content bind label
    text size.sm
    weight weight.medium
  }

  part control: input {
    content bind placeholder
    emits {
      valueChange on input payload bind value
    }
    bind value value
    value string
    a11y { labelled-by label; described-by hint }
    layout { size fill }
    fill surface
    ink text
    border line hairline
    corner radius.sm
    inset space.sm space.md
    text size.md
    state focus-visible {
      border accent hairline
    }
    state error {
      border warning hairline
    }
    state disabled {
      opacity 50
    }
  }

  part hint: text {
    present when invalid
    content bind error
    ink warning
    text size.sm
  }
}

component Switch "One boolean, as a sliding switch." {
  archetype BooleanInput
  group Forms

  props {
    checked : boolean = false "Whether it is on."
    label : string required
    disabled : boolean = false
  }

  events {
    checkedChange
  }

  part Switch: container {
    bind state disabled disabled
    layout { stack row; gap space.sm; align center }
  }

  part toggle: input {
    control switch
    a11y { name bind label }
    emits {
      checkedChange on change payload bind checked
    }
    bind value checked
    value boolean
  }
}

component Dialog "A blocking layer that asks one question." {
  archetype Dialog
  group Feedback

  uses presentation(surface: dialog) as P
  uses dismiss as D
  wire D.dismissRequested -> P.close

  props {
    trigger : string = "Open" "The label of the control that opens the dialog."
    title : string required "The dialog's accessible name."
    open : boolean = false
  }

  part Dialog: container {
    bind state open open
  }

  part trigger: control {
    layout { stack row; gap space.sm; align center; distribute center }
    fill accent
    ink on-accent
    corner radius.sm
    inset space.sm space.lg
    state focus-visible {
      ring accent
    }
  }

  part trigger.label: text {
    content bind trigger
    weight weight.medium
  }

  part backdrop: container {
    fill text
    opacity 50
  }

  part surface: container {
    layout { stack column; gap space.md }
    fill surface
    ink text
    corner radius.lg
    inset space.xl
  }

  part surface.title: text {
    content bind title
    text size.lg
    weight weight.bold
  }

  part surface.content: container {
    slot of content default
    ink text-muted
  }
}

rule one-primary-per-card warning {
  max 1 Button where variant = Primary within Card
  because "A card has one way forward. Two primary buttons make the reader choose between them."
}

rule no-badge-in-button warning {
  never Badge within Button
  because "A badge inside a button is read as part of its label, so the count becomes part of the action's name."
}

rule no-stacked-dialogs error {
  never archetype Dialog within archetype Dialog
  because "A dialog over a dialog traps focus twice. Replace the content of the first one instead."
}

rule values-from-tokens warning {
  tokens color, spacing, radius
  because "Colours, spacing and radii come from the foundations, so a theme change reaches every component."
}

example "Card with actions" for Card {
  part stage: instance {
    instance Card {
      tone = "success"
    }
  }
}

assets {
  folder brand
  svg "logo" in brand {
    mime "image/svg+xml"
    view-box "0 0 24 24"
    url "assets/logo.svg"
  }
}

icons {
  set lucide "Lucide" { provider lucide; license ISC }
  glyph lucide check { label "Done"; svg "<path d='M20 6 9 17l-5-5'/>" }
  custom "logo-mark" { asset logo; label "Acme" }
}

docs {
  site { name "Acme"; tagline "One system, every surface" }
  footer { copyright "Acme, 2026" }
  page "Getting started" in "Guides" {
    heading "Getting started" anchor top
    paragraph "Install the package, import the theme, and build from the components below."
    component-group Actions
  }
  page "Button" in "Components" {
    heading "Button" anchor top
    paragraph "Use one primary action per view."
    component Button
    component-props Button
    component-events Button
  }
  page "Card" in "Components" {
    heading "Card" anchor top
    component Card
    example "Card with actions"
  }
}