Components reference
All 24 semantic components: props, when to use them, accessibility, rendering rules and platform mappings.
Generated from packages/spec/components/*.json, the same source the schema, validator and renderer use. Every component also accepts id, component, key, accessibility and visible.
| Component | Web | iOS | Android |
|---|---|---|---|
| Card | shadcn Card (CardHeader/CardTitle/CardContent/CardFooter) | GroupBox, or a Button/NavigationLink with card styling in lists | Card / ElevatedCard / OutlinedCard (onClick variant when actionable) |
| Disclosure | shadcn Collapsible (or Accordion for several) | DisclosureGroup | Expandable ListItem / AnimatedVisibility with a toggle row |
| Group | <div role=group> with flex/grid and semantic spacing tokens | VStack / HStack / Grid (ViewThatFits for inline) | Column / Row / FlowRow |
| Section | <section> + heading; shadcn has no Section primitive (plain markup with semantic tokens) | Section(header:) inside List/Form, or VStack with .accessibilityAddTraits(.isHeader) on the title | Column with a Text heading marked Modifier.semantics { heading() } |
| Views | shadcn Tabs | Picker(.segmented) switching content, or TabView for top-level | PrimaryTabRow + content |
| Chart | shadcn Chart (Recharts) | Swift Charts (Chart) | Third-party (e.g. Vico) or Canvas; no built-in M3 chart |
| Collection | List markup with Card or row template; ScrollArea for long lists | List + ForEach | LazyColumn + items |
| DetailList | <dl> with semantic tokens | LabeledContent rows in a Form/List section | ListItem(headlineContent, trailingContent) rows |
| Media | <img> / shadcn AspectRatio | AsyncImage | AsyncImage (Coil) / Image |
| Metric | Card-like block with semantic tokens (no dedicated shadcn primitive) | LabeledContent or a VStack; Gauge for bounded values | Column with Text styles |
| Table | shadcn Table (optionally Data Table with TanStack) | Table on regular width; List of rows on compact width | LazyColumn of rows with a header row (no built-in M3 table) |
| Text | <p> with type tokens | Text | Text |
| Status | shadcn Alert / Skeleton / Sonner toast | ContentUnavailableView / ProgressView / inline Label | Snackbar / Card with status color / CircularProgressIndicator |
| Choice | shadcn ToggleGroup / RadioGroup / Select / Combobox (Command + Popover) / Checkbox | Picker (.segmented / .inline / .menu) or multi-select List | SegmentedButton / RadioButton rows / ExposedDropdownMenuBox / Checkbox rows |
| DateInput | shadcn Calendar + Popover (Date Picker) | DatePicker | DatePicker / DateRangePicker |
| Form | shadcn Form (react-hook-form + zod) | Form | Column of fields + Button (no Form primitive) |
| RangeInput | shadcn Slider | Slider | Slider |
| TextInput | shadcn Input / Textarea with Label and FormMessage | TextField / TextEditor with .keyboardType and .textContentType | OutlinedTextField with KeyboardOptions |
| Toggle | shadcn Switch / Checkbox | Toggle | Switch / Checkbox |
| Action | shadcn Button (default / secondary / ghost / destructive) | Button with .borderedProminent / .bordered / .borderless; role: .destructive | Button / FilledTonalButton / OutlinedButton / TextButton |
| ActionBar | Flex row of shadcn Buttons (DialogFooter-style) | .toolbar or .safeAreaInset(edge: .bottom) | BottomAppBar or Row of Buttons |
| Comparison | shadcn Table or Cards grid | Grid / Table on regular width; List of GroupBox on compact | LazyRow of Cards or Column of Cards |
| Confirm | shadcn AlertDialog | .confirmationDialog / .alert with role: .destructive | AlertDialog |
| Steps | Progress + step content (no shadcn stepper primitive) | NavigationStack pushes or a paged view with a ProgressView | Custom stepper Row + content; LinearProgressIndicator |
Cardstructure
One self-contained entity (an account, an order, a place), optionally actionable as a whole.
| Prop | Type | Notes |
|---|---|---|
title required | text or binding | Name of the entity |
subtitle | text or binding | |
media | component id | Optional Media component id |
badge | object | |
children | component ids | |
action | action | Activating the card as a whole (e.g. open details) |
Use when
- Items in a Collection that each represent an entity
- A single entity summary in a larger surface
Don't use when
- Plain grouping: use Group
- Tabular data with many attributes: use Table
Accessibility article (or link/button when it has an action)
- Title is the accessible name
- When actionable, the whole card is one target; nested actions are not allowed then
Agents: Activate the card by its title.
Rendering rules
- If 'action' is set, children must not contain Actions (one target per card)
- Uses surface.raised and radius.default
A2UI: Card
Disclosurestructure
Progressive disclosure: secondary content hidden behind a toggle.
| Prop | Type | Notes |
|---|---|---|
summary required | text or binding | Always-visible label describing what is hidden |
children required | component ids | |
open | boolean |
Use when
- Details most people don't need (fees breakdown, advanced options, help text)
Don't use when
- Content everyone needs to complete the task
- Primary navigation
Accessibility button (aria-expanded) + region
- Summary is a button with expanded state
- Hidden content is not in the tab order while closed
Agents: Expand by the summary text; state is exposed as expanded/collapsed.
Rendering rules
- Never hide required inputs or the primary action inside a closed Disclosure
A2UI: No direct equivalent; exports as Column (content always shown)
Groupstructure
Visually groups closely related items without a heading (proximity).
| Prop | Type | Notes |
|---|---|---|
children required | component ids | |
label | text or binding | Accessible name for the group when it isn't obvious from its content |
arrangement | "auto" | "inline" | "grid" | Hint only. 'inline' for a few short peers (e.g. metrics); renderers stack on narrow screens. |
Use when
- A few items that belong together (a key figure and its caption, several metrics)
Don't use when
- The group needs a heading: use Section
- Choosing between items: use Choice or Comparison
Accessibility group
- Has an accessible name when it contains interactive items
Agents: Treated as one unit when its label is present.
Rendering rules
- 'inline' collapses to a stack below the compact breakpoint
- Gap uses space.stack.default or space.inline.default
A2UI: Column or Row
Sectionstructure
A titled region of the surface that groups related content under a heading.
| Prop | Type | Notes |
|---|---|---|
title required | text or binding | Heading for the section |
description | text or binding | Optional one-line explanation under the heading |
children required | component ids |
Use when
- The surface has two or more distinct topics (e.g. 'Recipient' and 'Amount')
- Content would otherwise exceed one screen and needs signposting
Don't use when
- Only one topic: put content directly under the root
- Visual grouping without a heading: use Group
Accessibility region (with heading)
- Title is rendered as a real heading; nesting depth sets the heading level
- Title is the region's accessible name
Agents: Navigate by heading; the title names the region.
Rendering rules
- Heading level follows nesting depth, never chosen by the model
- Spacing between sections uses space.stack.section
A2UI: No direct equivalent; exports as Column with a Text(variant: h2) first child
Viewsstructure
Switch between alternative views of the same subject (tabs).
| Prop | Type | Notes |
|---|---|---|
views required | list of objects | |
selected | binding | Key of the selected view in host data |
Use when
- 2–6 peer views of the same data (Overview / Transactions / Settings)
Don't use when
- Sequential steps: use Steps
- Filtering one list: use Choice
Accessibility tablist / tab / tabpanel
- Each tab has a visible label
- Arrow keys move between tabs
Agents: Select a view by its label.
Rendering rules
- More than 4 views on compact screens become a menu or scrollable tabs
A2UI: Tabs
Chartcontent
A visual summary of data, always paired with a text summary.
| Prop | Type | Notes |
|---|---|---|
intent required | "trend" | "comparison" | "composition" | "distribution" | What the chart should communicate; the renderer picks the chart type |
data required | binding | Array of data points in host data |
title required | text or binding | |
summary required | text or binding | Text alternative stating the takeaway (required) |
x required | object | |
series required | list of objects |
Use when
- A trend, comparison, share or spread matters more than exact values
Don't use when
- Exact values matter most: use Table or Metric
- A single number: use Metric
Accessibility img (figure) with the summary as description, plus a data-table alternative
- 'summary' states the takeaway in words
- Series are distinguishable without color (labels, markers)
- The underlying data is available as a table
Agents: Reads the summary, and the data through the table alternative.
Rendering rules
- trend → line, comparison → bar, composition → stacked bar (pie only for ≤ 4 parts), distribution → histogram
- Series colors use color.data.categorical.N in order
A2UI: No equivalent in the Basic catalog; exports as Text(summary) + List
Collectioncontent
A list of items from host data, each rendered with the same template.
| Prop | Type | Notes |
|---|---|---|
items required | { path, componentId } | |
label required | text or binding | What the list contains, e.g. 'Recent payments' |
empty | component id | Status component shown when there are no items |
selection | "none" | "single" | "multiple" | |
selected | binding | Selected item value(s) in host data when selection is not 'none' |
Use when
- Browsing a set of similar entities
- Search results
Don't use when
- Attributes compared across items: use Table or Comparison
- Choosing an option in a form: use Choice
Accessibility list / listitem (listbox when selectable)
- Announces the item count
- An empty state is provided
Agents: Items are enumerated with their titles; each item exposes its own actions.
Rendering rules
- Long lists are virtualized by the renderer
- Items keep their order from host data
A2UI: List (templated children)
DetailListcontent
Label/value pairs describing one thing (a summary, a receipt, a review step).
| Prop | Type | Notes |
|---|---|---|
items required | list of objects | |
title | text or binding |
Use when
- Reviewing before submitting
- Showing the attributes of one entity
- Receipts
Don't use when
- Many entities with the same attributes: use Table
- Comparing entities: use Comparison
Accessibility list of term/definition pairs (dl)
- Label and value are programmatically associated
Agents: Read each label with its value.
Rendering rules
- Keeps the item order stable across generations (keys are remembered)
A2UI: Column of Row(Text, Text)
Mediacontent
An image supplied by the host.
| Prop | Type | Notes |
|---|---|---|
src required | binding | Image reference in host data; generated UI never contains URLs |
alt | text or binding | Required unless decorative |
decorative | boolean | |
aspect | "square" | "landscape" | "portrait" | "auto" |
Use when
- Photos or illustrations that help identify something (a product, a place, a person)
Don't use when
- Decoration with no information
- Icons for actions (renderer supplies those)
Accessibility img
- Has alt text unless decorative (then hidden from assistive technology)
Agents: Reads the alt text.
Rendering rules
- Images never convey information that is not also in text
A2UI: Image
Metriccontent
A key figure with a label, and optionally its change.
| Prop | Type | Notes |
|---|---|---|
label required | text or binding | |
value required | binding | Always bound to host data |
format | format | |
change | object | |
caption | text or binding |
Use when
- One to four headline numbers the user asked about
Don't use when
- Many numbers: use Table or DetailList
- Trends over time: use Chart
Accessibility group (label + value)
- Change direction is conveyed in text as well as color
- Screen readers read label, value and change as one sentence
Agents: Read label and value.
Rendering rules
- Value uses type.numeric.display
- Change uses color.data.positive / negative according to 'favorable'
A2UI: Column of Text
Tablecontent
Tabular data: many items sharing the same attributes.
| Prop | Type | Notes |
|---|---|---|
rows required | binding | Array of row objects in host data |
caption required | text or binding | What the table shows (required for accessibility) |
columns required | list of objects | |
rowAction | action | Activated per row; context bindings resolve against the row |
empty | component id | Status component shown when there are no rows |
Use when
- Scanning or comparing many rows by several attributes
Don't use when
- One entity: use DetailList
- A few entities where the user must choose: use Comparison
Accessibility table with caption and column headers
- Has a caption
- Column headers are real header cells
- Numeric columns are right-aligned by format
Agents: Read by row and column headers; rows with rowAction are activatable.
Rendering rules
- On compact screens, rows become stacked cards with label/value pairs
- Alignment follows the column format, never chosen by the model
A2UI: No direct equivalent; exports as List of Row
Textcontent
A run of text.
| Prop | Type | Notes |
|---|---|---|
text required | text or binding | |
variant | "body" | "supporting" | "caption" | |
format | format | Only when 'text' is bound to a non-string value |
Use when
- Explanations, instructions, messages
Don't use when
- Headings: use Section title
- Label/value pairs: use DetailList
- Key figures: use Metric
Accessibility text
- Body text is at least 16px (type.body.default)
- Line length is capped by measure.max
Agents: Read as text.
Rendering rules
- 'supporting' uses color.text.muted
- No inline styling; emphasis comes from structure
A2UI: Text
Statusfeedback
Feedback about state: info, success, warning, error, empty or loading.
| Prop | Type | Notes |
|---|---|---|
kind required | "info" | "success" | "warning" | "error" | "empty" | "loading" | |
title required | text or binding | |
message | text or binding | |
action | component id | Optional Action to recover or continue (e.g. 'Try again') |
Use when
- Results of an action
- Empty collections
- Errors with a way to recover
- Loading states
Don't use when
- Validation errors on a field: inputs show their own errors
- Confirmation before acting: use Confirm
Accessibility status or alert (live region)
- success/info/loading are polite live regions; error is assertive
- Meaning is in text, not only color or icon
Agents: Reads the title and message; the recovery action is exposed.
Rendering rules
- error and warning use color.status.* tokens and an icon
- empty states explain why and what to do next
- loading shows a skeleton when layout is known, otherwise a progress indicator
A2UI: Text + Icon
Choiceinput
Pick one or several options from a known set. The renderer chooses the control.
| Prop | Type | Notes |
|---|---|---|
label required | text or binding | |
options required | options (list or from data) | |
value required | binding | Two-way binding into host data |
mode | "single" | "multiple" | |
help | text or binding | |
required | boolean |
Use when
- Any choice from a known set of options
Don't use when
- On/off setting: use Toggle
- Picking between rich entities by comparing attributes: use Comparison
Accessibility radiogroup / group of checkboxes / listbox / combobox (depends on rendering)
- Visible group label
- Every option has a text label
Agents: Select options by label.
Rendering rules
- single, 2–4 short options → segmented control or radios
- single, 5–10 → radios (or select on compact screens)
- single, > 10 → searchable select
- multiple, ≤ 10 → checkboxes; > 10 → searchable multi-select
- Option order comes from host data or remembers the last order used
A2UI: ChoicePicker
DateInputinput
A date, time, date-time or date range.
| Prop | Type | Notes |
|---|---|---|
label required | text or binding | |
value required | binding | ISO 8601 value(s) in host data |
kind | "date" | "time" | "datetime" | "dateRange" | |
min | text or binding | |
max | text or binding | |
help | text or binding | |
required | boolean |
Use when
- Any date or time entry
Don't use when
- Relative choices like 'This month / Last month': use Choice
Accessibility group of spinbuttons or a date picker dialog
- Typing the date is always possible, not only picking from a calendar
Agents: Fill by label with an ISO date.
Rendering rules
- Memorable dates (birthdays) use separate day/month/year fields; near dates use a calendar
A2UI: DateTimeInput
Forminput
Collects inputs and submits them together.
| Prop | Type | Notes |
|---|---|---|
children required | component ids | |
submit required | { label, action } | |
cancel | { label, action } | Optional; defaults to ui.dismiss when omitted and the surface is dismissible |
Use when
- Any set of inputs that are submitted together
Don't use when
- Settings that apply immediately: use Toggles with actions
- Several distinct stages: use Steps
Accessibility form
- Submit is a real submit button
- On submit errors, focus moves to an error summary that links to each field
Agents: Fill fields by label, then activate the submit button by its label.
Rendering rules
- One column; labels above fields
- Submit is the primary action; cancel is secondary
- Submit context is built from the form's input bindings
A2UI: Column + Button
RangeInputinput
A number within known bounds where the exact value matters less than its position.
| Prop | Type | Notes |
|---|---|---|
label required | text or binding | |
value required | binding | Number in host data |
min required | number | |
max required | number | |
step | number | |
format | format |
Use when
- Approximate values within a range (budget cap, volume)
Don't use when
- Exact values: use TextInput with kind 'number' or 'currency'
Accessibility slider
- Current value is shown as text
- Keyboard arrows change the value by step
Agents: Set the value directly by label.
Rendering rules
- Shows min, max and the current formatted value
A2UI: Slider
TextInputinput
A single text-like value: text, number, email, phone, currency, search or long text.
| Prop | Type | Notes |
|---|---|---|
label required | text or binding | |
value required | binding | Two-way binding into host data |
kind | "text" | "multiline" | "number" | "currency" | "email" | "phone" | "url" | "search" | |
currency | text or binding | ISO 4217 code when kind is 'currency' |
help | text or binding | Hint shown below the label (not a placeholder) |
required | boolean | |
validation | object | |
autocomplete | string | HTML autocomplete token hint (e.g. 'email', 'postal-code') |
Use when
- Free-form values the user types
Don't use when
- A value from a known set: use Choice
- Dates: use DateInput
- Secrets (passwords, card numbers): not generated; hosts provide their own secure flows
Accessibility textbox / searchbox / spinbutton
- Visible label, never placeholder-only
- Help and errors are associated with the field
- Kind sets the right keyboard and autocomplete
Agents: Fill by label.
Rendering rules
- Width suggests expected length
- Errors appear after the user leaves the field or submits, not while typing
A2UI: TextField
Toggleinput
An on/off setting.
| Prop | Type | Notes |
|---|---|---|
label required | text or binding | |
value required | binding | Boolean in host data |
description | text or binding | |
action | action | Set when the change applies immediately (a setting); omit inside a Form |
Use when
- Settings that take effect immediately
- A single yes/no inside a form
Don't use when
- Choosing between named options: use Choice
Accessibility switch (immediate) / checkbox (in a form)
- Label says what 'on' means
Agents: Toggle by label; state is exposed as on/off.
Rendering rules
- With 'action' it renders as a switch; inside a Form without action, as a checkbox
A2UI: CheckBox
Actionaction
A button that triggers a host capability.
| Prop | Type | Notes |
|---|---|---|
label required | text or binding | Verb-first label saying what happens ('Send £20', not 'OK') |
action required | action | |
emphasis | "primary" | "secondary" | "tertiary" | |
tone | "default" | "danger" | |
disabled | boolean or binding |
Use when
- Anything the user can do that isn't typing or choosing
Don't use when
- Navigating between views: use Views
- Confirming something destructive: wrap in Confirm
Accessibility button
- Label describes the outcome
- Target at least size.target.min
- Disabled actions explain why nearby
Agents: Activate by label.
Rendering rules
- At most one primary action visible at a time
- danger tone uses color.action.danger.*
A2UI: Button
ActionBaraction
The set of actions for a surface or section; the renderer places it where that platform expects.
| Prop | Type | Notes |
|---|---|---|
children required | component ids | Action component ids, in order of importance |
Use when
- Two or more actions that apply to the whole surface or section
Don't use when
- A single action inline with content: use Action directly
Accessibility toolbar / group
- Order in the accessibility tree is order of importance
Agents: Actions are listed together.
Rendering rules
- Web: bottom of the section, primary last on desktop and first on mobile stacks
- iOS/Android: toolbar or bottom bar on compact screens
A2UI: Row of Button
Comparisonflow
Compare a few options across the same attributes, and choose one.
| Prop | Type | Notes |
|---|---|---|
items required | binding | Array of options in host data (2–4 recommended) |
itemTitle required | JSON Pointer | Relative path to each item's name |
attributes required | list of objects | |
choose | { label, action } | Action per item; context bindings resolve against the item |
recommended | text or binding | Optional value of the recommended item's title, with a reason in 'summary' |
summary | text or binding |
Use when
- Choosing between 2–4 plans, products, routes or offers
Don't use when
- More than ~5 items: use Table with sorting
- No decision needed: use Table
Accessibility table (items as columns on wide screens) or list of cards
- Attribute names are headers
- 'better' is conveyed in text, not only color
Agents: Reads attributes per item; choose actions are named with the item title.
Rendering rules
- Wide: items as columns; compact: one card per item with the same attribute order
- Best values per attribute are marked when 'better' is set
A2UI: No direct equivalent; exports as List of Card
Confirmflow
Asks the user to confirm a consequential or destructive action, showing what will happen.
| Prop | Type | Notes |
|---|---|---|
title required | text or binding | Question naming the action ('Send £250 to Alex?') |
message | text or binding | |
consequence | text or binding | What happens and whether it can be undone |
summary | component id | Optional DetailList summarising what is being confirmed |
severity required | "consequential" | "destructive" | |
confirm required | { label, action } | |
cancel | { label, action } | Defaults to 'Cancel' → ui.dismiss |
typeToConfirm | text or binding | Text the user must type before confirming (for irreversible, high-impact actions) |
Use when
- Moving money, deleting data, sending on someone's behalf, anything irreversible
Don't use when
- Routine reversible actions (prefer undo)
- Information only: use Status
Accessibility alertdialog
- Focus starts on the least destructive option
- Confirm label repeats the action verb
- Escape cancels
Agents: Reads title and consequence; confirm and cancel are named buttons.
Rendering rules
- destructive uses color.action.danger.*
- Capabilities with high risk level must use Confirm (enforced by the verifier)
A2UI: Modal with Text and Buttons
Stepsflow
A task split into ordered steps with visible progress.
| Prop | Type | Notes |
|---|---|---|
steps required | list of objects | |
current | number or binding | Index of the current step (default 0) |
finish required | { label, action } |
Use when
- Tasks with dependent stages or too many inputs for one view (checkout, onboarding)
Don't use when
- Independent views: use Views
- Under ~6 inputs: a single Form
Accessibility group with step progress (aria-current on the current step)
- Current step and total are announced
- Back never loses entered data
Agents: Reads 'step N of M'; next/back are exposed as buttons.
Rendering rules
- Renderer provides Back (ui.back) and Next (ui.next); the last step shows 'finish'
- Each step validates before moving on
A2UI: No direct equivalent; exports as Column of the current step
Polyxd is an early preview. Found something unclear? It will get better with your feedback.