Brand Swatch Board
A brand-book swatch board React component: colour blocks fill in top to bottom, RGB, CMYK and WCAG contrast are computed automatically, text on each face picks white or black by contrast, and swatches lift on hover and copy their code on click.
The colour page from a brand book, as a component you can drop in. Pass a list of { name, hex } and it computes RGB, CMYK and the WCAG contrast against white and black, labelled AA / AAA / 不足 (fail); text on each colour face picks white or black automatically so it always reaches at least 4.5:1. When the board enters the viewport each face fills in from top to bottom in sequence, with its text fading in once filled; swatches lift on hover, and clicking (or Enter / Space when focused) copies the code in the current format while a "Copied" chip pops up in the middle of the face. A primary colour can span two columns via primary, and an optional toggle at the top switches between HEX / RGB / CMYK / All.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add brand-swatch-board.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<BrandSwatchBoard/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/brand-swatch-board.en.jsonOr, once registries are configured in components.json, install it as @webberui/brand-swatch-board.
Usage
import { BrandSwatchBoard } from "@/components/ui/brand-swatch-board";
<BrandSwatchBoard
columns={3}
showFormatToggle
swatches={[
{ name: "山嵐墨", hex: "#1E2A2F", note: "主色", primary: true },
{ name: "烘焙棕", hex: "#7A4E2D" },
{ name: "燕麥奶", hex: "#EFE6D8" },
{ name: "柿子橘", hex: "#E07A3F" },
]}
onCopy={(swatch, value) => console.log(`已複製 ${swatch.name} ${value}`)}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
swatches | BrandSwatch[] | — | Swatch list; changing a hex updates the colour face live (the face background is a CSS variable) |
columns | number | 3 | Number of columns on wide screens (1–6); narrow screens use at most two |
format | "hex" | "rgb" | "cmyk" | "all" | — | Controlled display format; managed internally when omitted (uncontrolled) |
defaultFormat | "hex" | "rgb" | "cmyk" | "all" | "all" | Initial display format in uncontrolled mode |
onFormatChange | (format: BrandSwatchFormat) => void | — | Called when the display format changes (via the top toggle) |
showFormatToggle | boolean | false | Whether to show the format toggle at the top |
stagger | number | 80 | Delay between each colour face's entrance (ms); falls back to --wb-bsb-stagger when omitted |
onCopy | (swatch: BrandSwatch, value: string) => void | — | Called after a successful copy with the swatch and the exact string copied |
className | string | — | Passed through to the outermost container |
BrandSwatch
| Field | Type | Default | Description |
|---|---|---|---|
name | string | — | Colour name, e.g. "Forest Green" |
hex | string | — | Colour code; supports #rgb and #rrggbb (the # is optional) |
note | string | — | Usage note, shown under the name |
primary | boolean | false | Primary colour: the face spans two columns (when columns ≥ 2) |
The pure helpers hexToRgb, rgbToHex, rgbToCmyk, relativeLuminance, contrastRatio and contrastLevel are also named exports, so the same conversions can be reused outside the board.
How it works
- Conversions:
hexToRgbaccepts three- and six-digit codes;rgbToCmykis a naive conversion without a colour profile, rounded to integer percentages — use your printer's profile before going to press; contrast follows the WCAG relative luminance formula, with ≥ 4.5 as AA and ≥ 7 as AAA for normal text. - What gets copied follows the current format: HEX gives
#1E2A2F, RGB givesrgb(30, 42, 47), CMYK givescmyk(36%, 11%, 0%, 82%); with All selected, HEX is copied. If the clipboard is unavailable or denied, the face shows "Could not copy". - The face background is
bg-[var(--wb-bsb-color)]with the hex passed through an inline CSS variable, so changingswatchesrepaints immediately. - Rhythm variables:
--wb-bsb-stagger(80ms),--wb-bsb-fill-duration(600ms) and--wb-bsb-ease(cubic-bezier(0.23,1,0.32,1)) can be overridden globally; astaggerprop takes precedence. The hover lift uses--wb-duration-fastand--wb-ease-out. - The entrance is triggered by an IntersectionObserver and plays only once.
Accessibility
- Each swatch is a native
<button>whosearia-labeldescribes the name and the code in the current format (e.g. "Copy Ridge Ink #1E2A2F"); focus it with Tab and copy with Enter or Space - The copy result is written to a hidden
role="status",aria-live="polite"region and announced by screen readers; decorative text on the face and the "Copied" chip are hidden from assistive technology to avoid duplication - The format toggle is a
role="radiogroup"; arrow keys, Home and End move the selection, with afocus-visiblering - Text on each face uses whichever of white or black has the higher contrast, guaranteeing at least 4.5:1
- When the user has reduce motion enabled at the system level, the fill and lift animations are skipped and swatches appear directly; the copy chip only fades in and out
Chaptered Report Shell
An annual-report / white-paper page shell — a sticky chapter rail generated automatically from the content, giant chapter numbers cross-fading between chapters, full-bleed color-block transitions between sections, and a dual reading-progress indicator.
Tri-Pane Workspace Shell
A nav + list + detail master-detail application shell with built-in collapsing, an overlay mode, and a page-by-page responsive state machine, its transitions choreographed as a spring sequence.