Accent switch
Switch zzzUI's accent colour at runtime: three presets that keep ink readable on them, chosen with a row of swatches 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-switchExamples
Recolouring one part of a page
import { useRef } from "react";import { AccentSwitch } from "@/components/zero/accent-switch";import { Badge } from "@/components/zero/badge";import { Button } from "@/components/zero/button";import { Progress } from "@/components/zero/progress";import { Switch } from "@/components/zero/switch";import { Tabs, TabsList, TabsTab } from "@/components/zero/tabs";export default function AccentSwitchPreview() { // 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"> <AccentSwitch target={() => box.current} storageKey={null} aria-label="Accent of the preview" /> <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> <Tabs defaultValue="open" size="sm"> <TabsList> <TabsTab value="open">Open</TabsTab> <TabsTab value="taken">Taken</TabsTab> </TabsList> </Tabs> <Progress value={62} aria-label="Route done" /> </div> </div> );}Behaviour
The presets are in the foundation: set data-zero-accent to lime (the default), cyan or magenta on <html> for the whole page, or on any element for what is inside it. Each keeps ink text on it readable (cyan 12.6 : 1, magenta 7 : 1) and is clear of the colours that already mean something here: the focus yellow, danger red and the five rarity tiers. That is why there is no amber, mint or violet.
AccentSwitch writes that attribute (on <html>, or on target) and remembers the choice in localStorage under storageKey; pass null to keep it to this page view. It is a radio group: the arrow keys move between the swatches, and each is named. Put AccentScript in <head> so a remembered accent is on the page before it paints. The switch in this site's header is one.
Corners are not switchable: zzzUI cuts, it does not round. The nearest knob is the slant, --zero-slant; at 0deg every cut becomes a square corner (see Theme zzzUI with its tokens).
Props
AccentSwitch
| Prop | Type | Default | Description |
|---|---|---|---|
| value / defaultValue / onValueChange | "lime" | "cyan" | "magenta" | "lime" | The accent, controlled or not. |
| target | () => HTMLElement | null | <html> | Where data-zero-accent is written. |
| storageKey | string | null | "zero-accent" | Where the choice is remembered; null to forget it. |
| aria-label | string | "Accent colour" | The group's name. |
AccentScript
| Prop | Type | Default | Description |
|---|---|---|---|
| storageKey | string | "zero-accent" | The same key as the switch. |