KKUONIDesign System

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 typeMinimum ratioWCAG criterion
Normal text (< 18 px regular, < 14 px bold)4.5 : 11.4.3 AA
Large text (≥ 18 px regular or ≥ 14 px bold)3 : 11.4.3 AA
UI components and graphical objects3 : 11.4.11 AA
Focus ring against adjacent colour3 : 11.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.
Do

Always pair Wow Yellow buttons with text-kuoni-dark (#1A1A1A) labels.

Dark text on Wow Yellow achieves 10.67:1 — AAA for all sizes.

Don't

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 widthring-2 / border.width.focus
  • Wow Yellow colourring-kuoni-yellow / border.color.focusDs
  • 2 px offsetring-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-none alone. Always pair it with a visible replacement (ring-*).
  • Use focus-visible: not focus: — 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.
Do

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.

Don't

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

KeyExpected behaviour
TabMove focus to the next interactive element
Shift + TabMove focus to the previous interactive element
EnterActivate a focused button or link
SpaceToggle a focused checkbox, radio, or toggle; activate a button
EscapeClose an open Modal, Drawer, Dropdown, Popover or Megamenu

Component-specific patterns

ComponentAdditional keyboard behaviour
Select, SortDropdownArrowUp / ArrowDown navigate options; Enter selects
Checkbox, RadioGroupArrow keys move between options within a group
Modal, DrawerFocus is trapped inside; Escape closes and returns focus to trigger
DatePickerArrow keys navigate calendar days; Enter selects a date
RangeSliderArrowLeft / ArrowRight adjust value by step
CarouselArrowLeft / ArrowRight navigate slides; focus follows the active slide
TabsArrowLeft / 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-labelledby on 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 use leading-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:

  1. Keyboard-only navigation — unplug your mouse and tab through every interactive element. Confirm focus order is logical and every state is reachable.
  2. Screen reader — VoiceOver (macOS/iOS), NVDA (Windows), or TalkBack (Android). Navigate by headings, then by form controls, then linearly.
  3. Automated scanaxe DevTools or Lighthouse. These catch ~30–40% of issues; manual testing is irreplaceable.
  4. Zoom to 200% — confirm no content is clipped, overlapping or lost at double zoom.
  5. High-contrast mode — Windows High Contrast or macOS Increase Contrast. Confirm borders and focus rings remain visible.