Color

Color picker field with a preset palette and a free-form picker, both storing HSL.

Preview

Usage

import { z } from "zod";
import { AutoForm } from "@vitnode/core/components/form/auto-form";
import { AutoFormColor } from "@vitnode/core/components/form/fields/color";
const formSchema = z.object({
  color: z.string().default('hsl(240, 80%, 60%)'),
})
<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: 'color',
      component: (props) => <AutoFormColor {...props} label="Color" />,
    },
  ]}
/>

Preset palette

The popover opens with a grid of preset swatches - eight hues (gray, red, orange, yellow, green, teal, blue and purple) in three tones each. Click one and the field stores it as an HSL string, for example hsl(215, 81%, 52%). The swatches form a single radio group, so arrow keys move between them and the selected one shows a check mark.

The first swatch is the default, and it carries no color of its own. It renders in the current theme's text color and stores an empty value, which is how you say "no color" without leaving the palette.

The three tone rows were picked in OKLCH, so they step evenly to the eye rather than just on paper, and then written out as HSL - the format the picker itself speaks. Every tone keeps at least a 3:1 contrast ratio against both the light and the dark theme background, so a color chosen by an author in one theme stays readable for a reader in the other. A unit test enforces that, so keep it in mind if you fork the palette.

The presets live in @vitnode/core/components/ui/color-presets, and the standalone swatch grid ships as ColorPresetPicker from @vitnode/core/components/ui/color-preset-picker if you want the palette without the free-form picker:

import { ColorPresetPicker } from '@vitnode/core/components/ui/color-preset-picker'

;<ColorPresetPicker value={color} onChange={setColor} />

Output format

Both the presets and the free-form picker store an HSL string such as hsl(215, 81%, 52%), and the two stay in sync - click a preset and the picker's handle moves onto that color. The text field below takes any CSS color you paste into it, so the stored value is whatever you type there. Hex, rgb(...), hsl(...) and oklch(...) all show up on the picker; anything else leaves it parked on black until you drag it. The picker is powered by react-colorful and is lazy-loaded, so its bundle only ships once the popover is opened.

Editor text color

The rich text editor's text color toolbar action uses the same preset palette and nothing else - no free-form picker. Its popover is titled "Text color" and ends with a "Remove color" button, disabled while the selection has no color. The default swatch clears the color the same way.

Props

Prop

Type