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
ButtonNative <button>, accessible name, visible focus, correct disabled / loading behaviour
Icon buttonaria-label, decorative icon with aria-hidden="true"
Input<label for="">, aria-invalid, aria-describedby for help or error text
Modal / Dialogrole="dialog", aria-modal="true", labelled title, focus trap, Escape close, restore focus on close
Dropdown / MenuTrigger with aria-expanded, keyboard navigation, Escape close, focus return
Tabsrole="tablist", arrow-key navigation, selected state, linked tab panels
Toast / AlertAppropriate live-region behaviour for important status messages
TableSemantic 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 outline without a focus-visible replacement
  • 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

• Complete one full workflow with keyboard only. If you cannot reach an action, that is an accessibility bug. • Use VoiceOver (macOS) or NVDA (Windows) for screen-reader smoke testing — test forms, modals, and dynamic content. • Run Lighthouse or axe for automated checks, but verify manually — automated tools catch missing ARIA but miss broken focus order or unexpected behaviour. • Do not skip heading levels. Go from 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.

Unlock Premium Patterns

Get access to copy-paste ready Pro blocks, SaaS templates, and advanced component patterns.

Explore Pro Components