chroma-panel works beside shadcn/ui without a provider or adapter. Use
ColorInput for the quickest setup. Use ChromaPanel when your shadcn/ui
popover, dialog, sheet, or form should own the surrounding UI.
Install
npm install chroma-panelThe package is independent of shadcn/ui, Radix UI, and Base UI. It does not add another copy of those libraries to your app.
Use the built-in popover
ColorInput already handles its trigger, positioning, focus, mobile bottom
sheet, and hidden form value.
"use client";
import { useState } from "react";
import { ColorInput } from "chroma-panel";
export function BrandColorField() {
const [color, setColor] = useState("#2563eb");
return (
<ColorInput
value={color}
onChange={(next) => setColor(next.hex)}
classNames={{
trigger:
"h-9 w-12 rounded-md border border-input bg-background shadow-xs",
}}
aria-label="Brand color"
/>
);
}The trigger accepts Tailwind classes through classNames.trigger. The panel
parts use the same pattern; see styling with
Tailwind.
Match the shadcn/ui theme
Map the panel variables to the tokens already defined by your shadcn/ui theme. This keeps light mode, dark mode, borders, and focus rings in sync.
.cp-root {
--cp-surface: var(--popover);
--cp-surface-raised: var(--muted);
--cp-border: var(--border);
--cp-text: var(--popover-foreground);
--cp-text-muted: var(--muted-foreground);
--cp-accent: var(--primary);
--cp-focus: var(--ring);
--cp-radius-lg: var(--radius);
}If your theme values use OKLCH, they can be passed through unchanged. The
theming guide lists every --cp-* variable.
Use a shadcn/ui popover or dialog
Do not place ColorInput inside another popover: that creates two overlays and
two focus managers. Put the inline ChromaPanel inside the shadcn/ui
PopoverContent, DialogContent, or SheetContent instead.
import { ChromaPanel } from "chroma-panel";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Choose color</Button>
</PopoverTrigger>
<PopoverContent className="w-auto border-0 bg-transparent p-0 shadow-none">
<ChromaPanel defaultValue="#2563eb" />
</PopoverContent>
</Popover>;shadcn/ui can be configured with different primitives. If your generated
PopoverTrigger uses render instead of asChild, keep the same composition
and follow the API in your local popover.tsx file.
Inside a modal or sheet, an inline panel is usually simpler. See color picker in a modal for focus and Escape-key behavior.