Control

A small on-page control inside a showcase: pick a preset, switch a mode, toggle a view.

Atom · beta · v1.0.0 · owner: Site system lead · contract 19 of 20 points (open: figma)

Do and don't

Do: A preset chip

Rendered by control.ts

Open example: Control · Do: A preset chip Use the full-page example to follow page links.

One tap shows the result.

Do: A pressed toggle

Rendered by control.ts

Open example: Control · Do: A pressed toggle Use the full-page example to follow page links.

The state is visible and announced.

Don't: A numbered option

Rendered by control.ts

Open example: Control · Don't: A numbered option Use the full-page example to follow page links.

Options are named by what they do.

Don't: A control as navigation

Rendered by control.ts

Open example: Control · Don't: A control as navigation Use the full-page example to follow page links.

Moving to another page is a link.

Generated API variants, rendered live

Generated variants of the first authored example, from selected boolean and enum props. These are API explorations, not authored state declarations or verification results. Hover and focus respond to the pointer and the keyboard in the examples above.

Base authored example

Rendered by control.ts

Open example: Control · Do: Base authored example Use the full-page example to follow page links.

One tap shows the result.

pressed

Rendered by control.ts

Open example: Control · Do: pressed Use the full-page example to follow page links.

With pressed.

Rules

RuleWhyCheck
Toggles and segments expose aria-pressed.Without it, a screen reader cannot tell which option is on.Automated: axe on showcase pages (tests/site/pages.spec.ts)
Controls only appear inside a ShowcaseFrame or a form.Elsewhere they would look like actions without a purpose.Automated: scripts/site/system/check-pages.ts

API

PropTypeRequiredDefaultWhat it does
kindchip | toggle | segmentYesWhich control.
labelstringYesThe option, in words.
pressedbooleanNofalseWhether it is on (aria-pressed).
attrshtmlNoData attributes the showcase script reads.

Contract details

The full contract: identity, anatomy, variants, states, tokens, layout, motion, accessibility, content, tests

Identity

AspectValue
Idcontrol
LayerAtoms
Categoryaction
OwnerSite system lead
Statusbeta
Version1.0.0
Sourceapps/showcase/site/system/atoms/control.ts (private repository; the contract and the render function are this one module)
VersionDateChange
1.0.02026-10-26The small controls inside showcases: chips, toggles and segmented choices.

Use when

  • A showcase lets the reader change what it shows. Why: Trying it is the proof; the control must look like the site's other controls.
  • Choosing one of a few presets. Why: Chips show every option at once.

Do not use when

  • For the page's main action. Why: That is the primary button.
  • For navigation. Why: Links take the reader somewhere; controls change the page in place.

Anatomy

PartWhat it is
rootbutton.chip, button.toggle or a .seg in a .segmented group.
labelThe option's words.

Made of: tokens:type, tokens:colour, tokens:radius.

Used in: playback-controls, academy-lesson, agent-check, autonomy-demo, before-after, layers-demo, ops-board, read-your-stylesheet, roles-picker, score-us.

Variants

VariantWhenWhy
chipA preset among a few.All choices visible, one tap each.
toggleOn or off.aria-pressed tells assistive technology the state.
segmentOne of two to four modes.A segmented group shows that the choices exclude each other.

States

StateRendered or not applicable
defaultOutlined in --line-strong
hoverBackground --surface-2.
focus-visibleThe --focus ring.
activePressed: ink background, cotton text (aria-pressed=true).
disabledA caller may pass the native disabled attribute through attrs. The atom does not own playback, hide itself or manage an async action.
loadingNot applicable: No intrinsic pending/async API; the containing feature owns any async action and its status.
emptyNot applicable: A visible label is required content; the renderer has no empty-state variant or nonempty-label validation.
errorNot applicable: The atom performs no validation/fetch; the containing feature owns action and data failures.
currentThe selected segment uses the pressed style.
visitedNot applicable: Not a link.
expandedNot applicable: Does not disclose content.

Tokens

PartToken
root.border--rule-hair --line-strong
root.font--type-small or --type-label
pressed.background--ink
pressed.color--cotton
root.min-height44 px
--control-border-line-strongvar(--line-strong)
--control-border-radius-radiusvar(--radius)
--control-padding-space-3var(--space-3)
--control-padding-space-6var(--space-6)
--control-font-type-labelvar(--type-label)
--control-color-inkvar(--ink)
--control-border-left-line-strongvar(--line-strong)
--control-background-inkvar(--ink)
--control-color-cottonvar(--cotton)
--control-padding-space-1var(--space-1)
--control-padding-space-4var(--space-4)
--control-background-teal-softvar(--teal-soft)
--control-color-teal-inkvar(--teal-ink)
--control-background-saffron-softvar(--saffron-soft)
--control-color-saffron-inkvar(--saffron-ink)
--control-background-indigo-softvar(--indigo-soft)
--control-background-madder-softvar(--madder-soft)
--control-color-madder-inkvar(--madder-ink)
--control-gap-space-3var(--space-3)
--control-color-ink-2var(--ink-2)
--control-margin-right-space-2var(--space-2)
--control-gap-space-5var(--space-5)
--control-padding-space-5var(--space-5)
--control-border-inkvar(--ink)
--control-background-surfacevar(--surface)
--control-background-linevar(--line)
--control-transition-dur-slowvar(--dur-slow)
--control-top-space-1var(--space-1)
--control-left-space-1var(--space-1)
--control-transition-ease-standardvar(--ease-standard)
--control-background-teal-2var(--teal-2)
--control-background-cottonvar(--cotton)

Layout

AspectValue
breakpointsRequired review at 390, 768, 1024 and 1440 px; execution is not implied by this declaration; groups wrap onto more lines on phones.
wrappingLabels stay on one line; the group wraps.
textExpansionWraps the group, not the label.

Spacing and rhythm

AspectValue
internal--space-3 by --space-5.
externalGap --space-3 inside a group.
rhythmInside a showcase's control row.

Motion

AspectValue
movesBackground colour.
triggerHover and press.
duration--dur-fast
easing--ease-standard
interruptibleYes.
reducedKept: a colour change is not motion.
whyInstant feedback that the control took the press.

Runtime and reuse

  1. Render-only element; mount any registered child enhancements separately. Preserve required Site CSS, fonts, responsive and locale rules from nativeElement.

Accessibility passport

AspectValue
rolebutton (with aria-pressed for toggles and segments)
nameThe label
keyboardTab to it, Enter or Space to press.
focusIn order within its showcase.
screenReaderReads the label and 'pressed' or 'not pressed'.
contrastInk on cotton 15.9:1; pressed cotton on ink 15.9:1.
target44 px tall.

Content

SlotLengthVoice
label3 wordsThe option's name

Good: “Settings page” · “With an operating model”

Bad: “Option 1”

Japanese: Showcase strings come from the interactives' i18n tables (scripts/site/interactives/i18n.ts).

Icons and illustrations

None: this element uses no icon or illustration of its own.

Decisions and exceptions

Usage

35 uses across the built pages (measured by npm run site:system).

Tests

AspectValue
unittests/unit/site-system.test.ts: renders every do and don't example through the registry and checks the classes it owns
visualNot yet: there are no screenshot baselines; the Library page is checked for sideways scroll at 1440 px by tests/site/system.spec.ts
a11ytests/site/system.spec.ts: axe (0 serious or critical) on the element's Library page in light and dark
parityscripts/site/system/check.ts: Library examples are rendered by the same render function the site imports

Figma

not-yet: The Loom Site Figma library is the last stage of the plan; this element is not mapped yet.

Open in Storybook

Start with a conversation about your system.

We read your system, show you what we found, and agree with your team what to fix first.