Skip to content
Verdant
Theme

A design system for the W3C Web Sustainability Guidelines

Verdant

Sustainable Defaults for the Green Web

Start with the parts teams usually add later: system fonts, a System/Light/Dark theme preference, restrained motion, and only the CSS each page uses. Verdant gives a product page or docs site those lighter defaults from the first commit.

Initial render assets, local Brotli estimate
20.5 KiB
Offline shell + worker, unique local Brotli estimate
31.7 KiB
Cold first session, measured local transfer
52.6 KiB
Warm repeat visit, measured local transfer
11.6 KiB

Measured 2026-09-23 locally over HTTP with Brotli response bodies in KiB (1024 bytes), excluding headers, then rounded up to a stable tenth for this summary. Cold includes the service worker install and its duplicate precache fetches; warm is a repeat visit with the shell already cached. Production-network transfer is unmeasured here.

How it grows

From a small token set to a whole site.

Each layer only adds the parts the previous one cannot, which keeps the finished site small.

  1. 1 · Seed

    Tokens

    21 colors, a 4px spacing grid, four radii and system type. Small enough to stay consistent without one-off values.

  2. 2 · Sprout

    Components

    Button, card, field, theme preference, switch specimen and motion — each one demonstrates a WSG behavior live instead of describing it.

  3. 3 · Sapling

    Pages

    A product page or docs page gets landmarks, a skip link, one focus ring, one stylesheet and two small same-origin scripts from day one.

  4. 4 · Canopy

    Sites

    Security headers, cache rules, an offline shell and a real 404 — the hosting checks, handled before launch.

Palette

21 colors, shown in both themes.

One token set with a light and a dark value each. System mode follows prefers-color-scheme; explicit Light and Dark choices reuse the same tokens without a second stylesheet. Both values of every token are shown side by side.

Interface

Public colors for text, controls, state, and supporting UI tones. Use the verified pairings below rather than assuming every token combination is readable.

  • accent

    Light#2f6b4a
    Dark#7fcfa3
  • accent.strong

    Light#234f38
    Dark#5fb98c
  • positive

    Light#1f7a6c
    Dark#5cc9b7
  • info

    Light#2b5f8e
    Dark#8cbcea
  • warning

    Light#8a5a00
    Dark#f2c14e
  • critical

    Light#c1440e
    Dark#ff8f5e
  • focusRing

    Light#a5670a
    Dark#e8a83e
  • accent.ink

    Light#ffffff
    Dark#10241a
  • ink

    Light#1c1a15
    Dark#f1ede2
  • ink.muted

    Light#5b5548
    Dark#b6ae9c
  • ink.placeholder

    Light#75726a
    Dark#8d8a81
  • border

    Light#c7bda9
    Dark#514b3b
  • border.control

    Light#7f735b
    Dark#8f866f
  • surface.200

    Light#ffffff
    Dark#1e1c15
  • surface.100

    Light#faf8f3
    Dark#15140f

Illustration only

Decorative foliage and sunlight tokens. They are available for art, but not approved as default text, controls, or status meaning.

  • foliage.bright

    Light#6fcd4f
    Dark#86dc62
  • foliage

    Light#3ca24a
    Dark#45ad55
  • foliage.deep

    Light#1f6a31
    Dark#2a7d3a
  • foliage.mid

    Light#9ed65f
    Dark#2d6b34
  • foliage.far

    Light#cdeaae
    Dark#1c3a22
  • sunlight

    Light#f4b63f
    Dark#e8a83e

Verified text pairings

Measured from the shared theme tokens at build time. These are the approved readable combinations — not every token can be mixed freely. 3 of 13 reach WCAG AAA (7:1) in both themes; every one meets AA (4.5:1), which also makes it AAA for large text. The build fails below the stated target, and AAA is reported, not required.

  • Body text on site canvas

    ink on surface.100

    Default long-form copy and labels on the page background.

    Light 16.38:1 WCAG AAA · Dark 15.76:1 WCAG AAA · Target 4.5:1

  • Body text on raised surface

    ink on surface.200

    Card and field content when a component steps onto the raised surface.

    Light 17.38:1 WCAG AAA · Dark 14.57:1 WCAG AAA · Target 4.5:1

  • Supporting text on site canvas

    ink.muted on surface.100

    Secondary copy such as intros, captions and helper text.

    Light 6.97:1 WCAG AA · Dark 8.36:1 WCAG AAA · Target 4.5:1

  • Supporting text on raised surface

    ink.muted on surface.200

    Card bodies, palette tiles, hints and the bands that sit on surface-200.

    Light 7.40:1 WCAG AAA · Dark 7.73:1 WCAG AAA · Target 4.5:1

  • Placeholder text in fields

    ink.placeholder on surface.200

    Example values inside inputs, which sit on the raised surface.

    Light 4.80:1 WCAG AA · Dark 4.94:1 WCAG AA · Target 4.5:1

  • Links and accents on site canvas

    accent on surface.100

    Inline links, eyebrows and stat values on the page background.

    Light 5.95:1 WCAG AA · Dark 9.97:1 WCAG AAA · Target 4.5:1

  • Links and accents on raised surface

    accent on surface.200

    Links and accent text inside cards and bands.

    Light 6.32:1 WCAG AA · Dark 9.22:1 WCAG AAA · Target 4.5:1

  • Primary button label

    accent.ink on accent

    Default primary action styling.

    Light 6.32:1 WCAG AA · Dark 8.81:1 WCAG AAA · Target 4.5:1

  • Primary button label on hover

    accent.ink on accent.strong

    Hover state for the primary action.

    Light 9.35:1 WCAG AAA · Dark 6.83:1 WCAG AA · Target 4.5:1

  • Positive status on raised surface

    positive on surface.200

    Success status inside cards, e.g. the scorecard.

    Light 5.17:1 WCAG AA · Dark 8.52:1 WCAG AAA · Target 4.5:1

  • Info status on raised surface

    info on surface.200

    Neutral guidance or sample-only notes inside cards.

    Light 6.71:1 WCAG AA · Dark 8.52:1 WCAG AAA · Target 4.5:1

  • Warning status on raised surface

    warning on surface.200

    Non-blocking cautions inside cards, e.g. open items on the scorecard.

    Light 5.93:1 WCAG AA · Dark 10.16:1 WCAG AAA · Target 4.5:1

  • Critical status on raised surface

    critical on surface.200

    Errors, failures and destructive actions only.

    Light 5.12:1 WCAG AA · Dark 7.58:1 WCAG AAA · Target 4.5:1

Verified functional boundaries

Borders and focus indicators target at least 3:1 non-text contrast against the surfaces they appear on.

  • Control border on site canvas

    border.control on surface.100

    Input, secondary button and switch-track outlines on the page background.

    Light 4.39:1 WCAG 3:1 non-text · Dark 5.10:1 WCAG 3:1 non-text · Target 3:1

  • Control border on raised surface

    border.control on surface.200

    Input and secondary button outlines on the raised surface.

    Light 4.66:1 WCAG 3:1 non-text · Dark 4.71:1 WCAG 3:1 non-text · Target 3:1

  • Focus ring on site canvas

    focusRing on surface.100

    Visible focus indicator against the main page background.

    Light 4.35:1 WCAG 3:1 non-text · Dark 8.87:1 WCAG 3:1 non-text · Target 3:1

  • Focus ring on raised surface

    focusRing on surface.200

    Visible focus indicator inside cards and bands.

    Light 4.62:1 WCAG 3:1 non-text · Dark 8.20:1 WCAG 3:1 non-text · Target 3:1

Increased contrast

When the operating system asks for more contrast (prefers-contrast: more), 11 tokens switch to stronger values in either theme, control borders thicken to 2px and the focus ring to 3px. The build checks every pairing above against stricter targets in that mode: 7:1 for text (the weakest is 7.60:1) and 4.5:1 for control borders and focus rings.

  • ink.muted — Light #5b5548#3f3a30 · Dark #b6ae9c#d6cfbf
  • ink.placeholder — Light #75726a#4f4b42 · Dark #8d8a81#bdb8ab
  • border — Light #c7bda9#7f735b · Dark #514b3b#8f866f
  • border.control — Light #7f735b#4f4636 · Dark #8f866f#b3a88e
  • focusRing — Light #a5670a#8a5200 · Dark #e8a83e#ffc15a
  • accent — Light #2f6b4a#245a3d · Dark #7fcfa3#9be0bb
  • accent.strong — Light #234f38#1a3f2c · Dark #5fb98c#7fd0a5
  • positive — Light #1f7a6c#145e52 · Dark #5cc9b7#7fdccb
  • info — Light #2b5f8e#1f4f7a · Dark #8cbcea#a9cff2
  • warning — Light #8a5a00#6e4700 · Dark #f2c14e#ffd57a
  • critical — Light #c1440e#922e08 · Dark #ff8f5e#ffab85

Public token scope

What Verdant publishes, inherits, and keeps page-specific.

The palette above defines the supported color API. These cards document the rest of the token surface so adopters can tell reusable design tokens from Panda defaults and demo-only layout values.

Colors

Interface colors are the public text, control and status tokens. Illustration colors stay public for artwork only.

  • Interface: accent, accent.strong, positive, info, warning, critical, focusRing, accent.ink, ink, ink.muted, ink.placeholder, border, border.control, surface.200, surface.100
  • Illustration only: foliage.bright, foliage, foliage.deep, foliage.mid, foliage.far, sunlight

Spacing

Public spacing tokens follow a 4px rhythm.

  • 1 — 4px
  • 2 — 8px
  • 3 — 12px
  • 4 — 16px
  • 6 — 24px
  • 8 — 32px
  • 12 — 48px

Radii

Rounded corners stay on four reusable steps.

  • sm — 4px
  • md — 8px
  • lg — 16px
  • full — 999px

Typography

System fonts plus six public size/line-height pairs. If a brand needs its own typeface, follow the web-font off-ramp: self-hosted, subset WOFF2 with a CI byte budget.

  • sans — system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif
  • mono — ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace
  • displayLg — 32px / 40px
  • displayMd — 24px / 32px
  • displaySm — 20px / 28px
  • body — 16px / 24px
  • bodySm — 14px / 20px
  • label — 13px / 16px

Layout & breakpoints

Three layout primitives cover page structure; responsive and page-art values are called out separately. See the layout specimen.

  • container() — centered, capped at 1080px (override with maxWidth), inline padding of spacing 4, then 6 from md.
  • stack() — a flex column by default with a spacing-4 gap; takes direction, align, justify and gap.
  • grid()columns (a number or responsive object) makes minmax(0, 1fr) tracks; minChildWidth (a CSS length) makes auto-fit tracks capped at 100%; default gap is spacing 4.
  • Responsive rules use Panda defaults (base, sm, md, lg) because Verdant does not publish a custom breakpoint token scale.
  • Hero wordmark: 56px at base and 88px from md upward; this is page artwork, not a reusable type token.
  • Components and 404 page titles: 40px at base and 56px from md upward; also page-specific, not public tokens.

Saved evidence record

Implementation checklist for selected WSG-aligned defaults.

Verdant WSG evidence snapshot reviewed on 2026-09-22 (4 verified, 2 open). This section is generated from a saved artifact, not a live deploy audit, and it documents selected evidence rather than full WSG conformance.

Linked records: PR #18 — System/Light/Dark theme preference and its head-loaded bootstrap script, PR #19 — finalized measurements in dist/measurements.json, PR #20 — reduced-data behavior description, Evidence register (PR #26) — hosting provenance and governance

The card headings follow wsg-check's scanner categories, not the WSG section structure. Status is always a word plus a symbol, never color alone, and the full record is also available as JSON.

Built with PandaCSS

Tokens in, only-what-you-use CSS out.

Verdant fits in one PandaCSS config. Build-time extraction keeps the stylesheet tied to what the page actually renders.

  • Semantic tokens carry both themes; _dark maps to the OS preference.
  • Recipes for button, card, field and switch mirror the component guidelines.
  • No runtime CSS-in-JS reaches the browser.
// panda.config.ts
conditions: {
  dark: '@media (prefers-color-scheme: dark)',
},
theme: { extend: { semanticTokens: { colors: {
  accent: { value: { base: '#2f6b4a', _dark: '#7fcfa3' } },
  // …the other 20, same shape
} } } },
// No staticCss: ship only the rules pages use.

Use it in your next project.

Clone the repo, copy panda.config.ts, and keep the checklist. Start with the defaults, then layer in only the styling your product needs.