Accessibility
Semantic defaults, visible focus, reduced-motion handling, and Axe-tested in CI.
What ships by default
- Visible focus rings —
:focus-visibleoutlines on every interactive component - Skip link —
.b-skip-linkfor keyboard users - Screen reader only —
.b-sr-onlyfor visually hidden text - Reduced motion — animations and transitions auto-shorten when the user prefers reduced motion
- Semantic HTML — components are built on
<button>,<dialog>,<details>,<input>, etc. - ARIA wired up — tabs, dropdowns, popovers, dialogs all set the right ARIA attributes
- Color contrast — text/background pairs meet WCAG AA at every spec'd combination
Skip link
Add at the top of <body>:
<a class="b-skip-link" href="#main">Skip to content</a>
<header>...</header>
<main id="main">...</main>
The link is invisible until focused, then animates into view.
Focus management in dialogs
The bundled trapDialogFocus helper traps Tab focus inside an open <dialog>:
import { trapDialogFocus } from "blastcss/js";
const dialog = document.querySelector("#my-dialog");
trapDialogFocus(dialog);
autoInit() calls this for every <dialog> on the page.
Form labelling
Always pair <label class="b-label"> with an input via for/id or by nesting:
<div class="b-field">
<label class="b-label" for="email" data-required>Email</label>
<input class="b-input" id="email" type="email" required>
<span class="b-help">We'll never share this address.</span>
</div>
Use aria-describedby to wire the help/error text:
<input class="b-input" id="email" aria-describedby="email-help" aria-invalid="true">
<span id="email-help" class="b-error">Please enter a valid address.</span>
Keyboard support
| Component | Keys | | --- | --- | | Dialog | Esc closes, Tab cycles within, Shift+Tab reverses | | Drawer | Same as dialog | | Tabs | Arrow keys move focus, Home/End jump | | Combobox | ArrowUp/Down navigate, Enter selects, Esc closes | | Command palette | ⌘/Ctrl+K opens, ArrowUp/Down + Enter | | Dropdown (<details>) | Esc closes |
Reduced motion
@media (prefers-reduced-motion: reduce) {
* { transition-duration: 0.01ms !important; }
}
This is applied at the framework level — your custom transitions inherit the same treatment unless you opt out explicitly.
Color contrast
The OKLCH palette is tuned to meet WCAG AA contrast on standard pairings:
--b-texton--b-bg— AA Large + AA--b-mutedon--b-bg— AA Large--b-primary-inkon--b-primary— AA--b-primaryon--b-primary-soft— AA Large
If you customize the palette, validate contrast.
Testing
The repo ships with Axe checks in Playwright. Run:
npm run test:browser
Every fixture page must pass with zero violations (excluding color-contrast on dynamic backgrounds, which is whitelisted).