Components
Seven documented specimens pair preview states, concise guidance, and copyable snippets without implying a live backend. The site-wide System/Light/Dark preference lives in the header.
Links
Use anchors for navigation in prose. The gallery keeps a deliberate visited style and leaves keyboard focus to the browser.
Preview
Read the field guidance, revisit the sample scan findings, or inspect the loading specimen.
Visited styling appears after your browser records a destination, so the sample above uses real anchors instead of a fake disabled link.
State examples
Default
Usage guidance
- Keep link text specific to the destination instead of repeating “click here.”
- Visited styling is intentional for prose links so readers can tell which references they have opened.
- Never present navigation as disabled; if an action is unavailable, use a button or explanatory text instead.
Copyable examples
HTML
<p>
Read the <a href="#field-specimen-title">field guidance</a>.
</p>Panda
const proseLink = css({
color: 'accent',
textDecoration: 'underline',
textUnderlineOffset: '3px',
_visited: { color: 'accent.strong' },
})Buttons
Buttons stay native for actions. Show a pressed state only on toggle buttons, and use the disabled attribute when the action truly cannot run.
Preview
The pressed example is a toggle button specimen only; ordinary submit buttons should not use aria-pressed.
State examples
Default
Hover
Hover should reinforce the action without changing the label.
Focus-visible
Tab to buttons; Space or Enter activates them.
Pressed toggle
Use only when the button keeps an on/off state.
Disabled
Disabled buttons are skipped by keyboard focus.
Usage guidance
- Choose links for navigation and buttons for in-page actions or form submission.
- Only persistent toggles should use
aria-pressed; one-off actions stay unpressed. - Disabled buttons should explain why elsewhere when the next step is not obvious.
Copyable examples
HTML
<button type="button" aria-pressed="true">
Sample alerts on
</button>Panda
const actionButton = button({ variant: 'primary' })
const quietButton = button({ variant: 'secondary' })Fields
Single-field forms still need associated hints, actionable errors, and room for read-only or success states.
Preview
State examples
Required
Hints match the input width on narrow screens.
Error
Provide a reachable sample URL.
⚠ Add https:// so the address is complete.
Read-only success
✓ Sample target saved for the next review.
Usage guidance
- Keep every hint and error associated with the field through
aria-describedby. - Error text should explain the fix, not just restate that something is invalid.
- Read-only fields remain focusable and selectable, which helps people copy sample values.
Copyable examples
HTML
<label for="site-url">Website URL</label>
<input id="site-url" type="url" aria-invalid="true"
aria-describedby="site-url-hint site-url-error">
<p id="site-url-hint">Provide a reachable URL.</p>
<p id="site-url-error">Add https:// so the address is complete.</p>Panda
const field = fieldInput()
const invalidField = css({ borderColor: 'critical' })
const successField = css({ borderColor: 'positive' })Switches
Switches represent an immediate on/off setting. The track and knob follow aria-checked, so visual and announced state cannot drift.
Preview
Live specimen
Demonstration only: the site-wide System/Light/Dark preference lives in the header. This specimen keeps its track and knob in sync with its own state.
State examples
Off
On
Use aria-checked to expose state.
Disabled
Space toggles switches; disabled ones stay inert.
Usage guidance
- Use switches for immediate preferences, not deferred form submissions.
- A switch needs a visible label plus
aria-checkedto announce its state. - Native buttons already support Space and Enter, so
role="switch"can build on that behavior.
Copyable examples
HTML
<button type="button" role="switch" aria-checked="false"
aria-labelledby="alerts-label">
<span class="knob"></span>
</button>
<span id="alerts-label">Email alerts</span>Panda
const switchOff = switchTrack()
const switchOn = switchTrack({ on: true })Cards and status
Sample scan cards stay obviously static. Icons and text communicate state together so the specimen does not rely on color alone.
Preview
Sample scan results
Sample result
Homepage scan
18 checks passed, 2 warnings, 0 failures.
Sample result
Checkout flow
Render-blocking script appears on the payment step.
State examples
Passing
Accessibility summary
Keyboard checks passed in this sample.
Informational
Queue note
This card shows static copy only; no network request has been sent.
Warning
Images review
One decorative image still needs an empty alt attribute.
Critical
Form submission
The contact form posts to an endpoint that returns an error.
Usage guidance
- Label examples as samples whenever the page is not showing live application data.
- Pair status color with an icon or word so the meaning survives monochrome and high-contrast modes.
- Match the token to the stakes:
infofor neutral notes,warningfor cautions that do not block,criticalonly for errors and destructive actions. - Keep the card body focused on the next action or takeaway instead of reproducing raw scanner output.
Copyable examples
HTML
<article class="card">
<h3>Homepage scan</h3>
<p>18 checks passed, 2 warnings, 0 failures.</p>
<p>✓ Passing sample</p>
</article>Panda
const resultCard = card()
const passingStatus = css({ color: 'positive' })
const infoStatus = css({ color: 'info' })
const warningStatus = css({ color: 'warning' })
const criticalStatus = css({ color: 'critical' })Loading patterns
Loading feedback is bounded, labelled, and explicitly demo-only. Nothing animates until you ask, and reduced motion keeps it still.
Preview
Static by default. The preview runs four spinner turns and three skeleton pulses (about four seconds), then stops. With reduced motion requested, the examples stay still.
Example only — this gallery does not start a backend scan.
Use skeletons for known layout while content is on the way.
Static by default. Preview runs once, then stops automatically.
State examples
Spinner
Pair any indicator with text; the text carries the meaning.
Skeleton
Decorative placeholders stay out of the accessibility tree.
Disabled while loading
Reserve disabled loading buttons for actions already in progress.
Usage guidance
- Announce active work with text, not with motion alone.
- Keep loading indicators inside the region they describe so users can tell what is pending.
- If the layout is already known, skeletons can reduce surprise while content arrives.
Copyable examples
HTML
<div role="status" aria-label="Loading sample results"></div>
<p>Preparing sample scan results</p>Panda
const busySpinner = spinner()
const loadingSkeleton = skeleton()Layout primitives
Three token-driven patterns, container, stack and grid, cover page structure, so pages compose layouts instead of writing display: grid by hand. This page is built from them.
Preview
container()
stack({ gap: '3' })
State examples
Stack
Vertical flow, spacing-token gap.
Grid, fixed columns
columns: 2; tracks never overflow.
Grid, auto-fit
minChildWidth: 64px; wraps on its own.
Usage guidance
- Reach for
container,stackandgridbefore hand-written layout CSS; shared patterns reuse the same atomic classes, so new pages add almost no bytes. - Gaps take spacing tokens (1–12), and the default is spacing 4 (16px), so layouts stay on the 4px rhythm.
- Use
columnsfor a fixed count andminChildWidth(a CSS length) when items should wrap on their own. Both keep every track inside a 320px viewport.
Copyable examples
HTML
<main class="container">
<section class="stack">
<h2>Scan results</h2>
<ul class="grid">…</ul>
</section>
</main>Panda
import { container, stack, grid } from '../styled-system/patterns/index.mjs'
const page = container() // 1080px, token padding
const section = stack({ gap: '6' }) // column, 24px gap
const results = grid({ minChildWidth: '240px' }) // wraps, never overflows
const stats = grid({ columns: { base: 2, md: 4 } })