Foundations
Accessibility
System-wide accessibility principles — contrast, focus, keyboard navigation, screen reader semantics and reduced motion — that apply across every Kuoni component.
Accessibility is not a checklist completed at the end of a project. It is a
set of decisions made at every level of the system — in the colour tokens
chosen, in the focus ring applied to every interactive control, in the role
attribute written into the first version of a component. These decisions,
made once at the system level, propagate to every product that uses the system.
The Kuoni design system targets WCAG 2.1 Level AA as its minimum conformance level. Several key pairings and patterns exceed AA and reach AAA. Specific per-component accessibility guidance appears in each component page; this page documents the system-wide principles that underpin all of it.
Colour contrast
All text, icons and interactive elements must meet WCAG 2.1 minimum contrast ratios. The values below are the enforced minimums — not aspirational targets.
| Content type | Minimum ratio | WCAG criterion |
|---|---|---|
| Normal text (< 18 px regular, < 14 px bold) | 4.5 : 1 | 1.4.3 AA |
| Large text (≥ 18 px regular or ≥ 14 px bold) | 3 : 1 | 1.4.3 AA |
| UI components and graphical objects | 3 : 1 | 1.4.11 AA |
| Focus ring against adjacent colour | 3 : 1 | 1.4.11 AA |
See the Colour page for precise ratios for every token pairing in the Kuoni palette. Key callouts:
- Mid Grey (
#666666) on White is the minimum for secondary body text at 4.54 : 1. Do not use lighter greys for readable copy. - Wow Yellow on White fails all levels (1.63 : 1). Never use Wow Yellow as a text or icon colour on white surfaces.
- Warning (
#B7791F) on White achieves only 3.64 : 1 — sufficient for large text only. Use Dark text on the Warning background tint for body-size warning messages.
Always pair Wow Yellow buttons with text-kuoni-dark (#1A1A1A) labels.
Dark text on Wow Yellow achieves 10.67:1 — AAA for all sizes.
Use Wow Yellow as a text or icon colour on any light background.
Wow Yellow text on white is invisible at 1.63:1.
Focus indicators
Every interactive element must have a visible focus indicator when accessed
via keyboard. The system uses a consistent ring pattern applied by Tailwind's
focus-visible: variant — triggered only by keyboard navigation, not mouse
clicks, to avoid cluttering the pointer interface.
Standard focus ring
<button
className="rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kuoni-yellow focus-visible:ring-offset-2"
>
Enquire now
</button>
The ring is:
- 2 px width —
ring-2/border.width.focus - Wow Yellow colour —
ring-kuoni-yellow/border.color.focusDs - 2 px offset —
ring-offset-2, so the ring sits just outside the element boundary
The 2 px yellow ring achieves a minimum 3 : 1 contrast against the white
page background, meeting 1.4.11. Against the active bg-kuoni-yellow100
sidebar item, use ring-kuoni-dark as an alternative.
Focus ring rules
- Never remove focus rings with
outline-nonealone. Always pair it with a visible replacement (ring-*). - Use
focus-visible:notfocus:— this suppresses the ring for mouse/touch while keeping it for keyboard. - All form controls (Input, Select, Textarea, Checkbox, RadioButton, Toggle, RangeSlider, DatePicker) apply the standard ring.
- Modal and Drawer must trap focus — tabbing cannot exit the overlay while it is open.
Apply focus-visible:ring-2 focus-visible:ring-kuoni-yellow focus-visible:ring-offset-2 to every interactive element.
focus-visible keeps keyboard users oriented without distracting mouse users.
Write className="outline-none" on a button, link or form control without a visible replacement.
Removing focus rings makes the interface unusable for keyboard-only users.
Keyboard navigation
Every interaction available by mouse must also be available by keyboard. The expected keyboard behaviours for each component type are documented below.
Universal expectations
| Key | Expected behaviour |
|---|---|
Tab | Move focus to the next interactive element |
Shift + Tab | Move focus to the previous interactive element |
Enter | Activate a focused button or link |
Space | Toggle a focused checkbox, radio, or toggle; activate a button |
Escape | Close an open Modal, Drawer, Dropdown, Popover or Megamenu |
Component-specific patterns
| Component | Additional keyboard behaviour |
|---|---|
Select, SortDropdown | ArrowUp / ArrowDown navigate options; Enter selects |
Checkbox, RadioGroup | Arrow keys move between options within a group |
Modal, Drawer | Focus is trapped inside; Escape closes and returns focus to trigger |
DatePicker | Arrow keys navigate calendar days; Enter selects a date |
RangeSlider | ArrowLeft / ArrowRight adjust value by step |
Carousel | ArrowLeft / ArrowRight navigate slides; focus follows the active slide |
Tabs | ArrowLeft / ArrowRight move between tabs (roving tabindex) |
Screen reader semantics
Roles and labels
Use semantic HTML first. When a native element covers the use case, prefer it
over a div with a role attribute — a <button> is a button, a <nav> is
navigation.
Where custom interactive patterns require explicit ARIA:
- Icon-only buttons:
aria-label="Search"(or equivalent) on the<button> - Decorative icons:
aria-hidden="true"on the icon element - Live regions (search results, filter counts):
aria-live="polite"on the container that updates - Expanded state:
aria-expanded={open}on the trigger for dropdowns, accordions, the Sidebar - Current page:
aria-current="page"on the active sidebar link - Dialog:
role="dialog"+aria-modal="true"+aria-labelledbyon Modal
Labelling images
Every <img> element must have an alt attribute. For decorative images
(destination photography that adds mood but carries no information), use
alt="" to mark it as decorative. For meaningful images (a map, an award
badge), write a description of what the image communicates — not what it
depicts.
Reduced motion
Users who experience discomfort from animation can set prefers-reduced-motion: reduce in their operating system. The Kuoni system honours this preference
globally through a rule in globals.css that compresses all transitions and
animations to near-zero duration.
Do not write animations that work around this preference. See the Motion page for details on which components animate and how they respond to the preference.
Text and content
- Minimum font size: 12 px (
font.size.xs) for captions and metadata. Do not render any text smaller than 12 px. - Line length: prose content is capped at 860 px (
layout.contentMaxWidth) to keep line lengths in the 60–80 character range for readability. - Line height: body copy uses
leading-7(1.75 rem). Headings useleading-tight(1.2) — only acceptable at large sizes where tight leading aids visual grouping. - Don't rely on colour alone: every semantic state (error, success, warning) combines colour with a text label, icon or pattern.
Testing accessibility
No automated tool catches all accessibility issues. The recommended test stack:
- Keyboard-only navigation — unplug your mouse and tab through every interactive element. Confirm focus order is logical and every state is reachable.
- Screen reader — VoiceOver (macOS/iOS), NVDA (Windows), or TalkBack (Android). Navigate by headings, then by form controls, then linearly.
- Automated scan — axe DevTools or Lighthouse. These catch ~30–40% of issues; manual testing is irreplaceable.
- Zoom to 200% — confirm no content is clipped, overlapping or lost at double zoom.
- High-contrast mode — Windows High Contrast or macOS Increase Contrast. Confirm borders and focus rings remain visible.