Pattern Accessibility Contracts
Each pattern in Airframe has an accessibility contract - what we guarantee when you use it as documented.
Buttons
Section titled “Buttons”Element: <button class="af-btn"> or <a class="af-btn">
- Native
<button>elements, not divs with roles - Proper
typeattributes (button,submit,reset) - Disabled state via
disabledattribute on<button>,aria-disabled="true"on<a> - Loading state keeps accessible name, uses
aria-busy="true" - Visible focus via
:focus-visible
Inputs
Section titled “Inputs”Element: Native <input>, <select>, <textarea> with .af-input
- Labels associated via
<label for>or wrapping - Invalid state:
aria-invalid="true" - Help/error text linked via
aria-describedby - Disabled uses native
disabled - Focus ring visible
Checkbox & Radio
Section titled “Checkbox & Radio”Element: Native <input type="checkbox">, <input type="radio">
- Native inputs styled, never replaced with divs
- Label association required
- Grouping uses
<fieldset>and<legend>
Switch
Section titled “Switch”Element: <input type="checkbox" role="switch">
- Checkbox semantics with
role="switch" aria-checkedstateSpacetoggles
Element: <form> with .af-form-* layout classes
<label>elements withforattributes or wrapping- Error messages with
aria-describedbyandaria-invalid="true" - Required fields with
aria-required="true" - Help text linked via
aria-describedby - Field grouping with
<fieldset>and<legend> - Required indicators are never color-only
Navigation
Section titled “Navigation”Element: <nav class="af-navbar">
- Semantic
<nav>element - Active page marked with
aria-current="page" - Breadcrumbs use proper
<nav>+<ol>structure - Skip links via
af-sr-only-focusable
Dialogs
Section titled “Dialogs”Element: <dialog class="af-dialog">
- Native
<dialog>element opened withshowModal() - Accessible name via
aria-labeloraria-labelledby - Page content marked inert when open (native behavior)
Escapecloses- Backdrop click closes
- Focus trap and focus restoration require supplemental JS for full cross-browser reliability
Drawers
Section titled “Drawers”Element: <dialog class="af-drawer">
- Same contract as dialogs (treated as modal)
aria-modal="true"with dialog role- Body scroll lock when open
Escapecloses, backdrop click closes- Focus trap and focus restoration require supplemental JS (same as dialog)
Tables
Section titled “Tables”Element: <table class="af-table">
- Semantic
<table>,<thead>,<tbody>structure <caption>for table descriptions<th>withscope="col"orscope="row"- No “div table” patterns
Alerts & Callouts
Section titled “Alerts & Callouts”Element: .af-callout
- Status messages:
role="status"(polite) - Errors/urgent:
role="alert"(assertive) - Decorative icons use
aria-hidden="true"
Accordion / Disclosure
Section titled “Accordion / Disclosure”Element: <details> / <summary>
- Native
<details>and<summary>elements <summary>is keyboard focusable (native)- Focus styles on
<summary> - Disclosure marker styled, not removed
Avatar
Section titled “Avatar”Element: .af-avatar
- Informative avatars:
<img alt="Name"> - Decorative avatars:
alt=""oraria-hidden="true" - No essential info in background images
Skeleton & Spinner
Section titled “Skeleton & Spinner”Element: .af-skeleton, .af-spinner
- Skeleton containers:
aria-busy="true"while loading - Decorative spinners:
aria-hidden="true" - Standalone spinners: provide “Loading” text via
aria-labeloraf-sr-only
Keyboard Contracts
Section titled “Keyboard Contracts”| Pattern | Keys |
|---|---|
| Buttons | Enter, Space activate |
| Dialogs | Escape closes, Tab cycles within (supplemental JS recommended) |
| Drawers | Escape closes, Tab cycles within (supplemental JS recommended) |
| Forms | Standard form navigation |
| Accordion | Enter, Space toggle on <summary> |
ARIA Quick Reference
Section titled “ARIA Quick Reference”| Pattern | Required attributes |
|---|---|
| Dialogs | aria-modal, aria-labelledby or aria-label |
| Navigation | aria-current="page" on active link |
| Forms | aria-invalid, aria-describedby, aria-required |
| Alerts | role="status" or role="alert" |
| Loading | aria-busy="true" on container |
| Switch | role="switch", aria-checked |