Accent picker
A theme colour picker for React in a small button: the accent in use as a swatch, opening on twelve presets and a colour of your own, kept legible and remembered across visits.
Install
# once per project: where @zzzui items come frompnpm dlx shadcn@latest registry add @zzzui=https://zzzui.catforest.dev/r/{name}.jsonpnpm dlx shadcn@latest add @zzzui/accent-pickerExamples
Recolouring one part of a page
import { useRef } from "react";import { AccentPicker } from "@/components/zero/accent-picker";import { Badge } from "@/components/zero/badge";import { Button } from "@/components/zero/button";import { Progress } from "@/components/zero/progress";import { Switch } from "@/components/zero/switch";export default function AccentPickerPreview() { // this demo recolours only the box below; without `target` it recolours the page const box = useRef<HTMLDivElement>(null); return ( <div className="grid w-full max-w-md gap-4"> <div className="flex items-center gap-3"> <AccentPicker target={() => box.current} storageKey={null} align="start" aria-label="Accent of the preview" /> <span className="font-zero-text text-zero-sm text-zero-text-2"> Twelve presets, or a colour of your own </span> </div> <div ref={box} data-zero-accent="lime" className="grid gap-5 border-(length:--zero-stroke) border-zero-ink bg-zero-surface p-5" > <div className="flex flex-wrap items-center gap-3"> <Button>Take the job</Button> <Badge>Rush</Badge> <Switch defaultChecked aria-label="Night routes" /> </div> <Progress value={62} aria-label="Route done" /> </div> </div> );}Behaviour
The button shows the accent in use as a slanted swatch and opens a popover: the presets as a grid of swatches (a radio group, so the arrow keys move between them) and, under them, a colour of your own, from the colour well or typed as hex. The site's header has one.
A colour of your own is applied as you pick it, but only if ink text reads on it at 7 : 1 or more, the floor every preset meets: buttons, badges and the active tab all set ink on the accent. A darker colour is refused, and the note under the field says so with its ratio; the note is a polite live region, so it is heard too. Hover and pressed shades are mixed from the colour by the foundation. It is written as data-zero-accent="custom" with the colour inline, and remembered like a preset.
It keeps and writes the accent exactly as the Accent switch does, with the same target, storageKey and controlled value, so the two can share a page, and AccentScript in <head> puts a remembered colour on the page before it paints. Use the switch where there is room for a row of swatches and no need for a colour of your own.
Props
AccentPicker
| Prop | Type | Default | Description |
|---|---|---|---|
| value / defaultValue / onValueChange | AccentPreset | "#rrggbb" | "lime" | The accent, controlled or not. |
| accents | AccentPreset[] | all twelve | Which presets to offer. |
| custom | boolean | true | Offer a colour of your own. |
| target | () => HTMLElement | null | <html> | Where data-zero-accent is written. |
| storageKey | string | null | "zero-accent" | Where the choice is remembered; null to forget it. |
| side / align | Popover's | "bottom" / "end" | Where the popover opens. |
| aria-label | string | "Accent colour" | The button's name, followed by the accent in use. |
| labels | AccentLabelsInput | accentLabels | What it says in another language: the presets' names, the button's name, the title, the label for your own and the notes (functions of the ratio). Any part; the rest stays English. |