Components, parts & the contract

A component is two things: a contract, the props and events consumers code against, and an anatomy, the tree of parts it is made of. Both start from an archetype, and both are written in the file in full, by name.

#The contract

The contract is the component's public surface and its identity. A release derives its version from how the contract changed; a select realized as a wheel and one realized as a listbox are the same component because they share one.

  • A prop is name : type, then optionally a default, or required when a condition over the other props, a to one of the archetype's parameters, annotations such as @status deprecated, and its prose in quotes. The prose travels into the generated types and into DESIGN.md.
  • Types are neutral: string, number, boolean, enum(a, b), array<T>, object { f : T }, union(T, U), component(A, B) for a slot of components, a shared by name, and context Group (see Tokens & context). Each target maps them to its own.
  • Constraints between props - , , - are written in the props block and enforced by the generated types where a type can. See Usage rules & constraints.
  • Events are declared in events { } and raised by parts: a part's block says on which interaction (activate, change, input, focus…) and with what payload.

#The anatomy

A part is one node of the component. Its name is a path, and the path is how the tree is written down: Header.Title is the child Title of the part Header. The first part is the root; a part with no dotted prefix is a child of the root.

milda
component Card {
  part Card: container {
    layout { stack column }
  }
  part Header: container {
    layout { stack row; align center }
  }
  part Header.Title: text {
    content "Project Aurora"
  }
  part Header.Close: control {
    a11y { name "Close" }
  }
  part Body: container {
    slot of content default
  }
}

A part is written as a header and a body. The header says what the part is: its kind, after the colon: container, item, text, control, input, output, icon, content (media), fragment (a grouping with no box), table, row, cell, instance, foreign. The platform element is never written: a target derives it from the kind, the archetype and the part's role.

Which parts a component cannot do without is not written at all: it is what the archetype requires. A Button has a label, a Dialog has its content, and the editor will not let you remove them or lift them out of their place. A file may leave a required part out and the reader adds it; your editor reports the gap as an error on the component and offers to write the part in.

The body says everything else, each as a statement of its own:

  • Provenance: origin author for a part you added; an archetype-born part has the default and is printed without one.
  • Presence: present when <predicate>. Absent means not in the tree, not hidden. The predicate grammar is on Facets.
  • Content: content "Save", content bind label, or a block of rules tried in order. For an icon part the content is the glyph's name; for an input it is the placeholder.
  • Data: value string for what an input holds, bind value <prop> to make a prop that value, bind state <state> <prop> to drive a state from a prop, repeat items as item to draw the part once per element of a list. What a binding means for behaviour is on Behaviours.
  • Naming: a11y { name …; labelled-by <part>; described-by <part> }, by part path.
  • Media and links: media img, source, alt, .
  • Realization choices the archetype offers: control checkbox | switch, group radio | segmented, presentation accessible-name, and the per-archetype config blocks (calendar, tabs, slider…).
  • Paint, layout, states and motion - on their own pages: Facets, Layout.
milda
part label: text {
  content {
    default bind label
    when loading -> "Saving…"
    when disabled -> "Unavailable"
  }
}
It is content, not text
text is the paint facet for a type style (text body). A keyword whose meaning depended on whether its argument was a string or a word would read fine and parse wrong, so a part's text is content.

#Archetypes

You rarely start from a blank tree. archetype Button brings an invariant contract, a published anatomy with named parts and roles, and a composition of behaviours. The archetype is the component's correctness; your work is the contract you expose, the tokens you bind and the states you paint. A component without an archetype is a surface: every part is yours and it brings no behaviour. The catalogue is on Archetypes.

#Slots

A slot is a part the consumer fills. Its arity (single, optional, many) and content contract (of content, of MenuItem) are part of the public API; default says this slot receives positional children, as gives it a public name, keyed-by names the field that identifies each item of a many slot, and sample is a design-time fill the preview shows and nothing generates.

milda
component Menu {
  archetype Menu
  props {
    items : array<string>
  }
  part Menu: container {
  }
  part trigger: control {
    slot optional of content as trigger role trigger sample "Open"
  }
  part surface: container {
    slot many of MenuItem role panel keyed-by value
  }
}

component MenuItem {
  props {
    value : string required
    label : string
  }
  part MenuItem: item {
    content bind label
  }
}

#Instances

A part can be another component of the library, nested: part Status: instance { instance Badge { tone = bind tone } }. Props are fixed or bound to the outer component's props. A component cannot contain itself, directly or through others. Instances are how a Card is built from a Badge and two Buttons, and how the generated code imports one component into another.

#Foreign content

When the vocabulary cannot say what one target needs, a part carries that target's code in quarantine, tagged with the target. A reader that does not implement the target keeps the text and skips it, and the rest of the system stays portable. It is the escape hatch of last resort; slots and per-target options come first.

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

#Composition

A component may also name its behaviour composition - , , - in the algebra the prelude uses to define archetypes. See Behaviours for what that means today.

These words, in the reference

  • A shared type, declared once and used by name in any prop.
  • What a prop, a demo variable or a part value holds.
  • A prop declared once for the whole system, so every component that takes it agrees on its name, type and default.
  • A component of the library: its contract (props, events), its anatomy (parts), and how it is realized.
  • The component’s public inputs.
  • Takes system-wide props declared with global-prop.
  • Descriptive annotations on a prop, such as its lifecycle.
  • The events the component raises.
  • A node of the component’s anatomy.
  • What the part IS, in neutral terms.
  • Where the part came from: provenance.
  • The part exists only while the predicate holds - not hidden, absent.
  • The part is another component of the library, nested, with its props set.
  • Code written in one target’s own language, quarantined.
  • The part is a hole the consumer fills: the label of a button, the items of a menu.
  • The text the part shows: fixed, bound to a prop, or chosen by conditions.
  • The type of value an input part holds, such as string for a text field or number for a slider.
  • The part is drawn once per item of a list prop (or of a fixed list).
  • A table cell’s role: a data cell, or a header cell for its column or its row.
  • What kind of media a content part shows.
  • Where a media part’s file comes from: a fixed asset of the library, or a prop - with an optional sample the preview shows while the prop is empty.
  • A media part’s text alternative, fixed or bound to a prop.
  • Where a navigating part goes - a link’s target - fixed or bound to a prop.

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

See it built in the tour3. A first component6. Anatomy: parts, kinds and slots7. Composition: instances and groups