Skip to main content

Accessibility

Accessibility isn't a separate checklist — it's part of how every Senco component is built. All components target WCAG 2.1 Level AA as a minimum.

Target standard

WCAG 2.1 Level AA compliance for all components, patterns, and page templates. Level AAA where practical (e.g., enhanced contrast for financial data).

Color and contrast

RequirementRatioApplies to
Normal text4.5:1Body text, labels, descriptions
Large text (18px+ bold or 24px+)3:1Headings, large labels
UI components3:1Borders, icons, focus rings, form controls
Non-text contrast3:1Charts, graphs, status indicators
  • Never use color alone to convey meaning — always pair with text, icons, or patterns
  • Status badges use color + dot indicator + text label
  • Error states use red border + error icon + descriptive text

Contrast verification

All token pairings have been verified for WCAG AA compliance in both light and dark mode. See the full contrast tables and key decisions in Color > Accessibility.

Do / Don't
Do
You send
$0.00
CA flagCAD

Visible label above the input stays present when the user types.

Don't
$0.00
US flagUSD

Placeholder-only label disappears on focus, leaving no context.

All text and UI element pairings must meet minimum contrast ratios.

ElementMinimum ratioStandard
Normal text (<18px)4.5:1WCAG 1.4.3 AA
Large text (18px+ bold, 24px+)3:1WCAG 1.4.3 AA
UI components & icons3:1WCAG 1.4.11
Focus indicators3:1WCAG 2.4.7
Aa
Heading on white
17.74:1
PASS
Aa
Body on white
10.31:1
PASS
Aa
White on primary
5.1:1
PASS
Aa
Primary on white
5.1:1
PASS
Aa
White on destructive
5.01:1
PASS
Aa
Muted on white
5.54:1
PASS
Aa
Muted on surface
5.04:1
PASS
Aa
Placeholder text
4.5:1
PASS

Keyboard navigation

All interactive components must be fully operable with keyboard only.

KeyAction
TabMove focus to next interactive element
Shift+TabMove focus to previous element
Enter / SpaceActivate buttons, links, toggles
Arrow keysNavigate within menus, tabs, radio groups
EscapeClose dialogs, tooltips, popovers, dropdowns
Home / EndJump to first/last item in a list

Focus management

  • Focus ring: 2px solid --ds-primary with 2px offset
  • Focus ring must meet 3:1 contrast against adjacent colors
  • Focus order follows visual reading order (left-to-right, top-to-bottom)
  • Never remove :focus-visible outlines without providing an alternative
  • Dialogs trap focus and return it to the trigger on close
  • Visible focus indicator on all interactive elements
  • Focus ring meets 3:1 contrast against adjacent colors
  • Custom focus styles consistent across the system
  • Never remove outline without providing an alternative
DoVisible focus ring

3:1 contrast, 3px ring, visible on the focused element

Don'tNo focus indicator

Keyboard users cannot see where they are

  • All interactive elements reachable via Tab
  • Logical focus order matching visual order
  • Escape key closes modals and overlays
  • Arrow keys navigate within composite widgets
  • Enter/Space activates buttons and controls

Tab index

Use tabIndex to control keyboard focus order.

ValueBehaviorWhen to use
tabIndex="0"Follows natural DOM orderCustom interactive elements (div with onClick)
tabIndex="-1"Focusable via JS, skipped by TabOff-screen elements, programmatic focus (modals, error summaries)
tabIndex="1+"Forces priority orderAvoid — breaks natural flow, confuses users
1
Devon
Tab ↓
2
Kim
Tab ↓
3
88 Queens Quay W
Tab ↓
4
Tab NextShift+Tab PreviousEnter ActivateEsc Close

Semantic HTML

  • Use native HTML elements whenever possible (<button>, <input>, <nav>, <main>)
  • Avoid <div> and <span> for interactive elements
  • Use heading hierarchy sequentially (h1 → h2 → h3, no skipping)
  • Use landmark elements: <header>, <nav>, <main>, <footer>, <aside>
  • Lists use <ul>/<ol>, not styled divs

ARIA patterns

Use ARIA only when native HTML semantics are insufficient.

PatternWhen to use
aria-labelIcon-only buttons, unlabeled controls
aria-describedbyError messages, hint text, supplementary info
aria-liveDynamic content updates (toast, loading status)
aria-current="page"Active navigation item, breadcrumb current page
aria-expandedAccordion triggers, dropdown toggles
aria-invalid="true"Form inputs with validation errors
role="alert"Error summaries, critical notifications
  • Use semantic HTML first, ARIA second
  • Apply ARIA roles and landmarks to define page regions
  • Use aria-label, aria-describedby, and aria-live where needed
  • Never use ARIA to override semantic meaning
<nav>
<header>
<main>
role="status"

Landmarks let assistive technology users jump between page regions.

Screen reader testing

Test all components with at least two screen readers:

PlatformScreen readerBrowser
macOSVoiceOverSafari
WindowsNVDAFirefox or Chrome
MobileTalkBack (Android)Chrome
MobileVoiceOver (iOS)Safari

What to verify

  • All interactive elements have accessible names
  • Form errors are announced when they appear
  • Page title updates on navigation
  • Dynamic content changes are announced via aria-live
  • Images have meaningful alt text (or aria-hidden if decorative)
  • Meaningful alt text for images
  • Hidden descriptive text for icon-only actions
  • Live regions for dynamic content updates
  • Form labels and error messages announced correctly
DoDescriptive labels
Visa ****7788
Expires 11/27
Edit
RBC ****4400
Chequing
Edit
“Visa ending 7788, selected, Expires 11/27”
“Edit, Visa ending 7788, button”
Don'tMissing labels
Visa ****7788
Expires 11/27
Edit
RBC ****4400
Chequing
Edit
“radio button”
“button”

Alt text for images

DoDescribe method + state
USDT ****9c1d
Tether
Edit
BTC ****k8xf
Bitcoin
Edit
alt="USDT wallet ending 9c1d, selected"
Don'tGeneric label
USDT ****9c1d
Tether
Edit
BTC ****k8xf
Bitcoin
Edit
alt="icon"
alt="wallet"
DoFlag + recipient context
South Korea flag
Min-ji Kim
Mar 28 · Sent
-$250.00 CAD
Completed
United Kingdom flag
James Wilson
Mar 25 · Received
+$80.00 CAD
Pending
alt="South Korean flag — Min-ji Kim, sent $250 CAD"
Don'tNo flag context
Min-ji Kim
Mar 28 · Sent
-$250.00 CAD
Completed
James Wilson
Mar 25 · Received
+$80.00 CAD
Pending
alt="flag"
alt=""

Skip to content

A skip link is a hidden anchor that becomes visible on keyboard focus. It lets users bypass repetitive navigation and jump straight to the main content area.

  • The skip link must be the first focusable element on the page
  • It is visually hidden by default and appears on :focus
  • The target must be a landmark with an id (e.g., #ds-main-content)
  • Skip links are required on every page with persistent navigation
Skip to content
Default — hidden
On Tab — visible
Skip to main content
DoFirst focusable element on the page
<body>
  <a href="#main" class="ds-skip-link">
    Skip to main content
  </a>
  <nav>...</nav>
  <main id="main">...</main>
Don'tSkip link buried after navigation
<body>
  <nav>...</nav>
  <a href="#main">
    Skip to main content
  </a>
  <main id="main">...</main>

Motion and animation

  • Respect prefers-reduced-motion media query
  • Disable non-essential animations when reduced motion is preferred
  • Essential animations (loading spinners) remain but simplify

Touch targets

  • Minimum touch target size: 48×48 CSS pixels (exceeds WCAG 2.5.5 AAA)
  • Minimum spacing between targets: 8px
  • The visual element can be smaller — the tappable hit area must be 48×48px

Checklist

Use this checklist for every new component or pattern:

  • Color contrast passes 4.5:1 for text, 3:1 for UI
  • Keyboard operable (Tab, Enter, Escape, Arrow keys)
  • Focus indicator visible and meets contrast requirements
  • Screen reader announces name, role, and state
  • Error states use text + icon, not color alone
  • Touch targets are 48×48px minimum
  • prefers-reduced-motion is respected
  • Semantic HTML used (no div buttons)
  • ARIA attributes correct and minimal
  • Tested with VoiceOver and NVDA