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 Cardplaces 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 Buttonin a page is a live preview,component-props Buttonits props table,component-group Actionsevery 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’sdemovariables. - 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" } }