Behaviours
Interaction in Milda is modelled, not coded. A small set of atoms, each a formal state machine, is composed by every archetype, and the states those machines emit are what paint keys on. This page lists the vocabulary, read off the prelude, and says what the file can say about it and what the React target does with it.
#Three layers
| The spec defines | A .milda file can say | The React target does |
|---|---|---|
19 atoms as state machines, 7 composites built from them, and a composition algebra: , , , broadcast with aggregates, and arena for gestures racing for one input. | Which archetype a component starts from, and so which composition it has. On a part: which event an interaction raises, which prop drives a state, and the free behaviours (behavior focus-trap…). On a component: , and , carried in the file. | Realizes every archetype's composition as idiomatic React: state hooks, ARIA, keyboard handling, portals. Reads a component's event emissions, state bindings and free behaviours. Does not yet read an authored // block. |
A component can declare uses, wire and gate, and the file carries them. No target realizes an authored composition yet: a component’s behaviour is still its archetype’s.proposal 0024
#The atom model
An atom is not a widget. It is an abstract interaction state machine: a few named states, the events that move between them, and the outputs it emits along the way. A toggle is two states, one event that flips them, and a change output, with nothing about checkboxes or tap targets. Three properties make atoms platform-neutral invariants:
- They emit states, not pseudo-classes. Paint keys on
checked,open,selected, never on:hover. - They gate on capabilities, not platforms. An atom that needs a keyboard declares
input.keyboard; a target without it drops or substitutes the atom. No atom mentions a platform by name. - They compose through a small algebra. Wire one machine's output to another's input, listen only under a condition, fan out to a set, arbitrate a race. A select is activation opening a presentation, browse moving the highlight, selection committing the value, dismiss closing the surface: five stock machines and a few wires. The iOS wheel changes one wire.
#The atoms
19 indivisible machines. For each: what it does, the states it exposes for paint, what it requires, and the archetypes that compose it.
Invoke an action + emit event/payload on a trigger.
- Exposes
- no states: a transducer
- Composed by
- Button, Link, FileUpload
transducer (single state); foundational atom under almost every interactive archetype
schedulertimeout · debounce · intervalTimed transition: auto-dismiss, auto-advance, debounce, delay.
- Exposes
- no states: a transducer
load-bearing: all time-based interactions compose from it
Flip a boolean (BooleanInput core; tristate → +mixed).
- Exposes
checked- Composed by
- ToggleButton, BooleanInput
selectionsingle · multiple · range · noneChoose member(s) of a collection.
- Exposes
selected- Composed by
- ChoiceGroup, SingleSelect, MultiSelect, Combobox, DatePicker, TimePicker, TagsInput, Rating, Accordion, Tree, List, Table, Grid
multiple=set; range=anchor+extent (date range, shift-click, cell range)
browseroving · spatial · tree · scroll-detentMove a cursor over a collection.
- Exposes
active- Requires
- input.keyboard or input.remote (mode∈{roving,tree})
- Composed by
- MenuButton, ChoiceGroup, SingleSelect, MultiSelect, Combobox, DatePicker, TimePicker, Tabs, ContextMenu, Menu, Tree, List, Table, Carousel, Grid
neighbor(): roving=linear · spatial=2D · tree=in/out/sib · scroll-detent=nearest
presentationinline · popover · menu · sheet · dialog · push · wheelManage a surface.
- Exposes
open- Composed by
- MenuButton, SingleSelect, MultiSelect, Combobox, DatePicker, TimePicker, ColorPicker, Dialog, Drawer, Popover, Tooltip, ContextMenu, Menu, Toast
surface is realization-only
Close on a configured trigger.
- Exposes
- no states: a transducer
- Composed by
- MenuButton, SingleSelect, MultiSelect, Combobox, DatePicker, TimePicker, ColorPicker, Dialog, Drawer, Popover, ContextMenu, Menu
trigger ∈ configured [outside, escape, commit, swipe-down, timeout]
Trap / contain / restore focus within a region (modal a11y).
no visual states (exposes none) - a behaviour guarantee, not a paint surface; realized as a focus trap on web, OS-owned on native sheets
Free text/value entry; commit on change|blur|enter.
- Exposes
focused,empty,dirty- Composed by
- TextField, TextArea, SearchField, Combobox, Stepper, ColorPicker, TagsInput
Value(s) along a min/max track (single/dual thumb).
- Exposes
active- Composed by
- Slider, ColorPicker
live (change) vs committed (commit)
Increment/decrement a value.
- Exposes
atMin,atMax- Composed by
- Stepper, Pagination
Valid/invalid against rules (PURE - does not listen to input).
the when:change|blur|submit policy = a wire from value-entry/submission into check
Form-level orchestration: collective validation, submit, focus-first-error.
- Exposes
loading,dirty- Composed by
- Form
fields: DYNAMIC set of validation machines; FAN-OUT broadcast + FAN-IN aggregate/select
gestureswipe · long-press · pinch · pan · drag · dropSwipe | long-press | pinch | pan | drag | drop.
- Exposes
dragging- Requires
- input.touch (per gesture)
- Composed by
- Slider, FileUpload, Drawer, Carousel
load-bearing: all drag-based interactions compose from it
Idle | loading | success | error lifecycle.
- Exposes
loading,success,error- Composed by
- Button, FileUpload, Skeleton
Observe scroll position: infinite-load, scroll-spy, reveal-on-scroll, parallax.
- Exposes
atStart,atEnd,scrolled
atEnd → wire → async-state.start = infinite-load
progressdeterminate · indeterminateDeterminate / indeterminate completion.
- Exposes
complete- Composed by
- ProgressBar
livenesspolite · assertiveAnnounce dynamic changes (polite/assertive).
- Exposes
- no states: a transducer
- Requires
- a11y.screenreader
- Composed by
- Toast, Alert, ProgressBar
#Composites
7 familiar behaviours are not atoms. They are compositions, kept as first-class conveniences that compile down to their canonical form:
disclosure=toggle+presentation- ≈ toggle(expanded) + presentation(inline); exposes expandedtypeahead=scheduler+browse- jump to a member by typed prefix; requires input.keyboarddnd=gesture+ordering- drag-and-drop move/reorder across containers; ordering is a non-behavior (collection mutation)resize=gesture- drag-resize a region/panel; requires input.pointerauto-dismiss=scheduler- self-close after a timeout (toast)pressability=gesture- press/hover/active feedback (ripple, highlight); contributes pressed/hovered feedback states ONLY - does not fire the action (that is activation)
#Decorations
Decorations shape how something happens or is presented without adding states of their own; they decorate machines and surfaces the way paint decorates parts:
motion- decorates existing transitions (spring/flip/fade+slide); adds no statesmasking- format/constrain entry (date/phone/currency) - decorates value-entry draftanchoring- position a surface relative to a reference (placement/flip/offset/match-size) - decorates presentation(popover|menu)backdrop- scrim / blur behind a layer - decorates a modal presentation; painted via the `backdrop` facet on the backdrop anatomy partmomentum-scroll- inertial scroll with snap / detent - decorates scroll/browse on touch surfaces
#Non-behaviours
Four things that look like behaviours are deliberately excluded. They transform data or the render, not interaction, and belong to the data layer or the generator:
filter- derived view over a collection (data, not interaction)virtualization- render windowingordering- collection mutation (reorder/sort)aggregation- data transformation (grouping/totals) - DataGrid math
#The states
The vocabulary paint keys on: the atom states every target can draw, plus ambient gates, collection-position pseudo-states and the calendar's cell states. It is a closed list, and a word outside it is reported. A machine state no target draws yet, such as validation's invalid, is reached through a bound prop instead. Each one, and the atom or source that contributes it:
hoveredpressabilitypressedpressabilityfocusedvalue-entryfocus-visiblevalue-entry (keyboard focus)activebrowseselectedselectioncheckedtoggleindeterminatetoggle (tristate)expandeddisclosureopenpresentationdisabledambient (global gate)readonlyambientloadingasync-stateerrorasync-statesuccessasync-statefirstcollection-positionlastcollection-positionoddcollection-positionevencollection-positiontodaythe calendar core (DatePicker day)outside-monththe calendar core (DatePicker day)range-startthe calendar core (range mode)range-endthe calendar core (range mode)in-rangethe calendar core (range mode)highlightedbrowse (the calendar cursor)
How a part uses them: Facets, under “States and conditions”.
#What a file says about behaviour
A component names its archetype and gets its composition. Beyond that, the file speaks about behaviour in four places, all on parts or in the contract:
emits { change on input payload bind value }- which declared event an interaction raises, with what.bind value value,bind state open open- a prop as the part's value; a prop driving a state.a11y { labelled-by label },control switch,group segmented, the per-archetype config blocks - naming and realization choices the archetype offers.behavior roving-tabindex { orientation vertical }- a free behaviour attached to a part, from a closed list: focus-trap, typeahead, cursor-navigation, clickaway, overlay-management, scroll-lock, roving-tabindex, filter.
#Authored composition
A component may also write its composition in the algebra the prelude uses, under aliases that and refer to:
archetype SingleSelect props { options : array<string> required value : string bind selected placeholder : string = "Choose" } events { change from S.change }
uses presentation(surface: dialog) as P uses dismiss as D wire D.dismissRequested -> P.close gate D active when open
The reader lowers this into the document and the file round-trips it. No target reads it yet: a component's behaviour today is its archetype's canonical composition, which for every archetype is the same wiring the spec writes down. The statement is in the language because the spec is, and because an agent proposing a new interaction needs a place to say it.
These words, in the reference
- Composes a behaviour atom from the prelude into the component, under an alias that
wireandgaterefer to. - Coordination between composed behaviours: when one atom signals, another acts.
- A composed behaviour is only active while a prop condition holds.
- A capability the component needs (
input.pointer,platform.web). - Which declared event this part raises, and on what interaction.
- A prop drives something about the part.
- A free behaviour attached to the part: portable interactions that are not tied to an archetype.
- Inside a part: a presentation intent.
- How assistive technology names and describes the part: a name of its own (fixed or bound), or another part’s text by path.
- A demo interaction, meaningful only inside an
example: when the part is activated, flip or set one of the scene’sdemovariables.
Open a word for its entry, or read them all in the reference.
See it built in the tour8. Inputs and behaviour4. Variants and states