Skip to content

Accessibility Quality Bar

Sigil 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

Sigil uses a three-layer testing approach:

1 — Unit tests (jsdom + craft 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 Sigil tests:

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

it('has no axe violations', async () => {
  const fixture = await mount('sg-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
sg-buttonbutton (implicit) Enter/Spacearia-disabled, aria-busyIcon-only requires aria-label
sg-inputtextbox (native)aria-describedby, aria-invalid, aria-required
sg-textareatextbox (native)aria-describedby, aria-invalid, aria-required
sg-selectcombobox Arrow/Enter/Escapearia-expanded, aria-activedescendant
sg-comboboxcombobox Arrow/Enter/Escapearia-expanded, aria-activedescendant
sg-checkboxcheckbox (native) Spacearia-checked, aria-required
sg-radioradio (native) Arrow keysaria-checkedGroup nav
sg-switchswitch Spacearia-checked
sg-sliderslider Arrow keysaria-valuenow, aria-valuemin, aria-valuemax, aria-valuetext
sg-ratingradiogroup + radio Arrow keysaria-label per star
sg-number-inputspinbutton Arrow keysaria-valuenow, aria-valuemin, aria-valuemax
sg-otp-inputgroup of spinbutton Arrow/Tabaria-label per cell
sg-file-inputbutton-triggered nativearia-label

Feedback

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

Disclosure

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

Overlay

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

Content

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

Layout

ComponentNotes
sg-sidebarNavigation landmark; aria-label required
sg-gridLayout only; no interactive semantics
sg-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
sg-select virtual listVirtual items need aria-setsize / aria-posinsetTracked

CI Badges

CITests