KKUONIDesign System

Components

Checkbox

Beta

Checkboxes let users select one or more options from a set, or toggle a single boolean setting on or off.

Purpose

Checkboxes are for binary selection — something is either included or it is not. In a travel booking context they appear wherever a guest must opt in to optional extras (travel insurance, room upgrades, airport transfers) or confirm a set of conditions (terms, consent, notification preferences).

Usage

Use a checkbox group when the user may select any number of items from a fixed set. Use a single standalone checkbox for a boolean toggle that is not time-sensitive — for example confirming agreement to terms. For a toggle that immediately performs an action without a form submit, prefer the Toggle component instead. Do not use checkboxes for mutually exclusive choices — when only one option may be active at a time, use RadioButton.

Checkboxes typically sit within a form step of the booking funnel (extras, consent), grouped under a <fieldset> with a shared legend.

Anatomy

Return transfers from your local airport.

  1. 1
    Control box16×16 px square with rounded-sm corners (4 px). Unchecked: white background, grey-300 border. Checked: teal background and border with white checkmark SVG overlay.
  2. 2
    LabelSentence-case text to the right of the control. Clicking the label toggles the checkbox.
  3. 3
    Required markerRed asterisk appended to the label when required is true. Marked aria-hidden; a visually-hidden "(required)" text is provided for screen readers.
  4. 4
    DescriptionOptional secondary text beneath the label. Gives context without cluttering the label itself.
  5. 5
    Help textGuidance below the control pair. Hidden when an error is present.
  6. 6
    Error messageValidation text with warning icon, shown when the error prop is set. Turns the control border red.
  7. 7
    Focus ring2 px teal ring with 2 px offset, visible on keyboard focus only (focus-visible). Meets WCAG 2.4.7.

Variants

Unchecked

The default resting state. A grey-300 border on a white background signals that the option is available but not selected.


Checked

Teal background and border with a white checkmark. The selected state is visually distinct at a glance and never relies on colour alone — the checkmark icon provides a second signal.


Indeterminate

Shown when a parent checkbox controls a group of children and only some are checked. The white dash on a teal background is the conventional "mixed" signal. Set the indeterminate prop — it is applied via a ref on the underlying input element because the HTML indeterminate property is not an attribute.

When to use: Parent checkboxes in a hierarchical selection list. For example: "Select all destinations" controls a group of regional destination checkboxes where a subset is already chosen.

Do

Use the indeterminate state on parent checkboxes when a sub-group is partially selected.

Indeterminate communicates partial selection at a glance before expanding the group.

Don't

Leave a parent checkbox in checked or unchecked state when some but not all children are selected.

Showing a fully unchecked or fully checked parent when the state is actually mixed misleads the user.


With description

A description prop adds secondary text directly beneath the label. Use it to clarify what the option includes without extending the label itself.


Checkbox group

Stack multiple checkboxes with consistent gap-3 spacing to form a selection group. Wrap the group in a <fieldset> with a <legend> to give assistive technology the shared context.

Holiday extras

States

Focus

The focus ring appears only on keyboard focus (:focus-visible). It is a 2 px teal ring with 2 px offset around the control box. Mouse clicks do not show the ring.

Disabled

The entire control — box, label, and description — is rendered at reduced opacity. The cursor changes to not-allowed. The input cannot be toggled and is removed from the tab order.


Error

An error message is shown below the control when validation fails. The control border turns red. Use this when a required checkbox has not been checked before form submission.


With help text

Help text appears below the control and provides context or guidance. It is hidden when an error message is present.

We will email you personalised deals based on your destinations.

Best practices

Do

Wrap checkbox groups in a fieldset with a descriptive legend so screen readers can announce the group context.

A fieldset with legend gives the group a shared accessible name.

Don't

Render a list of checkboxes with no wrapping fieldset or group label.

Without a group label, each checkbox is announced in isolation with no shared context.

Do

Write labels as short, specific noun phrases: "Travel insurance", "Airport transfers", "Room upgrade".

Clear, specific labels like these make the choice unambiguous before the tap.

Don't

Use generic labels like "Option 1" or "Extra" that do not name the specific choice.

Vague labels force the user to seek context elsewhere before deciding.

Do

Write error messages that explain what is required: "You must accept the terms to complete your booking."

Error messages guide the user to the exact action needed to proceed.

Don't

Use a generic error like "Required" without explaining what the user needs to do.

'Required' alone does not tell the user what to do or why.

Props

PropTypeDefaultDescription
label*stringVisible label text rendered beside the control. Clicking the label toggles the checkbox.
idstringHTML id for the input element. Auto-generated when omitted. Used to associate the label via htmlFor.
descriptionstringSecondary text rendered directly below the label. Use for clarifying copy that does not belong in the label.
checkedbooleanControlled checked state. When provided, the component is fully controlled and requires an onChange handler.
defaultCheckedbooleanfalseUncontrolled initial checked state. Use when you do not need to track the value in React state.
indeterminatebooleanfalsePuts the control into the mixed/partial-check state. Applied via the indeterminate DOM property on the input. Use for parent checkboxes in hierarchical groups.
disabledbooleanfalseDisables interaction. Applies reduced opacity and not-allowed cursor. Removes the input from the tab order.
requiredbooleanfalseMarks the field as required. Adds a red asterisk after the label (visual) and a visually-hidden "(required)" text for screen readers.
errorstringValidation error message. Turns the control border red and renders the message below with a warning icon.
helpTextstringGuidance text shown below the control. Hidden when error is set.
onChange(event: ChangeEvent) => voidChange handler. Required when using controlled mode (checked prop). Receives the native change event.
classNamestringAdditional Tailwind classes merged onto the root wrapper element.

Accessibility

Keyboard interaction

KeyAction
TabMove focus to the next interactive element
Shift + TabMove focus to the previous interactive element
SpaceToggle the checkbox on or off

Label association

The label prop is connected to the input via htmlFor / id. Clicking anywhere on the label text or description toggles the checkbox — important for touch targets and motor accessibility.

Groups with fieldset and legend

When rendering multiple checkboxes together, always wrap them in a <fieldset> and provide a <legend>. Screen readers announce the legend text before each checkbox label, giving users the shared group context without it being embedded in every label individually.

<fieldset className="border-0 p-0 m-0">
  <legend className="text-sm font-semibold text-kuoni-dark mb-3">Holiday extras</legend>
  <div className="flex flex-col gap-3">
    <Checkbox label="Travel insurance" />
    <Checkbox label="Airport transfers" />
  </div>
</fieldset>

Indeterminate state

The indeterminate DOM property is not reflected as an HTML attribute, so it cannot be set in JSX. The Checkbox component handles this internally via a useEffect and a ref. Screen readers announce indeterminate checkboxes as "mixed" — the ARIA-defined value for partially-selected state.

Error announcement

When error is set:

  • aria-invalid="true" is applied to the input element.
  • aria-describedby points to the error paragraph.
  • The error paragraph has role="alert" so assistive technology announces the message immediately when it appears.

Colour and contrast

The checked state uses a teal background (#005B55) with a white checkmark. Contrast between the icon and background is above 7:1. The error border colour (#C0392B) is accompanied by both a warning icon and error text — never colour alone.

ElementForegroundBackgroundRatioWCAG
Checkmark iconWhite #FFFFFFTeal #005B557.0:1AAA
Label text#1A1A1AWhite #FFFFFF17.4:1AAA
Error text#C0392BWhite #FFFFFF5.4:1AA
  • RadioButton — use instead when only one option in a set may be selected.
  • Toggle — use instead for a boolean setting that applies immediately without a form submit.