zzzUI

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

terminal
# 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-picker

Examples

Recolouring one part of a page

Twelve presets, or a colour of your own
Rush
x
accent-picker-preview.tsx
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

PropTypeDefaultDescription
value / defaultValue / onValueChangeAccentPreset | "#rrggbb""lime"The accent, controlled or not.
accentsAccentPreset[]all twelveWhich presets to offer.
custombooleantrueOffer a colour of your own.
target() => HTMLElement | null<html>Where data-zero-accent is written.
storageKeystring | null"zero-accent"Where the choice is remembered; null to forget it.
side / alignPopover's"bottom" / "end"Where the popover opens.
aria-labelstring"Accent colour"The button's name, followed by the accent in use.
labelsAccentLabelsInputaccentLabelsWhat 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.