Swatch Grid
A grid of pickable samples, a color or a glyph over a color, of which at most one is chosen. The arrow keys move through it in two dimensions and only a click, Enter or Space chooses, so a pick with side effects never fires on a focus move.
Installation#
Usage#
import { SwatchGrid, SwatchGridItem } from "@/components/ui/swatch-grid"
export function Example() {
const [color, setColor] = React.useState("blue")
return (
<SwatchGrid aria-label="Label color" className="grid-cols-8" value={color} onValueChange={setColor}>
<SwatchGridItem value="tomato" label="Tomato" color="#e5484d" />
<SwatchGridItem value="blue" label="Blue" color="#0090ff" />
<SwatchGridItem value="jade" label="Jade" color="#29a383" />
</SwatchGrid>
)
}Anatomy#
import { SwatchGrid, SwatchGridItem } from "@/components/ui/swatch-grid"
<SwatchGrid>
<SwatchGridItem />
</SwatchGrid>Glyphs over a color#
size="md" makes 2rem cells with the glyph inset 0.1875rem, for an icon, a sprite <img> or a Phosphor <svg>; sm (the default) makes 1.75rem cells for plain colors. Here the icon grid is painted in the color chosen above it, so every cell shows the marker as it will look. A cell without color paints nothing, which suits the "Automatic" way back to a default.
Translucent colors#
checkered lays a checkerboard under the cell's color. An opaque color hides it; a translucent one lets it through, so 50% black reads as half-transparent rather than as a dim grey.
Disabled#
A disabled cell dims, takes no press and is skipped by the arrow keys. Say why in its label, since that is all a screen reader gets.
Keyboard#
The grid is one stop in the Tab order. Moving never chooses: a pick can cost a request, so only a deliberate key does. ↑ and ↓ read the rows from the layout, so they follow whatever column count the grid has at that width.
| Key | Action |
|---|---|
| ← → | Move to the previous or next cell, wrapping at the ends. |
| ↑ ↓ | Move to the nearest cell in the row above or below. |
| Home End | Move to the first or last cell. |
| Enter Space | Choose the focused cell. |
API reference#
SwatchGrid
The root: Radix ToggleGroup.Root with type="single", laid out as a CSS grid. Set the columns with className (e.g. grid-cols-8), merged last. Name it with aria-label.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | - | The chosen cell's value; "" for none. |
| defaultValue | string | "" | The chosen cell when uncontrolled. |
| onValueChange | (value: string) => void | - | Called with the newly chosen cell, never with "": pressing the chosen cell again keeps it. Listen to the cell's onClick to act on a repeat press. |
| size | "sm" | "md" | "sm" | Reaches every cell: sm (1.75rem) for colors, md (2rem, the glyph inset 0.1875rem) for glyphs. |
| disabled | boolean | false | Disables every cell. |
| loop | boolean | true | ←/→ wrap from the last cell to the first. |
| rovingFocus | boolean | true | Radix roving focus: the grid is one Tab stop and the arrow keys move inside it. |
| dir | "ltr" | "rtl" | - | The reading direction ←/→ follow. |
| static | boolean | false | Drops the press scale on every cell. |
| Data attribute | Description |
|---|---|
| data-slot | "swatch-grid". |
SwatchGridItem
A cell: a Radix ToggleGroup.Item (role="radio"). Every button prop is forwarded; children are a glyph (an <img> or an <svg>, sized to the cell).
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | The cell's value. |
| label* | string | - | The accessible name: a swatch carries no visible text. |
| color | string | - | The fill: any CSS color, set as --swatch. Without it, no fill. |
| checkered | boolean | false | Lays a checkerboard under the color, so a translucent one reads as translucent, not dim. |
| disabled | boolean | false | Takes the cell out of the roving focus and dims it. |
| static | boolean | - | Drops the press scale on this cell. Falls back to the grid's static. |
| Data attribute | Description |
|---|---|
| data-slot | "swatch-grid-item". |
| data-state | "on" on the chosen cell, "off" on the rest. |
| data-disabled | Present when the cell is disabled. |
| CSS variable | Description |
|---|---|
| --swatch | The cell's color, from color. transparent on the root, so a cell without one inherits nobody's. |
| --surface | Read for the gap between the cell and its selection ring: the ground the grid sits on (falls back to --background). |