AuraProvider
Sets the four `data-*` attributes that drive the whole token stylesheet, and exposes the current values plus setters. Token overrides go through `vars` as inline custom properties (CSSOM), so a strict `style-src` CSP is respected.
Thin components over the shared stylesheet — each one emits the same aura-* classes in every framework, which is what keeps the four libraries looking identical. The dots show what each framework ships: ● full,◐ shipped with differences,○ not ported,▣ a class, no wrapper needed.
The font map. Picks size, weight, tracking and line-height from the token scale, and renders a sensible element per variant (h1 for `h1`, p for `body`, code for `mono`).
The button primitive. Two independent axes: `variant` decides how the colour is applied — a fill (`primary`), a ring (`secondary`), a bare label with a control-sized box (`ghost`) or without one (`text`) — and `tone` decides which colour, from the same twelve-step scales the badges and alerts read. Forwards every native button attribute.
A surface lifted off the canvas: `--surface` is a step lighter than `--canvas` in both themes, with a shadow under it. `elevation` names a shadow token rather than a shadow — `flat` is `--shadow-1`, not none — and the dark theme redefines all three tokens as denser black, because the soft blue-grey shadow that lifts a card off a white canvas is invisible on a near-black one. A titled card is the commonest thing built out of this one, so three classes are provided for it — `.aura-card__header`, `.aura-card__extra` and `.aura-card__footer` — rather than slots on the component: they go on your own `<header>` and `<footer>`, and they pull back out to the card’s edges so the rule under a title reaches both of them instead of stopping inside the padding.
`accent` is the unmodified base class; the other tones map to the semantic colour tokens. A tone here is not a palette — it is one colour and the strength of its wash, read through `--aura-badge-color` and `--aura-badge-tint`. So the set is open at both ends: `.aura-badge--warning { --aura-badge-tint: 22% }` restyles a built-in tone without touching the alerts and toasts that share `--warning`, and a class of your own naming those two properties is a sixth tone, with no fork of the stylesheet and no prop of ours to add.
Square icon-only control. Aura ships no icon set — bring your own SVG as the child.
Tag / chip, optionally dismissible. The dismiss glyph ships with the component; the four libraries name its event differently.
Toggle with `role="switch"` and `aria-checked` — the stylesheet keys off the ARIA state, not a class. Toggles on click and on Space/Enter (it is a real button).
Labelled text input. The label is wired to the input with a generated id when you do not supply one.
A scroll region. The bound is a prop — `maxHeight` — because a box free to grow never overflows, so a scroll container with no height is an ordinary div. The gutter is reserved whether or not a bar is showing, so the text does not shift sideways the moment the content crosses that bound, and the region does not hand the wheel to the page behind it when it reaches its end.
A real `<input type="checkbox">` under a custom paint job. The label wraps the input rather than pointing at it, so no id is needed and two on a page cannot collide.
Radios that are actually a group: they share one `name`, so the browser makes them mutually exclusive and gives arrow-key navigation — which moves *and* selects — for free. A `<fieldset role="radiogroup">` with a `<legend>` announces the group name with every option.
A single `<input type="radio">`. Rarely useful alone — radios are mutually exclusive only when they share a `name`, and arrow keys move within that group, so reach for `RadioGroup` unless you are laying the group out yourself.
The field's twin, minus the fixed height. `resize: vertical` only — a textarea that can be dragged wider escapes whatever laid it out. `autoGrow` makes it grow with the content instead, in CSS: `field-sizing: content` is the whole implementation, so there is no keystroke handler measuring `scrollHeight`, and it keeps working in markup that was never hydrated. Where the property is unsupported the control stays a fixed box with a scrollbar.
A select backed by a real listbox: grouping, descriptions, and options you render yourself — none of which a native `<select>` can carry, which is why this is the one the library ships. The trigger is a `<button role="combobox">`, so focus never leaves it: the cursor through the list is virtual, and there is nothing to trap and nothing to restore.
Pick several. Each choice becomes a chip in the field, and the field is a text input you filter the list from — so choosing and searching happen in one place instead of two. `role="combobox"` sits on that input rather than on the box around it: the thing announced as the list's owner has to be the thing the keyboard is in. The chips are `Chip`, at `sm`. One thing the pattern does not give you, in any of the four: a screen reader on the input does not read the chips back. Each chip's dismiss button carries its own name ("Remove Apple"), which is the part that is reachable, and APG's own multiselect has the same shape — recorded here rather than patched with an `aria-describedby` that would go stale.
Groups related controls — a real `<fieldset>`/`<legend>`, not a div with `role="group"`. The native pair is what makes a screen reader announce the legend with every control inside, and `disabled` on it disables them all without touching one of them.
Collapsible panels on `<details>`/`<summary>` — no state, no handlers, no JavaScript. The native pair already is a button that announces its expanded state, toggles on Enter and Space, and is findable by in-page search: the browser opens a closed panel to reveal a match.
A row of mutually exclusive options — which is to say, a radiogroup, and built as one. The radios are hidden by paint only, never by `display: none`: that would take them out of the tab order and out of the group the arrow keys walk. The browser keeps giving exclusive selection and navigation; the label only draws.
A message that needs attention. Polite by default: `role="alert"` makes a screen reader interrupt whatever it was reading, which is right for a message that just appeared and wrong for one that was on the page all along — set `urgent` only for the former. The tone is a colour and the strength of its wash, read through `--aura-alert-color` and `--aura-alert-tint`, so `.aura-alert--brand { --aura-alert-color: var(--brand) }` is a sixth tone with no fork of the stylesheet.
A face, or the initials standing in for one. The name lives on the root rather than the image: with initials there is no `<img>` to carry an `alt`, and the letters themselves are decoration — "AB" read aloud is noise. Which is also why `initials` is optional: the avatar already has the name, so the letters are derived from it, and passing them is for the names where that derivation is wrong.
Overlapping stack of avatars. Each keeps a ring in the canvas colour, which is what stops two adjacent faces from merging into one shape — drawn with a box-shadow, so it costs no layout.
A placeholder for content that has not arrived. Always `aria-hidden` — reading "loading" once per placeholder is worse than silence; put `aria-busy` on the waiting region instead, since that is the thing with a name.
An indeterminate spinner. The wrapper is `role="status"` and carries the label; the ring is `aria-hidden`, because a spinning border has nothing to say. It keeps turning under `prefers-reduced-motion` — a frozen spinner reads as a hang, not as calm.
A determinate bar. The number is stated once and rendered twice from the same source — the element carries `--aura-value` and the stylesheet turns it into a width, while `aria-valuenow` reports it. Values are clamped: a bar past its own end is a bug the caller should not see.
Inline code, and its block twin. The pill is for a fragment inside a line of prose; the block drops the fill for a frame, because a filled background behind many lines fights the text it is meant to hold.
A quotation, marked by a leading accent rule rather than by italics — a whole paragraph in italic is harder to read, not more quoted.
An ordered or unordered list with the markers toned down. `--plain` strips them entirely, for a list that is a list semantically but not visually — a nav, a feed, a stack of cards.
Highlighted text — a wash of the accent behind the words. The text colour is left alone on purpose: repainting it as well is how a highlight ends up unreadable.
A link. Underlined by default, and not as decoration: colour alone is not an affordance, and a link that is only accent-coloured text disappears for anyone who cannot separate it from the prose.
A dot on the corner of something — unread, online, needs-attention. The wrapper is only a positioning context, so it does not disturb the layout of what it wraps. With a count inside, the dot grows into a pill rather than clipping the number.
A determinate ring — the same `role="progressbar"` as `Progress`, drawn as one conic-gradient rather than an SVG. `label` names it and the children fill its middle: a ring reading "70%" still has to say what is 70%.
A stack of transient messages, and the first thing in the library with a real exit. The viewport is a live region and renders even when empty — a region added to the page at the same moment as its first message is a region assistive tech was not watching, and the message is simply lost.
A tablist and its panels. SegmentedControl looks similar and is a different thing: that is a radiogroup on real inputs, so the browser gives it exclusive selection, arrow keys and a roving tabindex for free. A tab has no native element to stand on, so the keyboard is written — it lives in `@ilomee/aura-core/listbox`, next to the cursor a tablist shares with a listbox and a menu.
A modal dialog: a scrim, a centred card, a focus trap and a scroll lock. All of it existed already, inside a recipe called FormDialog — which made that recipe the only way to get a modal, so a dialog that was not a form meant rebuilding the trap, the lock, the scrim click and the aria by hand. With this extracted, the recipe was a Dialog with a form in it, and that is an example rather than a component. A titled dialog with a row of buttons at the bottom is built from the same three composition classes a card uses — `.aura-card__header`, `.aura-card__extra` and `.aura-card__footer` — on your own `<header>` and `<footer>` inside the default slot. They are named for the card because `.aura-dialog` wears `.aura-card`; there is no second set to learn, and the negative margin that pulls the hairline out to the panel edges is the whole reason they exist.
An anchored panel of arbitrary content — a card in a popup, which is what the note on `.aura-popup` has always said a popover is. The difference from a tooltip is not the shape but what the reader can do with it: a tooltip is a label and closes when the pointer leaves, a popover holds things to interact with, so it opens on click, keeps focus, and closes on Escape or a pointerdown outside. Attaching it is the one thing each package spells its own way — React clones the trigger element, Vue the vnode, Svelte hands the trigger props to spread, Angular is a directive taking the panel as a template — and none of them adds an element: a wrapper would port everywhere and sit between the trigger and its own layout.
A list of commands, anchored to whatever opened it. The third thing that hangs off `place()`, and the difference from the other two is what the reader does with it: a tooltip is read, a popover is worked in, a menu is fired from and then gone — so every row dismisses it. It is not a Select either: a select reports a value that stays chosen and is announced as such, a menu fires and reports nothing, which is why no row here is ever `aria-selected` and the list carries no tick. Focus is the other half of that difference. A select keeps focus on its trigger and points `aria-activedescendant` at the row from there; a menu moves focus into the list, and Tab closes it rather than cycling inside — a menu you cannot leave forwards is a trap, not a menu. Pass `point` and it opens at a pair of coordinates instead of beside the trigger, which is what a context menu is.
A description that appears on hover or focus, next to what it describes. The pointer waits out a delay — resting on something is ambiguous, and the delay is what disambiguates it — while the keyboard opens at once, because arriving by Tab is a decision already made. A tooltip has no element of its own, so each package attaches it the way that adds nothing to the tree — React clones its child, Vue the vnode, Svelte attaches to the node, Angular is a directive that takes its own attribute back off once it has matched. A wrapper element would port everywhere and be worse: on a flex child it would change the layout it was only meant to annotate. ⚠️ A tooltip on a `disabled` control never opens, and no wrapper of ours will change that: a disabled element emits no pointer events at all, so nothing fires to open it — which is exactly backwards, because the tooltip on a disabled button is the one that says *why* it is disabled. The answer is `aria-disabled="true"` instead of `disabled`, plus an early return in your own handler. The control stays focusable and hoverable, announces itself as disabled, and can be explained; the stylesheet paints it inert and — since this was checked — no longer lets it lift, recolour or press under the pointer.
Contribution-style heatmap. Intensity is derived from the values, so it needs no colour configuration.
Config-driven table. Columns carry their own renderer, so the table stays generic over the row type.
Stepper header with the current step marked. You own the step bodies.
Weekly calendar grid with a live “now” line.
Flex container, vertical by default. The workhorse — most Aura layouts are a Stack of Groups.
Flex container, horizontal by default. Same knobs as Stack, but `direction` accepts every CSS flex-direction.
A row that wraps. For tag lists, button rows, anything that should reflow rather than overflow.
CSS grid with two modes: a fixed/responsive column count, or intrinsic auto-fit via `minItemWidth`. Breakpoints resolve in pure CSS — there is no media-query JS.
Centred, max-width page column with inline padding.
Column-flow masonry. Uses CSS columns today and upgrades itself to native grid masonry via `@supports` where the browser has it.
A rule. Renders a real `<hr>` in React, so it is a landmark for assistive tech rather than a decorative div.
A `flex: 1` shim that pushes its siblings apart. Hidden from assistive tech.