Skip to content
Verdant
Theme

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.

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

Visited tone

Browser-controlled once followed.

Focus-visible

Tab to links; Enter follows them.

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

Use a full https:// URL for this static example.

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

Email alerts

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

Motion follows system

On

Email alerts on

Use aria-checked to expose state.

Disabled

Locked by policy

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-checked to 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.

Passing sample

Sample result

Checkout flow

Render-blocking script appears on the payment step.

Needs attention

State examples

Passing

Accessibility summary

Keyboard checks passed in this sample.

Passing sample

Informational

Queue note

This card shows static copy only; no network request has been sent.

Sample only

Warning

Images review

One decorative image still needs an empty alt attribute.

Review before publish

Critical

Form submission

The contact form posts to an endpoint that returns an error.

Blocks publish

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: info for neutral notes, warning for cautions that do not block, critical only 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.

Preparing sample scan results

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

Loading sample data

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' })

Section heading
grid
minChildWidth
120px
wraps

State examples

Stack

1
2
3

Vertical flow, spacing-token gap.

Grid, fixed columns

1
2
3
4

columns: 2; tracks never overflow.

Grid, auto-fit

a
b
c

minChildWidth: 64px; wraps on its own.

Usage guidance

  • Reach for container, stack and grid before 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 columns for a fixed count and minChildWidth (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 } })