Color Picker

Pick one or more colors from a palette, with contrast and a preview in use.

Pattern

≈ 1,212 tokens per use (tool 632 · guidance 279 · typical payload 301)How token costs are estimated

Example: Color Picker

Pick a wall color for the living room; we currently have off-white.

Wall color for the living room

Linen Thread, AC-2041
Oat Milk, AC-2043
Sandbar, AC-2047
Driftwood Grey, AC-2052
Clay Pot, AC-2058
Pale Fern, AC-3110
Sage Leaf, AC-3114
Sea Glass, AC-4209
Slate Harbor, AC-4214
Juniper, AC-3126

Linen Thread

AC-2041 · Warm neutrals

#ECE4D4
Linen Thread next to the current color #F2EFE6, ΔE 3.3.
  • Against white: 1.26:1, Fails AA
  • Against black: 16.61:1, Passes AA and AAA
  • Against the current #F2EFE6: 1.10:1, Fails AA

When to use

  • The user must choose a color from a real set: paint colors, brand colors, fabric or product finishes, chart or theme colors.
  • Seeing the colors, their contrast or how they look in use helps the decision.

When not to use

  • You have no palette from a tool result. Fetch it first; never list colors or codes from memory.
  • The choice isn't visual, e.g. a product variant named by color. Use Product Options.
  • There is one obvious color. Say which and why.

How the agent should use it

  • Every color, hex and code comes from the source, exactly as given. code is the vendor's own code; never make one up.
  • Use group to split long palettes into families, e.g. "Warm neutrals", "Greens".
  • Set compareAgainst when the user mentions what they have now (the current wall or brand color), and use preview: "swatch" to show both side by side.
  • Use preview: "ui" for interface colors and preview: "text" when readability matters.
  • Set allowCustom only when a color outside the palette is acceptable. Custom colors come back without an id; treat them as hex values.
  • Act only on the colors returned with action: "select".

Interaction

  • The swatches are one listbox. Tab moves into it; arrow keys move across the grid (up and down follow the visual rows, across groups), Home and End jump to the ends, and Enter or Space selects. Selected swatches show a check mark and a ring, not only a color change.
  • Above 24 colors a search box filters by name, code, group or hex.
  • Swatches show the color name; the vendor code is shown in the detail panel and read with each swatch.
  • The detail panel follows the focused swatch: its name, code and group, the hex value with a copy button, the preview, and one compact contrast row: an "Aa" sample on white, black and compareAgainst, each with its WCAG ratio and level (AAA, AA, AA large or Fail). Screen readers hear the full verdict, e.g. "Against white: 4.52:1, Passes AA, fails AAA".
  • With allowCustom, hue, saturation and lightness sliders and a hex field mix a color, and the closest palette match is shown, e.g. "Closest: Harbor Blue". Its ΔE (OKLab, ×100) is read to screen readers.
  • RGB, HSL, ΔE and the nearest-color math are not shown on the card; they're exported from color-picker-utils.ts for the agent or server to use.

Installation

npx shadcn@latest add https://your-site.com/r/color-picker.json

Usage

app/api/chat/route.ts
import { tool } from "ai"
import { colorPickerTool } from "@/components/genui/color-picker.schema"

const tools = {
  // No execute(): the call is completed by the user's choice in the UI.
  [colorPickerTool.name]: tool({
    description: colorPickerTool.description,
    inputSchema: colorPickerTool.inputSchema,
  }),
}
components/message.tsx
import { ColorPicker } from "@/components/genui/color-picker"

function ToolPart({ part, addToolResult }) {
  if (part.type !== "tool-pick_color") return null

  return (
    <ColorPicker
      {...part.input}
      status={part.state === "input-streaming" ? "streaming" : undefined}
      result={part.state === "output-available" ? part.output : undefined}
      onResult={(result) =>
        addToolResult({
          tool: "pick_color",
          toolCallId: part.toolCallId,
          output: result,
        })
      }
    />
  )
}

The agent receives one of:

{ action: "select", colors: [{ id: "ac-3114", name: "Sage Leaf", hex: "#b5bea0", code: "AC-3114" }] }
{ action: "select", colors: [{ id: "c-blue", name: "Signal Blue", hex: "#2f6fc4", code: "DV-103" }, { hex: "#e0662b" }] }
{ action: "cancel" }

The color math (hex, RGB and HSL conversion, WCAG contrast, OKLab ΔE, nearest colors) lives in color-picker-utils.ts and has no React dependency, so you can reuse it on the server, e.g. to check contrast before you answer.

With LangGraph

Push it from a graph node with ui.push({ name: "pick_color", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.

Restoring from history

Pass the stored result back as result. The picker lists the chosen colors (or the cancelled state) read-only and never calls onResult again.

<ColorPicker
  {...part.input}
  result={part.state === "output-available" ? part.output : undefined}
/>

Schema

PropTypeNotes
titlestring?What to pick: "Wall color for the living room"
palette{ id, name, hex, code?, group? }[]Data-bound. 1–200 colors. code is the vendor's own code, up to 24 characters
selection"single" | "multiple"Default "single"
maxnumber?Most colors, when multiple
allowCustombooleanAdds a hue, saturation and lightness mixer. Default false
preview"swatch" | "text" | "ui"How to simulate the color. Default "swatch"
compareAgainststring?Hex of the current color. Data-bound when it describes something real
submitLabelstring?Verb for the confirm button. Default "Use this color" ("Use these colors" when multiple)

Host props: status (streaming, ready, submitting, selected, cancelled), onResult, result and className.

Source

On this page