Forms
ColorField
A color on the Field contract: a swatch that opens the system color picker, next to the same color as hex text, kept in step both ways. Typing a whole hex (#36f or #3366ff) moves the swatch, leaving the text tidies it, and the form always gets #rrggbb.
Install
Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):
npx shadcn@latest init --base base --preset nova --yes --no-monoreponpx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):
npx shadcn@latest add ourostack/ouro-ui/color-field#v1.2.13Or, with the @ouro namespace set up:
npx shadcn@latest add @ouro/ouro-base @ouro/color-fieldIts tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):
npx shadcn@latest add ourostack/ouro-ui/color-field#v1.2.13 ourostack/ouro-ui/color-field-tests#v1.2.13import { ColorField } from "@/components/ouro/color-field/color-field";Paths use shadcn's default @/ alias; the CLI rewrites them to yours.
Example
<ColorField label="Accent color" name="accent" defaultValue="#3366ff" error={errors?.accent} />
<ColorField label="Tag color" value={color} onValueChange={setColor} />Use instead when
- Use
RadioGroupinstead when the colors come from a short fixed list, such as tag colors.
Props
Type: ColorFieldProps.
className- Type:
string - Layout only: margins, width, grid placement (
mt-4). Appearance classes warn in development; style fields in your theme. defaultValue- Type:
string - Default:
"#000000" - The color the field starts with, and goes back to when its form resets, as hex.
description- Type:
ReactNode - Supporting text under the label, read through
aria-describedby. disabled- Type:
boolean - Disables the swatch and the text, and fades the field.
error- Type:
ReadonlyErrorMessages - What is wrong, from your server action or client check. It shows under the field, which gets
aria-invalid. A message is text or a React node (one with a link); an empty string or array means valid. hideLabel- Type:
boolean - Hides the label visually; screen readers still read it.
label(required)- Type:
string - What the color is for ("Accent color"), shown above the field and read as the text input's name. Required: every control needs a name. To hide it visually, keep it and add
hideLabel. name- Type:
string - The form field name the color submits under, always as
#rrggbb. onValueChange- Type:
((hex: string) => void) - Called with the color as
#rrggbbwhen it is picked, or typed as a whole hex. pickerLabel- Type:
string - Default:
"Pick a color" - The name of the swatch that opens the system color picker. The swatch is a color input, which can only be named by label text, so Playwright's loose
getByLabel("Color")also finds it when the field's label is in this name; find the field withgetByLabel("Color", { exact: true })orgetByRole("textbox", { name: "Color" }). Translate it with your app. ref- Type:
Ref<HTMLInputElement> - The hex text input.
required- Type:
boolean - Marks the field required: it gets
aria-required, and the label a marker. A color always has a value, so it never blocks a submit, even with the text cleared. value- Type:
string - The color as hex (
#3366ff, or short#36f), when you control it. Pair it withonValueChange.
Don't use
These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.
alpha: Not supported by ColorField. ColorField takes#rrggbb, with no transparency.color: Not a prop. Ouro UI usesvalueordefaultValue:<ColorField defaultValue="#ff0000">.errorMessage: Not a prop. Ouro UI useserror:<ColorField error="Pick a darker color.">.format: Not a prop. ColorField takes and gives#rrggbb:<ColorField defaultValue="#3366ff">.helperText: Not a prop. Ouro UI usesdescription:<ColorField description="...">.invalid: Not a prop.errormarks the field invalid:<ColorField error="What is wrong">.isDisabled: Not a prop. Ouro UI usesdisabled:<ColorField disabled>.isInvalid: Not a prop.errormarks the field invalid:<ColorField error="What is wrong">.isRequired: Not a prop. Ouro UI usesrequired:<ColorField required>.onChange: Not a prop. Ouro UI usesonValueChange, which gets the hex:<ColorField onValueChange={(hex) => ...}>.
className is for layout only
Pass margins, width and grid placement (mt-4, w-full, col-span-2). Your classes always win over Ouro UI's: ColorField merges className last with cn (tailwind-merge), so a class that conflicts with its own replaces it.layout-classes
Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (hover:, sm:) and any ! are removed, it starts with bg-, text-, border-, rounded-, shadow-, font-, ring-, from-, via-, to-, fill- or stroke-, or is border, rounded, shadow or ring, or has a dark: variant. Text wrapping, overflow and table layout classes (text-wrap, text-ellipsis, border-collapse) are layout and do not warn.
Alignment classes (text-center) warn too: set alignment on a parent element.
Types
ErrorMessage
One error message: text, or a React element for a message that carries a link or other markup.
type ErrorMessage = string | ReactElement;ReadonlyErrorMessages
A read-only message view accepted by fields and Form; mutable input arrays remain valid.
type ReadonlyErrorMessages = ErrorMessage | readonly ErrorMessage[];Claim sources
- layout-classes: test:registry/ouro/components/ouro/color-field/color-field.test.tsx::ColorField > forwards its ref to the text input and lets layout classes win on the root
verify:testExists(registry/ouro/components/ouro/color-field/color-field.test.tsx::ColorField > forwards its ref to the text input and lets layout classes win on the root)