Color Picker

How the Color Picker works with a keyboard and assistive tech, and where it stands against WCAG 2.2 AA.

AAWCAG 2.2 target
10/12contrast pairs passing
8keyboard interactions

The Colour Picker is a group of standard parts: a radio group for the fill type, sliders for the spectrum, hue, opacity and each gradient stop, a native select and text field for the value, and native radios for recent colours. Every colour can be set from the keyboard, and the value field means nobody has to drag to get an exact one.

Keyboard#

Tab

Moves through the panel's parts in reading order. The fill types and recent colours are one tab stop each.

→

In the fill types, chooses the next type and wraps; ↓ does the same. On the spectrum, raises saturation by 1%. On another slider, steps up by 1.

←

The reverse of →: the previous fill type, less saturation, or a step down.

↑

On the spectrum, raises brightness by 1%; ↓ lowers it. On another slider, steps up or down by 1.

Shift+↑

With any arrow, takes a step of 10 on the spectrum and on every slider.

Page Up

On hue, opacity and a stop, steps up by 10. Page Down steps down.

Home

On hue, opacity and a stop, jumps to the start. End jumps to the end.

Enter

In the value field, applies what was typed. Escape discards it.

Semantics#

role="group"

The panel is one group named Color picker.

Always
role="radiogroup"

Named Fill type. Each chip is role="radio" with aria-checked and its own name; only the chosen one is in the tab order.

Fill types
role="slider"

Named Saturation and brightness, with aria-roledescription="2D slider" and an aria-valuetext that gives both values.

Spectrum
aria-valuetext

Each slider reads its value in degrees or percent. A stop is named by its place and colour: Stop 2, #005CF8.

Hue, opacity, stops
<select aria-label>

A real select under the drawn button, so the platform's own picker opens.

Format
<input aria-label>

Named by the format (HEX value), and accepts #RGB, #RRGGBB or rgb() and rgba() text.

Value
<fieldset> + <legend>

The dots are native radios, each named by its colour.

Recently Used
role="tablist"

Custom and Libraries are tabs; ← and → move between them.

Fancy
disabled

The eyedropper is disabled where the browser has no EyeDropper API, with a title that says why.

No EyeDropper

Colour contrast#

Every variant and state has to meet WCAG 2.2 Level AA. Measure the colours as they land on screen: a translucent hover or press wash over whatever sits behind it.

4.5:1

Text and glyphs · Value, format, legend, buttons

The value, format and buttons in #2a2a2a, and the legend and eyedropper in #6d6d6d, against white or the 4% wash. All pass.

WCAG 1.4.3 Contrast (Minimum) ↗WCAG 1.4.11 Non-text Contrast ↗
4.5:1

Unselected Fancy tab · Fancy

The unselected tab is #cbcbcb on white, 1.62:1. It is active text, not disabled text, so it needs 4.5:1 and fails.

WCAG 1.4.3 Contrast (Minimum) ↗
3:1

Thumbs on the colour · Spectrum, sliders

The thumbs carry the picked colour inside a white ring with a soft shadow. On light colours the white ring all but disappears; the shadow is what keeps the thumb findable.

WCAG 1.4.11 Non-text Contrast ↗
3:1

Chosen fill type · Fill types

The chosen chip is lifted white on the #f2f2f2 well, which is 1.12:1. The #2a2a2a edge on its swatch is what reaches 3:1.

WCAG 1.4.11 Non-text Contrast ↗
3:1

Focus indicator · Focus visible

A 2px ring in #1a6ef8 off a white gap, on keyboard focus only. It measures 4.54:1 on white, so it clears 3:1 on every light surface the library uses.

WCAG 1.4.11 Non-text Contrast ↗WCAG 2.4.13 Focus Appearance ↗

Measured contrast#

The panel's own text and glyphs against the white section and the 4% hover wash, from the ds.css tokens; a part with no hover style keeps its rest figure; text is held to 4.5:1 and glyphs to 3:1. 2 of 12 pairs fall below their minimum (4.5:1 for text, 3:1 for graphics) and are marked in amber.

Part · elementRestHover
Format · label14.35passes13.17passes
Value · text on wash13.17passes13.17passes
Stop buttons · glyph14.35passes13.17passes
Eyedropper · glyph5.17passes4.75passes
Recently Used · legend5.17passes5.17passes
Fancy tab · unselected1.62below the minimum1.62below the minimum

Motion#

  • A recent colour dot shrinks over 120ms on hover. It is turned off under prefers-reduced-motion.
  • Dragging moves the thumbs with the pointer and nothing animates on its own.

Checklist#

Every slider can be set from the keyboard, and the value field takes an exact colour, so no part depends on dragging (WCAG 2.5.7).

The spectrum answers the arrow keys and Shift, but not Page Up, Page Down, Home or End as the other sliders do.

A colour is announced as a value, not a name. Where people choose among named colours, use a Radio group of swatches with names instead.

The unselected Fancy tab (#cbcbcb, 1.62:1) needs a darker colour before the code library ships.

Hue, opacity and stop thumbs are 20px, under the 24px minimum. They pass only because the whole track around them is a target.

The Fancy header's 24px reset and close buttons meet 2.5.8 exactly; leave space around them.