Color picker
Picking one colour from a named palette. Two parts: ColorSwatches draws the swatch grid (a radiogroup, optional "Bez barvy" row below it), and ColorPicker is its canonical wrapper — an anchored popover on desktop, a bottom sheet on the phone, a panel inside the dialog's own sheet when opened from a dialog. Built in apps/web/src/components/color/ColorSwatches.tsx and ColorPicker.tsx; CSS .ctype-grid / .ctype-sw / .sw-<colour> in apps/web/src/ds/calendar.css, tones .cs-tones / .segpick-tone in apps/web/src/ds/segment-screen.css, phone layer in apps/web/src/ds/responsive.css.
Status is candidate: it replaced four copies of the same grid (event label, calendar event type, mail label, segment tone) and waits for the AD to promote it. Stories render the canonical markup because the component lives in apps/web.
When to use
- A value that is one of a fixed set of named colours: an event label, the colour of a calendar type, a mail label, a segment tone.
- Where the colour is a property of a record that the user sets once and reads at a glance in lists and calendars.
When not to use
- A free colour (any hex). The picker offers only what the palette defines. The custom-colour input that sits next to the tones in the segment icon picker is a separate native
<input type="color">, not part of this component; whether it stays is an open decision. - A choice between states or categories that happen to have colours (stage, priority, status) — that is a Select, a Segmented control or a Status chip; the colour there is a result, not the thing being chosen.
- Several colours at once — a radiogroup holds one.
Anatomy
- Grid
.ctype-grid:role="radiogroup"with anaria-label, six columns,--sp-2gap,--sp-3/--sp-4padding. Rules are scoped to.acrm-popoverand.acrm-sheet, so the grid is drawn only inside a panel. - Swatch
.ctype-sw.sw-<colour>: a<button role="radio">,--sp-6(24 px) round, no frame. The fill isvar(--gc-<colour>); the app writes the 24 palette values on the document root from data (applyPaletteVars), so there is no colour value in CSS. - Selection: a ring — a gap in
--bg-canvas, then a ring in--ink-900— never a check mark inside the swatch. - "Bez barvy" row (optional,
noneLabel): a regular menu row.acrm-popover-itemwith thexglyph under the grid,aria-pressedwhile nothing is chosen. - Tones variant
.cs-tones(variant="tones"): one row of round tone dots.segpick-tone(.dot, 18 px) with the same radiogroup contract; the selected dot carries the accent ring. It sits in.segpick-tonesnext to the app's own custom-colour label. - Wrapper
ColorPicker:Popover role="dialog"on desktop; on a phoneBottomSheetwith the grab handle, a title (the sameariaLabel) and a full-widthHotovo; inside a dialog the sheet is portalled into the dialog so the modal does not make it inert.
Behaviour
- A pick is the value and closes the panel. One colour is one value, so choosing it calls
onPick(key)andonClose(). "Bez barvy" callsonPick(null). - Focus on open. After the panel opens, focus goes to the checked swatch, or to the first one when nothing is chosen. It is set two frames later, because the first pass of the panel is
visibility: hiddenandfocus()on a hidden element is dropped silently. - Keyboard moves inside the grid. Left and right move by one, up and down by one row (
columns, default 6), Home and End go to the first and last swatch; the move stops at the ends and does not wrap. - Escape closes the panel only. The popover consumes it before the dialog underneath would close.
- On a phone (below 720 px) the six columns are
1frand span the full width of the sheet; the swatch stays 24 px and is centred in its cell, the touch target is 44 px through an invisible::after, and the row pitch is 48 px so two targets never overlap. Desktop is unchanged. - The host owns the anchor and the opening. The trigger is the host's chip or button; the picker only draws the panel.
Best practices
Do
- Name the panel for what is chosen (
Barva štítku,Barva typu): the name is read by the screen reader and is the sheet title on the phone. - Show the chosen colour in the trigger, so the picker is not the only place the value is visible.
- Offer "Bez barvy" only where "no colour" is a valid value of the field.
- Keep the 24 palette keys stable; stored values refer to the key, not to the hex.
Don't
- Mark the selection with a check mark — it does not hold contrast on the light tones.
- Draw the grid outside a panel (
.acrm-popover,.acrm-sheet) — the rules do not apply there. - Open a second popover from the picker, or put the calendar picker's logic in it.
- Add a swatch from a colour that is not in the palette; extend the palette in
@acrm/sharedand every place picks it up.
Content
- Panel names:
Barva štítku,Barva typu,Barva segmentu. - Swatch names are the palette names as Google shows them (Tomato, Flamingo, … Graphite), deliberately not translated, so the user sees the same word as in Google Calendar. Tones are named
Barva <tone>. - No-colour row:
Bez barvy. Phone CTA:Hotovo.
Related
check-menu— a set of options toggled on and off, not one value.picker-menu— one value from a list of rows.popover— the panel shell on desktop.- Stories:
components--color-picker--default,--variants,--states,--composition.
Source: docs/choice/color-picker.usage.md · GitLab