Skip to Content
FoundationsAccessibility

Accessibility

Accessibility is not optional in the Mukoko ecosystem. Every component in this registry is built to meet strict accessibility standards, ensuring usability for all people across Africa’s diverse digital landscape.

Contrast requirements

The Mukoko design system uses APCA 3.0 AAA (Advanced Perceptual Contrast Algorithm) rather than the older WCAG 2.x contrast ratio. APCA is more perceptually accurate, particularly for the warm cream and deep night backgrounds used in our palette.

Minimum contrast values

Text typeMinimum APCA LcExample
Body text (16px)Lc 75--foreground on --background
Large text (24px+)Lc 60Headings on cards
UI controlsLc 60Button labels, form inputs
Placeholder textLc 45Input placeholders

Testing contrast

Every pairing of foreground and background tokens has been validated. When adding new tokens:

  1. Test in both light and dark themes
  2. Use the APCA contrast calculator  (not the WCAG 2.x ratio checker)
  3. Verify with --muted-foreground on --muted, --foreground on --card, etc.

Touch targets

All interactive elements must meet a 48px minimum touch target size. This is critical for mobile users, who make up the majority of Mukoko’s audience.

// Correct — 48px touch target via padding <Button size="default" className="h-10 px-4"> Submit </Button> // Also correct — icon button with sufficient size <Button size="icon" className="size-10"> <Menu className="size-5" /> </Button>

For smaller visual elements (like checkbox indicators), ensure the clickable area extends to 48px via padding:

<label className="flex min-h-[48px] items-center gap-3 px-3"> <Checkbox /> <span>Accept terms</span> </label>

Keyboard navigation

All Mukoko components use Radix UI primitives for accessible keyboard interaction. Radix handles:

  • Focus management — focus trapping in modals, focus restoration on close
  • Arrow key navigation — within menus, tabs, radio groups, comboboxes
  • Escape to close — dialogs, dropdowns, popovers, sheets
  • Enter/Space to activate — buttons, checkboxes, toggles
  • Home/End — jump to first/last item in lists

Focus indicators

All focusable elements display a visible focus ring using the Tailwind focus-visible variant:

focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring

Never remove focus indicators. They are essential for keyboard users.

Screen reader support

Semantic HTML

Use the correct HTML element for its purpose:

  • <button> for actions, not <div onClick>
  • <a> for navigation
  • <nav> for navigation landmarks
  • <main> for primary content
  • <h1> through <h6> in order, no skipped levels

ARIA attributes

Radix UI components include ARIA attributes automatically. For custom components:

// Describe the purpose of icon-only buttons <Button aria-label="Close dialog" size="icon"> <X className="size-4" /> </Button> // Link related elements <Label htmlFor="email-input">Email</Label> <Input id="email-input" type="email" /> // Announce dynamic content <div aria-live="polite" aria-atomic="true"> {statusMessage} </div>

Testing screen readers

Test with at least one screen reader:

  • macOS: VoiceOver (built in, Cmd+F5)
  • Windows: NVDA (free)
  • Mobile: TalkBack (Android), VoiceOver (iOS)

Color independence

Never use color as the sole indicator of meaning. Always pair color with:

  • Text labels — “Error: Invalid email” not just a red border
  • Icons — checkmark for success, X for error
  • Patterns — striped or dotted backgrounds for emphasis

The Five African Minerals palette is designed to be distinguishable to people with common color vision deficiencies. The mineral accents were chosen with this constraint in mind.

Checklist

When building or reviewing components, verify:

  • Contrast meets APCA 3.0 AAA in both light and dark themes
  • Touch targets are at least 48px
  • Keyboard navigation works for all interactions
  • Focus indicators are visible
  • Screen reader announces the component’s purpose and state
  • Color is not the sole indicator of meaning
  • Heading levels are sequential (no skipping)
Last updated on