Skip to content

Accessibility Quality Bar

Buildit targets WCAG 2.1 AA compliance across the component library. This page documents the quality contract, how it is enforced, and the per-component coverage checklist.

Quality Contract

Every published component must satisfy:

RequirementStandardEnforcement
Keyboard navigabilityWCAG 2.1 AA §2.1Unit tests (keyboard events)
Visible focus indicatorWCAG 2.1 AA §2.4.7focus-visible CSS enforced via mixin
Colour contrast ≥ 4.5:1 (normal text)WCAG 2.1 AA §1.4.3Design token review
Colour contrast ≥ 3:1 (large text / UI components)WCAG 2.1 AA §1.4.11Design token review
Accessible name on every interactive controlWCAG 2.1 AA §4.1.2Unit test + axe-core
Correct ARIA roles and statesWCAG 2.1 AA §4.1.2Unit tests
Live region announcements for dynamic changesWCAG 2.1 AA §4.1.3Unit tests
Touch target ≥ 44×44 CSS pxWCAG 2.5.5 (AAA) / WCAG 2.2 §2.5.8coarsePointerMixin
Forced-colors / Windows High ContrastWCAG §1.4.3 enhancedforcedColorsMixin
Reduced-motion fallbackWCAG 2.1 §2.3.3reducedMotionMixin

Testing Strategy

Buildit uses a three-layer testing approach:

1 — Unit tests (jsdom + craftit test helpers)

Every component has tests that verify:

  • Correct role, aria-* attributes at render time
  • Attribute changes reflected in ARIA state (e.g. aria-disabled, aria-busy, aria-expanded)
  • Keyboard event handling (Enter/Space/Arrow/Escape)
  • Focus management for overlays (trap, restore)
  • Slot content announcements via aria-live regions

2 — Axe-core automated audit (per test)

The axeCheck helper is available globally in all Buildit tests:

ts
import { mount } from '@vielzeug/craftit/testing';

it('has no axe violations', async () => {
  const fixture = await mount('bit-button', { attrs: { color: 'primary' } });
  const results = await axeCheck(fixture.element);

  expect(results.violations).toHaveLength(0);
});

Run only the wcag2a, wcag2aa, and best-practice rule sets to keep CI fast and focused.

3 — Manual audit checklist (per release)

Before each minor release, run the checklist below manually or with a screen reader (NVDA + Chrome, VoiceOver + Safari):

  • [ ] Focus order is logical and follows visual layout
  • [ ] All interactive controls have visible focus rings
  • [ ] No content is keyboard-trapped unless intentional (dialogs, drawers)
  • [ ] Dynamic changes (toasts, alerts, async states) are announced
  • [ ] Images and icons have appropriate alt or aria-hidden
  • [ ] Form labels are connected to their controls
  • [ ] Error states are communicated in text (not colour alone)

Per-Component A11y Coverage

Inputs

ComponentroleKeyboardARIA attrsaxeNotes
bit-buttonbutton (implicit)✅ Enter/Spacearia-disabled, aria-busyIcon-only requires aria-label
bit-inputtextbox (native)aria-describedby, aria-invalid, aria-required
bit-textareatextbox (native)aria-describedby, aria-invalid, aria-required
bit-selectcombobox✅ Arrow/Enter/Escapearia-expanded, aria-activedescendant
bit-comboboxcombobox✅ Arrow/Enter/Escapearia-expanded, aria-activedescendant
bit-checkboxcheckbox (native)✅ Spacearia-checked, aria-required
bit-radioradio (native)✅ Arrow keysaria-checkedGroup nav
bit-switchswitch✅ Spacearia-checked
bit-sliderslider✅ Arrow keysaria-valuenow, aria-valuemin, aria-valuemax, aria-valuetext
bit-ratingradiogroup + radio✅ Arrow keysaria-label per star
bit-number-inputspinbutton✅ Arrow keysaria-valuenow, aria-valuemin, aria-valuemax
bit-otp-inputgroup of spinbutton✅ Arrow/Tabaria-label per cell
bit-file-inputbutton-triggered nativearia-label

Feedback

ComponentroleKeyboardARIA attrsaxeNotes
bit-alertalert / status✅ (dismiss)aria-live via region
bit-toastalert / status✅ (dismiss)aria-live="polite" or "assertive"
bit-progressprogressbararia-valuenow, aria-valuemin, aria-valuemax, aria-valuetext
bit-skeletonnonearia-hidden="true" on bonesDecorative
bit-badgenonearia-label if meaningful
bit-chipbutton (when interactive)✅ Enteraria-pressed, aria-label
bit-asyncdynamicaria-busy, aria-live, role="alert" in errorNew

Disclosure

ComponentroleKeyboardARIA attrsaxeNotes
bit-accordionbutton + region✅ Enter/Spacearia-expanded, aria-controls
bit-tabstablist + tab + tabpanel✅ Arrow keysaria-selected, aria-controls, aria-labelledby

Overlay

ComponentroleKeyboardARIA attrsaxeNotes
bit-dialogdialog✅ Escape, focus traparia-modal, aria-labelledbyFocus restored on close
bit-drawerdialog✅ Escape, focus traparia-modal, aria-labelledbyFocus restored on close
bit-popoverdialog / menu✅ Escapearia-expanded, aria-haspopup
bit-menumenu + menuitem✅ Arrow/Enter/Escapearia-orientation
bit-tooltiptooltip✅ Escapearia-describedby on trigger

Content

ComponentroleKeyboardARIA attrsaxeNotes
bit-cardbutton (interactive mode)✅ Enter/Spacearia-disabled, aria-busy
bit-tabletable (native)aria-label, aria-busy
bit-paginationnavigationaria-label, aria-current
bit-breadcrumbnavigationaria-label, aria-current
bit-avatarnonealt or aria-label

Layout

ComponentNotes
bit-sidebarNavigation landmark; aria-label required
bit-gridLayout only; no interactive semantics
bit-boxLayout only; no interactive semantics

Known Gaps & Planned Work

AreaGapPlanned
Visual regressionNo snapshot CI yetPlaywright visual diff in vNext
NVDA + JAWS parityNot systematically testedManual audit before 2.0 stable
bit-select virtual listVirtual items need aria-setsize / aria-posinsetTracked

CI Badges

CITests