Skip to content
DocsSupportPricing
Roadmap (has updates coming soon)XFigma3.7K
Sign inGet All-Access
Overview
  • Introduction
  • Get Started
  • License Setup
  • Styling
  • Registry
  • MCP Server
  • Embed
  • Agent Skills
  • llms.txt
  • RTL
  • Changelogv2.8.0
  • YouTube
MCP Server
  • Claude
  • CodexCodexCodex
  • Cursor
  • Grok
  • Conductor
  • v0
  • Lovable
  • Replit
  • Bolt
  • OpenCode
  • VS Code
  • GitHub Copilot
  • Kilo Code
  • Zed
  • Antigravity
  • WSWindsurf
  • CLCline
  • Gemini CLI
  • AMAmp
  • JBJetBrains Junie
Components
  • Alert
  • Autocomplete
  • Badge
  • Cascader
  • Code Block
  • Color PickerNew Color Picker docs
  • Data GridData Grid adds grouped and spanning headers, scrolls faster, and its scrollbar stops above a sticky footer
  • Date Selector
  • Emoji PickerNew Emoji Picker docs
  • Event Calendar
  • File TypeNew File Type docs
  • File Upload
  • Filters
  • Frame
  • Gantt
  • Icon Stack
  • Icon Tile
  • Kanban
  • Number Field
  • Phone Input
  • Rating
  • Scrollspy
  • Signature Pad
  • Sortable
  • StepperAdded 29 new Stepper examples, 44 in all
  • Time Picker
  • Timeline
  • Tree

Application

  • App ShellAdded 4 new App Shell blocks
  • Auth
  • Card
  • Dashboard
  • Dialog
  • Empty State
  • Event Calendar
  • Flow
  • Form
  • Gantt
  • Kanban Board
  • List
  • Navbar
  • Onboarding
  • Profile
  • Rich Text Editor
  • Schedule
  • Settings
  • Sheet
  • Stats
  • Timeline
  • Whiteboard
  • Wizard

Data Grid

  • Base
  • AdvancedNew Advanced category with 3 Data Grid blocks
  • Columns
  • Drag & Drop
  • Editing
  • Expansion
  • Filtering
  • Grouping
  • Virtualization

Charts

  • Area Chart
  • Bar Chart
  • Composed Chart
  • Line Chart
  • Pie Chart
  • Radial Chart
  • Sparkline
  • Status Tracker

AI & Agents

  • AI Chat
  • Agent Activity

Solutions

  • Dev Ops
  • AI Ops
  • CRM
  • Agents
  • Analytics
  • Billing
  • Bookings
  • Files
  • Inventory
  • Users

Templates

  • E-commerce
  • SaaS
  • Dashboard
  • Landing
  • All templates

eCommerce

  • Category Card
  • Checkout
  • Comparison
  • Coupon
  • Filter Sidebar
  • Product Card
  • Product Detail
  • Product Grid
  • Receipt
  • Review
  • Shopping Cart
  • Wishlist
  • Shop Hero

Marketing

  • Blog
  • Compare
  • Contact
  • CTA
  • FAQ
  • Hero
  • How It Works
  • Pricing

Resources

  • Components
  • Blocks
  • Icons
  • MCP for Agents
  • Docs
  • Support
  • Pricing
  • Tailwind Plus Alternative
  • Roadmap(has updates coming soon)
  • AffiliateSoon

Legal

  • Privacy Policy
  • Terms & Conditions
  • License
  • Refunds
  • Cookies

© 2026 ReUI. All rights reserved.

3.7K

Shadcn Color Picker

PreviousNext

Custom Shadcn Color Picker for React and Tailwind CSS. A shadcn color picker with a saturation area, hue, opacity and channel sliders, a hue wheel, HEX, RGB, HSL, HSB and OKLCH fields, swatches and an eye dropper.

Base UIRadix UI

Installation

pnpm dlx shadcn@latest add @reui/color-picker

Usage

import {
  ColorPicker,
  ColorPickerArea,
  ColorPickerContent,
  ColorPickerFields,
  ColorPickerPanel,
  ColorPickerSlider,
  ColorPickerSwatches,

Shadcn Color Picker Free Components

Browse 15 production-ready Shadcn Color Picker components for dashboards, forms, and product UI. These examples use Base UI primitives from @base-ui/react and stay fully compatible with Shadcn Create so radius, color, and typography match your configured theme.

Browse all 15 Shadcn Color Picker components for copy-ready layouts, dashboards, and forms built with Tailwind CSS in the ReUI library.

Code BlockData Grid

On This Page

InstallationUsageFor AI AgentsExamplesPopover pickerColor inputOpacityPreset swatchesOKLCH theme tokenAPI ReferenceValuei18nColorPickerColorPickerTriggerColorPickerValueColorPickerInputColorPickerContentColorPickerPanelColorPickerAreaColorPickerSliderColorPickerWheelColorPickerFieldsColorPickerChannelInputColorPickerSwatches, ColorPickerSwatchItemColorPickerSwatchColorPickerEyeDropper, ColorPickerCopy, ColorPickerClearuseColorPickerHelpersFormsData AttributesKeyboardAccessibility
ColorPickerTrigger,
} from "@/components/reui/color-picker"
<ColorPicker value={color} onValueChange={setColor} alpha>
  <ColorPickerTrigger />
  <ColorPickerContent>
    <ColorPickerPanel>
      <ColorPickerArea />
      <ColorPickerSlider channel="hue" />
      <ColorPickerSlider channel="alpha" />
      <ColorPickerFields />
      <ColorPickerSwatches colors={["#ef4444", "#22c55e", "#3b82f6"]} />
    </ColorPickerPanel>
  </ColorPickerContent>
</ColorPicker>

For AI Agents

Hand your agent this brief or the ReUI MCP, not a screenshot. Free to install.

When to use

  • Brand, accent and theme token settings kept as a hex, rgb or oklch string
  • Label, text and highlight colors chosen from a fixed palette of named presets
  • Design and drawing tools: fill and stroke inspectors, brush pickers, contrast checks and shade scales

When not to use

  • A whole settings, profile or data entry screen with many fields and a save bar: use the Form blocks
  • A full document writing surface with a formatting toolbar, lists and links: use the Rich Text Editor blocks

Agent notes

  • ColorPicker renders no wrapper element, so its children lay out in the parent; className on it styles only the trigger it draws when given no children
  • A value passed in any syntax parseColor reads is accepted, but the next change comes back written in the root format
  • Without alpha, opacity in a passed value is ignored on screen and every value the picker emits is opaque
Markdownhttps://reui.io/docs/components/base/color-picker.md
MCPget_component("color-picker")
Installnpx shadcn@latest add @reui/color-picker
Full index for agents:llms.txtReUI MCP setup

Examples

The hue wheel, channel sliders, recent colors, contrast checker, brand scale and the other examples are on the Color Picker page.

Popover picker

Color input

Opacity

Preset swatches

OKLCH theme token

API Reference

Color Picker is composable. ColorPicker holds the value and renders no wrapper element. ColorPickerTrigger or ColorPickerInput opens ColorPickerContent, and ColorPickerPanel renders the same controls in place. Inside either, mix the area, sliders, wheel, fields, swatches and actions in any layout. It composes the shadcn Popover, Button, InputGroup and DropdownMenu, and does its own color math, so there is no color library to install.

Value

The value is a CSS color string, written in format: #3b82f6 by default. null or "" is empty. A value from outside can be any color parseColor reads, in any format; the picker shows it, and the next change is written in format.

formatOpaqueWith alpha
"hex"#3b82f6#3b82f680
"rgb"rgb(59 130 246)rgb(59 130 246 / 0.5)
"hsl"hsl(217.2 91.2% 59.8%)hsl(217.2 91.2% 59.8% / 0.5)
"hsb"hsb(217.2 76% 96.5%)hsb(217.2 76% 96.5% / 0.5)
"oklch"oklch(62.31% 0.18801 259.815)oklch(62.31% 0.18801 259.815 / 0.5)

OKLCH is written to 0.01% lightness, 5 decimals of chroma and 3 of hue, so hex to OKLCH and back lands on the same color. A swatch picked in an "oklch" picker stays selected after a reload.

onValueChange fires on every change, each move of a drag included, and only when the written string changes. onValueCommit fires once an interaction ends: a drag released, a key pressed, a field or the input read, a swatch, the eye dropper or Clear. Save to a server or an undo history there.

const [color, setColor] = useState<string | null>("#3b82f6")
 
<ColorPicker
  value={color}
  onValueChange={setColor}
  onValueCommit={(value) => saveBrandColor(value)}
/>

The picker keeps the channels a color was edited in, not only the string it emits. Drag to a gray and the hue slider stays where you left it; set OKLCH chroma to 0 and back and the hue returns. An OKLCH value outside sRGB, such as Tailwind v4's oklch(62.3% 0.214 259.815), round-trips verbatim in "oklch" format. The other formats, and the area and sliders, map it into sRGB the way CSS Color 4 does: chroma comes down at the same lightness and hue until clipping the rest is not noticeable, so the hex matches what a browser paints on an sRGB screen. lab(), lch(), oklab() and a wide-gamut color() are kept as OKLCH the same way.

Without alpha, the value is always opaque: an incoming alpha is ignored and the opacity slider and field are left out.

type ColorPickerColor = {
  space: "rgb" | "hsl" | "hsv" | "oklch"
  channels: [number, number, number]
  alpha: number
}

useColorPicker and the helpers hand you that parsed color. rgb runs 0 to 255; hsl and hsv take hue 0 to 360 and the rest 0 to 100; oklch takes lightness 0 to 100, chroma 0 to 0.4 and hue 0 to 360. alpha is 0 to 1.

i18n

Every string the picker renders comes from i18n. It is merged over the English defaults per section, so one label is enough.

LabelDefaultUsed for
placeholder"Select color"Trigger and input text while empty.
panelLabel"Choose color"Names the popover and an inline panel.
openPicker"Open color picker"Names the swatch button in ColorPickerInput.
area"Color"Names the saturation and brightness area.
hue, saturation, brightness, lightness, chroma"Hue", "Saturation" and so onNames the sliders and fields, and the area's text.
red, green, blue, alpha, hex"Red", "Green", "Blue", "Opacity", "Hex"Names the sliders and fields.
format"Color format"Names the format menu in ColorPickerFields.
eyeDropper, copy, copied, clear"Pick a color from the screen" and so onThe action buttons.
swatches"Swatches"Names a swatch list with no label of its own.
FunctionTypeDescription
getColorName(color: ColorPickerColor) => stringA spoken name, "dark vibrant blue", in the area's and the hue slider's value text and for unlabelled swatches.
const DE: ColorPickerI18nOverrides = {
  labels: {
    placeholder: "Farbe wählen",
    hue: "Farbton",
    alpha: "Deckkraft",
    eyeDropper: "Farbe vom Bildschirm aufnehmen",
  },
}
 
<ColorPicker i18n={DE} />

ColorPicker

The root. Holds the value and the popover state and renders no wrapper element. Without children it renders a ColorPickerTrigger and a ColorPickerContent with the default panel, so <ColorPicker /> alone is a complete picker; className and placeholder reach that trigger.

PropTypeDefaultDescription
valuestring | null-Controlled value, any color parseColor reads. null is empty.
defaultValuestring | nullnullInitial value when uncontrolled, and what a form reset restores.
onValueChange(value: string | null) => void-Fires on every change, a drag included, with the value in format.
onValueCommit(value: string | null) => void-Fires once an interaction ends, when the value changed during it.
formatColorPickerFormat"hex"How the value is written: "hex", "rgb", "hsl", "hsb" or "oklch".
alphabooleanfalseAdds the opacity slider and field, and lets the value carry alpha.
swatchesstring[]-Preset colors under the default panel.
openboolean-Controlled popover state.
defaultOpenbooleanfalseInitial popover state when uncontrolled.
onOpenChange(open: boolean) => void-Fires when the popover opens or closes.
disabledbooleanfalseDisables every part and stops the popover opening. A disabled picker submits nothing.
readOnlybooleanfalseThe popover opens and the controls take focus, but nothing changes the value.
invalidbooleanfalseSets aria-invalid on the trigger or the input, and data-invalid on the trigger.
namestring-Submits the value with a native form under this name.
formstring-Associates the field with a form elsewhere in the page.
requiredbooleanfalseFails native form validation while empty.
inputRefRef<HTMLInputElement>-The hidden form field, for form libraries that focus a field on error.
idstringgeneratedLands on the trigger or the input, so a label's htmlFor reaches it.
aria-labelstring-Names the trigger or the input, the popover and an inline panel.
aria-labelledbystring-The same, by id. The trigger keeps its own text in the name.
aria-describedbystring-Describes the trigger or the input.
i18nColorPickerI18nOverridesEnglishLabels and the color namer, merged over the defaults.
placeholderstringlabels.placeholderText of the default trigger while empty.
classNamestring-Classes for the default trigger.

ColorPickerTrigger

A shadcn Button that opens the popover, and the element that takes the root's id and ARIA props. It shows a ColorPickerSwatch, the value and a chevron. children replace all three, for a swatch-only or icon trigger. Accepts every Button prop.

PropTypeDefaultDescription
variantButton variant"outline"The look of the button.
placeholderReactNodelabels.placeholderShown while there is no value.
childrenReactNodeswatch, value, chevronReplaces the content of the button.

ColorPickerValue

The value as text, in format with hex in capitals, or the placeholder, in a span. Accepts every span prop.

PropTypeDefaultDescription
placeholderReactNodelabels.placeholderShown while there is no value.

ColorPickerInput

A shadcn InputGroup with a swatch button that opens the popover and a field you can type in. It reads everything parseColor reads, plus what only a page can resolve, on blur and Enter: color-mix(), relative colors, light-dark() and var(), so var(--primary) takes your theme's primary and var(--x, red) its fallback. A CSS-wide keyword, currentColor or a var() with no value is rejected rather than read as black. Text it cannot read is dropped and the value restored. An empty field clears the value. Accepts every input prop except value and defaultValue.

ColorPickerContent

The shadcn PopoverContent, aligned to the start and w-72 wide, or with alpha w-84 (w-96 below md, where the fields' text is 16px), capped to the viewport. Without children it renders ColorPickerPanel with the default controls. Accepts every PopoverContent prop, align, side and className included.

ColorPickerPanel

The controls in a column, spaced per style. Without children: the area, the eye dropper and a copy button either side of the hue slider (and the opacity slider with alpha), the fields and the root's swatches. Put it straight inside ColorPicker for an inline picker. Accepts every div prop.

ColorPickerArea

The saturation and brightness square at the current hue, h-40 and full width. Saturation runs left to right and brightness bottom to top. Accepts every div prop; size it with className.

ColorPickerSlider

One channel as a gradient track. The track shows what the channel can reach from the current color, so dragging red redraws green and blue. Accepts every div prop.

PropTypeDefaultDescription
channelColorPickerChannel-"hue", "saturation", "brightness", "lightness", "chroma", "red", "green", "blue" or "alpha".
colorSpace"hsv" | "hsl" | "rgb" | "oklch"per channelThe space the channel belongs to. HSB for hue, saturation and brightness, HSL for lightness, OKLCH for chroma, RGB for red, green and blue.
orientation"horizontal" | "vertical""horizontal"Vertical tracks run bottom to top and default to the area's height.

colorSpace="oklch" with "lightness", "chroma" and "hue" gives perceptual sliders, the ones the OKLCH theme token example uses.

ColorPickerWheel

A hue ring, size-48. Children fill the square inscribed in its hole, so a ColorPickerArea with className="size-full" there gives the ring-and-square picker. --color-picker-wheel-thickness sets the ring's width. Accepts every div prop.

ColorPickerFields

A format menu and the value's channels as joined fields in a shadcn InputGroup, divided by full-height shadcn Separators: 1 hex field, or 3 channels, plus opacity with alpha, which keeps its own width at the end while hex or the channels take the rest. Each menu item shows the color in that format. The shown format is the fields' own and never changes what onValueChange emits, but it is what a copy takes.

PropTypeDefaultDescription
formatColorPickerFormat-The shown format, controlled.
defaultFormatColorPickerFormatroot formatThe shown format, uncontrolled.
onFormatChange(format: ColorPickerFormat) => void-Fires when the menu changes it.
formatsColorPickerFormat[]all 5The menu's formats. 1 format hides the menu.

ColorPickerChannelInput

One channel as a borderless field, for your own InputGroup, sized by its widest value. Text is read on blur and Enter: one number with a comma or dot decimal. % is a share of the channel's range as in CSS, so 50% red is 127.5 and 50% chroma 0.2, and a hue takes deg, rad, grad or turn and wraps, so -30 is 330. A number field is a spinbutton with its range. Accepts every input prop except value and defaultValue.

The fields of OKLCH with opacity need about 290px, or 312px below md where the text is 16px. The popover's opacity width fits both; in a narrower inline panel the widest values clip.

PropTypeDefaultDescription
channelColorPickerChannel | "hex"-A channel, or "hex" for the 6 hex digits.
colorSpace"hsv" | "hsl" | "rgb" | "oklch"per channelAs on ColorPickerSlider.
<InputGroup>
  <InputGroupAddon>R</InputGroupAddon>
  <ColorPickerChannelInput channel="red" />
</InputGroup>

ColorPickerSwatches, ColorPickerSwatchItem

Preset colors as a listbox, in a grid that fills its width. --color-picker-swatch-size sets the chip size, 24px by default, and a grid-cols-* class fixes the columns. An item is selected while the value is its color, and shows a check in black or white, whichever contrasts more.

PropTypeDefaultDescription
colorsArray<string | { value: string; label?: string }>-The presets. Children replace them.
shape"square" | "circle""square"square follows each style's radius.
childrenReactNode-ColorPickerSwatchItem parts, for custom data.

ColorPickerSwatchItem takes value, any color parseColor reads, label, read out instead of the generated color name, and disabled.

ColorPickerSwatch

A color chip. It shows the picker's value by default, or color, so it also works outside a picker, in a table or a legend. Translucent colors sit on a checkerboard, and an empty value shows a slash. It is hidden from assistive tech unless you give it an aria-label. Accepts every span prop.

PropTypeDefaultDescription
colorstring | nullpicker's valueThe color to show.
shape"square" | "circle""square"square follows each style's radius.

ColorPickerEyeDropper, ColorPickerCopy, ColorPickerClear

shadcn Buttons for the three actions. Each accepts every Button prop.

  • ColorPickerEyeDropper samples a color from anywhere on screen with the browser's EyeDropper API and keeps the current opacity. Where the browser has no EyeDropper, Firefox and Safari among them, it renders nothing.
  • ColorPickerCopy copies the value in its own format prop or, by default, the format on screen: what ColorPickerFields shows, else the picker's. It shows a check for a moment and announces "Copied". ⌘ + C or Ctrl + C on any control copies the same way, with no button needed; in a field it does so only when no text is selected, so a selection still copies as text.
  • ColorPickerClear empties the value, for an optional color.

useColorPicker

Returns the picker's state and actions inside a ColorPicker, for a custom readout or action. It throws outside one.

FieldTypeDescription
valuestring | nullThe value as emitted.
colorColorPickerColor | nullThe same value, parsed.
formatColorPickerFormatThe root's format.
openbooleanWhether the popover is open.
setOpen(open: boolean) => voidOpens or closes the popover.
setValue(value: string | null) => voidCommits any color parseColor reads; null or "" clears, anything else is ignored.
clear() => voidEmpties the value.
function HexLabel() {
  const { color } = useColorPicker()
  return color ? <span>{formatColor(color, "hex")}</span> : null
}

Helpers

Pure functions for code that never mounts a picker. They ship in the same "use client" module as the picker, so a Server Component cannot call them.

HelperReturns
parseColor(value)A ColorPickerColor from hex, the 148 named colors, transparent, rgb(), hsl(), hwb(), lab(), lch(), oklab(), oklch(), color() in any CSS Color 4 space, hsb() or hsv(), else null.
formatColor(color, format)The color written in a format, as the table under Value shows.
getContrastRatio(text, background)The WCAG 2 contrast ratio, 1 to 21, from strings or parsed colors, or null.
getForegroundColor(background, { light, dark })The text color that reads better on the background, light (white) or dark (#0a0a0a), never light under 3:1, or null.
isColorInSrgb(color)Whether a color fits sRGB. Only an OKLCH color can fall outside it.
mergeColorPickerI18n(overrides)The full i18n config, the overrides merged over the defaults per section.
DEFAULT_COLOR_PICKER_I18NThe English labels and color namer.
COLOR_PICKER_FORMATS["hex", "rgb", "hsl", "hsb", "oklch"].
const blue = parseColor("#3b82f6") // { space: "rgb", channels: [59, 130, 246], alpha: 1 }
if (blue) formatColor(blue, "oklch") // "oklch(62.31% 0.18801 259.815)"
getContrastRatio("#ffffff", "#3b82f6") // 3.68
getForegroundColor("#3b82f6") // "#ffffff", where the ratio above prefers black

Forms

Give the root a name and the value submits with a native form, in format. An empty picker submits an empty string, so required works, and a disabled picker submits nothing. form ties the field to a form elsewhere in the page, and inputRef points at it for form libraries that focus a field on error.

A form reset restores defaultValue, empty unless you passed one; a controlled picker gets it through onValueChange. A listener that cancels the reset keeps the value. invalid is for your own validation: pair it with an error message in aria-describedby, as the form field example on the Color Picker page does.

<form onSubmit={handleSubmit}>
  <FieldLabel htmlFor="brand">Brand color</FieldLabel>
  <ColorPicker id="brand" name="brandColor" required>
    <ColorPickerInput />
    <ColorPickerContent />
  </ColorPicker>
</form>

Data Attributes

AttributeOnValues
data-placeholdertrigger, valuepresent while there is no value
data-invalidtriggerpresent when invalid
data-disabledpanel, ColorPickerInput, fieldspresent when disabled
data-draggingarea, slider, wheel trackpresent during a pointer drag
data-channelslider, channel inputthe channel, or hex
data-orientationsliderhorizontal, vertical
data-emptyswatch, swatchesno value; or no keyboard swatch yet
data-activeswatch itempresent on the swatch the keyboard is on
data-selectedswatch itempresent on the swatch of the value
data-copiedcopy buttonpresent while it shows the check
Partdata-slot
ColorPickerTriggercolor-picker-trigger
ColorPickerValuecolor-picker-value
ColorPickerInputcolor-picker-input, and color-picker-input-trigger on the swatch button
ColorPickerContentpopover-content, the shadcn popover's own
ColorPickerPanelcolor-picker-panel
ColorPickerAreacolor-picker-area, and color-picker-area-thumb inside
ColorPickerSlidercolor-picker-slider, and color-picker-slider-thumb inside
ColorPickerWheelcolor-picker-wheel, then color-picker-wheel-track and its thumb
ColorPickerFieldscolor-picker-fields, and color-picker-format-trigger on the menu button
ColorPickerSwatchescolor-picker-swatches, and color-picker-swatch-item on each item
ColorPickerSwatchcolor-picker-swatch
Eye dropper, Copy, Clearcolor-picker-eye-dropper, color-picker-copy, color-picker-clear
The hidden form fieldcolor-picker-field

Keyboard

On the area, a slider or the wheel:

KeyAction
← →Area: saturation down or up. Slider and wheel: the value down or up.
↓ ↑Area: brightness down or up. Slider and wheel: the value down or up.
Shift + arrowMoves 10 steps.
Page Down Page UpMoves 10 steps; brightness on the area.
Home EndThe lowest or highest value; saturation on the area.

A step is 1 unit: 1° of hue, 1% of saturation, brightness, lightness or opacity, 1 of red, green or blue, and 0.004 of chroma. The wheel wraps at 360°.

On swatches:

KeyAction
← → ↑ ↓Moves to the previous or next swatch in order, mirrored in right-to-left text.
Home EndMoves to the first or last swatch.
Enter SpacePicks the swatch the keyboard is on.

In the fields and ColorPickerInput:

KeyAction
EnterReads the typed text, before a form submits.
↑ ↓In a number field, steps it by 1, or 10 with Shift.
Page Up Page DownIn a number field, steps it by 10. A hue wraps.
Alt + ↓In ColorPickerInput, reads the text and opens the popover.
EscWhile editing, puts the value back and leaves the popover open.
⌘ / Ctrl + COn any control, with no text selected, copies the value in the format on screen.

In the popover, Tab and Shift + Tab cycle through the controls, and Esc closes it once no field holds an edit.

Accessibility

  • The area, each slider and the wheel are sliders. The area is one slider whose value text names both channels and the color, "Saturation 72%, Brightness 85%, vibrant blue", since a 2D control has no ARIA role of its own. The hue slider's value text adds the color name too.
  • A click focuses the control without painting the keyboard ring; the ring shows from the keyboard only, on the thumb.
  • Swatches are a listbox with one tab stop. aria-activedescendant points at the swatch the keyboard is on, and each option is named by its label or the color name, never by a hex code read digit by digit.
  • With aria-labelledby, the trigger is named by your label followed by its own text, so the value, or what custom children show, stays in the name.
  • The area, the sliders and the fields are dir="ltr" in every text direction: gradients and channel order do not mirror.
  • In forced colors mode the area, the tracks, the wheel and the swatches keep their colors (forced-color-adjust: none), since the color is the content, and the thumbs keep an outline.
  • The hidden form field is out of the tab order and hidden from assistive tech. A failed required check focuses it, and it hands focus on to the trigger, the input or, for an inline panel, the area.
"use client"

import { useState } from "react"
import {
  ColorPicker,
  ColorPickerPanel,
} from "@/components/reui/color-picker"

import { Card, CardContent } from "@/components/ui/card"

const SWATCHES = [
  "#ef4444",
  "#f97316",
  "#eab308",
  "#22c55e",
  "#06b6d4",
  "#3b82f6",
  "#8b5cf6",
  "#ec4899",
]

export function Pattern() {
  const [value, setValue] = useState<string | null>("#6366f1")

  return (
    <Card className="w-full max-w-96">
      <CardContent>
        {/* `alpha` adds the opacity slider and field; `swatches` adds the
            presets row under the default panel. */}
        <ColorPicker
          value={value}
          onValueChange={setValue}
          alpha
          swatches={SWATCHES}
          aria-label="Fill color"
        >
          <ColorPickerPanel />
        </ColorPicker>
      </CardContent>
    </Card>
  )
}
import { ColorPicker } from "@/components/reui/color-picker"

import {
  Field,
  FieldDescription,
  FieldLabel,
} from "@/components/ui/field"

export function Pattern() {
  return (
    <div className="w-full max-w-xs">
      <Field>
        <FieldLabel id="accent-color-label" htmlFor="accent-color">
          Accent color
        </FieldLabel>
        {/* With no children the picker renders its own trigger and popover.
            `aria-labelledby` keeps the value in the trigger's name, so it
            reads "Accent color #0EA5E9" rather than the label alone. */}
        <ColorPicker
          id="accent-color"
          defaultValue="#0ea5e9"
          aria-labelledby="accent-color-label"
          aria-describedby="accent-color-description"
        />
        <FieldDescription id="accent-color-description">
          Used for links, focus rings and primary buttons.
        </FieldDescription>
      </Field>
    </div>
  )
}
"use client"

import { useState } from "react"
import { ColorPicker } from "@/components/reui/color-picker"

import { Card, CardContent } from "@/components/ui/card"
import {
  Field,
  FieldDescription,
  FieldLabel,
} from "@/components/ui/field"

export function Pattern() {
  const [overlay, setOverlay] = useState<string | null>("rgb(15 23 42 / 0.55)")

  return (
    <Card className="w-full max-w-sm overflow-hidden pt-0">
      <div className="relative flex h-40 items-end bg-[linear-gradient(135deg,#f97316,#ec4899_45%,#6366f1)] p-5">
        <div
          aria-hidden="true"
          className="absolute inset-0"
          style={{ background: overlay ?? "transparent" }}
        />
        <div className="relative">
          <p className="text-xs font-medium tracking-wide text-white/80 uppercase">
            New season
          </p>
          <p className="text-lg font-semibold text-white">Spring collection</p>
        </div>
      </div>
      <CardContent>
        <Field>
          <FieldLabel id="banner-overlay-label" htmlFor="banner-overlay">
            Banner overlay
          </FieldLabel>
          {/* `alpha` adds the opacity slider and field; `format="rgb"` writes
              the value as rgb(15 23 42 / 0.55), ready for a style prop. */}
          <ColorPicker
            id="banner-overlay"
            value={overlay}
            onValueChange={setOverlay}
            alpha
            format="rgb"
            aria-labelledby="banner-overlay-label"
            aria-describedby="banner-overlay-description"
          />
          <FieldDescription id="banner-overlay-description">
            Darken the artwork so the headline stays readable.
          </FieldDescription>
        </Field>
      </CardContent>
    </Card>
  )
}
import {
  ColorPicker,
  ColorPickerContent,
  ColorPickerInput,
} from "@/components/reui/color-picker"

import {
  Field,
  FieldDescription,
  FieldLabel,
} from "@/components/ui/field"

export function Pattern() {
  return (
    <div className="w-full max-w-xs">
      <Field>
        <FieldLabel htmlFor="canvas-background">Canvas background</FieldLabel>
        {/* The input reads any CSS color on blur or Enter and writes it back
            as hex; the swatch button opens the full picker. */}
        <ColorPicker id="canvas-background" defaultValue="#f8fafc">
          <ColorPickerInput aria-describedby="canvas-background-description" />
          <ColorPickerContent />
        </ColorPicker>
        <FieldDescription id="canvas-background-description">
          Type a hex code, rgb(), hsl(), oklch() or a name like tomato.
        </FieldDescription>
      </Field>
    </div>
  )
}
"use client"

import { useState } from "react"
import type { CSSProperties } from "react"
import { Badge } from "@/components/reui/badge"
import {
  ColorPicker,
  ColorPickerChannelInput,
  ColorPickerCopy,
  ColorPickerSlider,
  getForegroundColor,
  isColorInSrgb,
  parseColor,
} from "@/components/reui/color-picker"

import { Button } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { Checkbox } from "@/components/ui/checkbox"
import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from "@/components/ui/input-group"
import { Label } from "@/components/ui/label"

const CHANNELS = [
  { channel: "lightness", short: "L" },
  { channel: "chroma", short: "C" },
  { channel: "hue", short: "H" },
] as const

export function Pattern() {
  /* Tailwind's blue-500, a P3 color: its chroma runs past sRGB. */
  const [primary, setPrimary] = useState<string | null>(
    "oklch(62.3% 0.214 259.815)"
  )
  const color = parseColor(primary)
  const foreground = color ? getForegroundColor(color) : null
  /* The preview re-points the theme tokens themselves, so the Button and the
     Badge below render exactly as they would with this --primary. */
  const theme =
    primary && foreground
      ? ({
          "--primary": primary,
          "--primary-foreground": foreground,
        } as CSSProperties)
      : undefined

  return (
    <Card className="w-full max-w-md">
      <CardHeader>
        <CardTitle>Primary color</CardTitle>
        <CardDescription>
          Tune the --primary token in OKLCH, the format shadcn and Tailwind v4
          themes use.
        </CardDescription>
      </CardHeader>
      <CardContent>
        <div className="flex flex-col gap-5">
          <ColorPicker
            value={primary}
            onValueChange={setPrimary}
            format="oklch"
            aria-label="Primary color"
          >
            <div className="grid gap-3">
              {CHANNELS.map(({ channel, short }) => (
                <div
                  key={channel}
                  className="grid grid-cols-[1rem_1fr_4.5rem] items-center gap-3"
                >
                  <span
                    aria-hidden="true"
                    className="text-muted-foreground text-xs font-medium"
                  >
                    {short}
                  </span>
                  <ColorPickerSlider channel={channel} colorSpace="oklch" />
                  <InputGroup>
                    <ColorPickerChannelInput
                      channel={channel}
                      colorSpace="oklch"
                    />
                  </InputGroup>
                </div>
              ))}
            </div>
            <InputGroup>
              <InputGroupInput
                readOnly
                value={`--primary: ${primary ?? "none"};`}
                aria-label="CSS variable"
              />
              <InputGroupAddon align="inline-end">
                <ColorPickerCopy size="icon-xs" />
              </InputGroupAddon>
            </InputGroup>
          </ColorPicker>
          <div className="flex flex-wrap items-center gap-3" style={theme}>
            {/* Each fills with --primary and draws on --primary-foreground,
                so all three follow the picked color. */}
            <Button>Save changes</Button>
            <Badge>New</Badge>
            <div className="flex items-center gap-2">
              <Checkbox id="primary-notify" defaultChecked />
              <Label htmlFor="primary-notify">Notify team</Label>
            </div>
            {color && !isColorInSrgb(color) && (
              <Badge variant="warning-light" className="ms-auto">
                Outside sRGB
              </Badge>
            )}
          </div>
        </div>
      </CardContent>
    </Card>
  )
}
"use client"

import { useState } from "react"
import { Badge } from "@/components/reui/badge"
import {
  ColorPicker,
  ColorPickerContent,
  ColorPickerSwatch,
  ColorPickerSwatches,
  ColorPickerTrigger,
  getForegroundColor,
} from "@/components/reui/color-picker"

import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { PlusIcon } from 'lucide-react'

const LABEL_COLORS = [
  { value: "#ef4444", label: "Red" },
  { value: "#f97316", label: "Orange" },
  { value: "#eab308", label: "Yellow" },
  { value: "#22c55e", label: "Green" },
  { value: "#14b8a6", label: "Teal" },
  { value: "#3b82f6", label: "Blue" },
  { value: "#8b5cf6", label: "Violet" },
  { value: "#ec4899", label: "Pink" },
  { value: "#64748b", label: "Slate" },
]

export function Pattern() {
  const [name, setName] = useState("Needs design")
  const [color, setColor] = useState<string | null>("#8b5cf6")
  const custom =
    !!color && !LABEL_COLORS.some((preset) => preset.value === color)
  const ink = color ? getForegroundColor(color) : null

  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Edit label</CardTitle>
        <CardDescription>Labels group issues across projects.</CardDescription>
      </CardHeader>
      <CardContent>
        <FieldGroup>
          <Field>
            <FieldLabel htmlFor="label-name">Name</FieldLabel>
            <Input
              id="label-name"
              value={name}
              onChange={(event) => setName(event.target.value)}
            />
          </Field>
          <Field>
            <FieldLabel id="label-color-label">Color</FieldLabel>
            <ColorPicker value={color} onValueChange={setColor}>
              <div className="flex items-center gap-3">
                <ColorPickerSwatches
                  colors={LABEL_COLORS}
                  shape="circle"
                  aria-labelledby="label-color-label"
                  className="flex-1"
                />
                {/* The plus opens the full picker; once a custom color is
                    set, it shows that color instead. */}
                <ColorPickerTrigger size="icon-sm" aria-label="Custom color">
                  {custom ? (
                    <ColorPickerSwatch shape="circle" />
                  ) : (
                    <PlusIcon  aria-hidden="true" />
                  )}
                </ColorPickerTrigger>
                <ColorPickerContent align="end" />
              </div>
            </ColorPicker>
          </Field>
          <div className="flex items-center gap-3">
            <span className="text-muted-foreground text-sm">Preview</span>
            <Badge
              style={{
                backgroundColor: color ?? undefined,
                color: ink ?? undefined,
              }}
            >
              {name.trim() || "Untitled"}
            </Badge>
          </div>
        </FieldGroup>
      </CardContent>
    </Card>
  )
}