Accessibility Overview
Why accessibility matters and how Byteion builds it into every component.
Overview
Accessibility is not a feature you add at the end. If a user cannot interact with your UI using a keyboard, screen reader, or assistive technology, the UI is broken. Byteion treats accessibility as part of the component contract — every interactive component is expected to work with semantic HTML, keyboard support, visible focus states, and ARIA only where native semantics are not enough.
Accessibility contract
Every Byteion component should follow these rules:
- Use native semantic HTML wherever possible
- Provide visible keyboard focus
- Support expected keyboard interactions
- Use ARIA only when native semantics are not enough
- Preserve readable contrast in light and dark mode
- Expose state clearly: expanded, selected, disabled, invalid, loading
- Work with screen readers and keyboard-only navigation
Core principles
1. Semantic HTML first
Use <button>, <a>, <input>, <select>, and <textarea> for interactive controls — not <div> or <span>. Native elements bring built-in keyboard support, screen-reader announcements, and form participation.
2. Keyboard-first interaction
Every interactive element must be reachable and operable via keyboard. Tab navigates, Enter and Space activate, Escape dismisses, and arrow keys move between related items. Static components avoid unnecessary ARIA and rely on native semantics.
3. Focus visibility
Never remove outline without providing an alternative. Byteion uses focus-visible:ring-2 focus-visible:ring-primary/20 so keyboard users see focus rings while mouse users do not. Use semantic token classes such as ring-primary/20, text-danger, text-danger-foreground, and border-danger consistently.
4. ARIA only when needed
ARIA enhances semantics; it should not replace native HTML. Do not add ARIA roles to elements that already have correct native semantics. Use aria-label on icon-only buttons, aria-expanded on dropdown triggers, and aria-describedby to link form fields with error messages.
5. Contrast and state clarity
Every foreground-background pair must pass WCAG AA contrast. Do not rely on colour alone to communicate errors or selected states — pair colour with icons, text, or structure. State changes (expanded, selected, disabled, invalid, loading) must be visually and programmatically exposed.
6. Motion should be usable
Animations must be subtle, purposeful, and not distracting. Avoid flashing or rapid motion that can trigger vestibular disorders. Respect the user's prefers-reduced-motion setting where motion-heavy transitions are used.
Component pattern table
| Component | Accessibility expectation |
|---|---|
| Button | Native <button>, accessible name, visible focus, correct disabled / loading behaviour |
| Icon button | aria-label, decorative icon with aria-hidden="true" |
| Input | <label for="">, aria-invalid, aria-describedby for help or error text |
| Modal / Dialog | role="dialog", aria-modal="true", labelled title, focus trap, Escape close, restore focus on close |
| Dropdown / Menu | Trigger with aria-expanded, keyboard navigation, Escape close, focus return |
| Tabs | role="tablist", arrow-key navigation, selected state, linked tab panels |
| Toast / Alert | Appropriate live-region behaviour for important status messages |
| Table | Semantic table structure, column headers, readable empty and loading states |
Practical examples
<!-- Icon-only button -->
<button type="button" aria-label="Delete item"
class="rounded-button bg-danger/10 px-2 py-2 text-danger
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/20">
<i class="ti ti-trash" aria-hidden="true"></i>
</button>
<!-- Modal -->
<div data-bi-modal="confirm" role="dialog" aria-modal="true"
aria-labelledby="modal-title" aria-describedby="modal-desc"
class="rounded-xl bg-card p-6 shadow-overlay border border-border">
<h2 id="modal-title" class="text-lg font-semibold text-foreground">Delete account?</h2>
<p id="modal-desc" class="text-sm text-muted-foreground">
This action cannot be undone.</p>
<div class="mt-4 flex gap-3">
<button data-bi-modal-hide
class="rounded-lg border border-border bg-background px-4 py-2 text-sm">Cancel</button>
<button class="rounded-lg bg-danger px-4 py-2 text-sm font-medium text-danger-foreground
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-danger/20">
Delete</button>
</div>
</div>
<!-- Form field -->
<label for="email" class="text-sm font-medium text-foreground">Email address</label>
<input type="email" id="email" aria-invalid="true"
aria-describedby="email-error"
class="w-full rounded-lg border border-danger bg-background px-3 py-2 text-sm
focus:outline-none focus:ring-2 focus:ring-danger/20">
<p id="email-error" role="alert" class="mt-1 text-sm text-danger">
Please enter a valid email.</p>
For modals specifically, Byteion's JavaScript behaviour handles focus trapping, Escape-to-close, scroll locking, and focus restoration to the trigger button on close.
Keyboard expectations
- Tab / Shift+Tab — moves through focusable controls in document order
- Enter or Space — activates buttons, links, and controls
- Escape — closes modals, drawers, dropdowns, popovers, and menus
- Arrow keys — navigate through tabs, menus, radio groups, and listbox-like controls
- Focus trap — focus cycles within an open overlay and never escapes behind it
- Focus restoration — after closing an overlay, focus returns to the trigger element
Testing workflow
Automated tools are a safety net, not a replacement for keyboard and screen-reader testing.
- Test every interactive workflow with keyboard only — Tab, Enter, Escape, Space, Arrow keys
- Test with at least one screen reader — VoiceOver (macOS) or NVDA (Windows)
- Run Lighthouse or axe DevTools for automated checks
- Test colour contrast in both light and dark mode
- Test forms, modals, dropdowns, tabs, tables, sidebars, and toasts specifically
- Test loading, disabled, invalid, selected, expanded, and destructive states
Do and Don't
Do
- Use native elements first —
button,a,nav,main - Give every control an accessible name
- Keep visible focus states with
focus-visible:ring-2 - Connect labels, descriptions, and errors to fields
- Trap and restore focus for overlays
- Test with keyboard and screen reader
Don't
- Use
<div onClick>for buttons - Remove
outlinewithout afocus-visiblereplacement - Add ARIA roles to elements that already have correct native semantics
- Leave icon-only buttons unlabeled
- Use colour alone to communicate errors or selected states
- Open overlays without focus management
Developer tips
h2 to h3, not h2 to h5.
• Test both light and dark mode — contrast that passes on white may fail on a dark surface.
• Check reduced-motion behaviour if motion-heavy components or transitions are used.Related
- Keyboard Navigation — Component keyboard behaviour
- Focus Management — Focus trap and restore rules
- Dark Mode — Contrast in light and dark themes
- ARIA Patterns — When and how to use ARIA roles, states, and properties
- Screen Reader — Labels, live regions, and announcement patterns
Unlock Premium Patterns
Get access to copy-paste ready Pro blocks, SaaS templates, and advanced component patterns.
Explore Pro Components