# ColorPicker

> A native colour swatch with a synced hex field, preset swatches and an optional eyedropper.

URL: https://apogee.avaui.dev/docs/color-picker
Last updated: 2026-10-15

A native colour swatch with a synced hex field, preset swatches and an optional eyedropper.

Last updated October 15, 2026

Uses JavaScript

Keeps the native colour input and the hex field in sync both ways, applies a chosen preset swatch to both, and wires the eyedropper where the browser has one. Every committed pick fires one `change` on the hex field, the input that carries `name`.

**Without JS:** The native colour input and the hex field each still work on their own, but don't stay in sync. Preset swatches and the eyedropper are hidden, since they only work through the script.

```astro
<ColorPicker label="Accent color" name="accent" value="#3b82f6" />
```

## Add

```sh
npx @ava-ui/apogee add color-picker
```

Pulls in `Field` (label, hint, error scaffold).

## Usage

```astro
---
import ColorPicker from '@/components/apogee/ColorPicker/ColorPicker.astro'
---
<ColorPicker label="Accent color" name="accent" value="#3b82f6" />
```

## Anatomy

-   **Label / hint / error** — from `Field`.
-   **Swatch** — native `<input type="color">`; opens the system color picker.
-   **Hex field** — text input, submits under `name`; kept in sync with the swatch.

## API

`@composable` — exposed to Content Editors in AVA. Any other attribute passes through to the swatch (`<input type="color">`) — e.g. `disabled`, or an `aria-label` override (it defaults to `"Color picker"`).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | auto-generated | Id for the hex field; wires `Field`’s label and `aria-describedby`. |
| `label` | `string` | — | Field label. |
| `hint` | `string` | — | Helper text under the control; it stays when `error` is set. |
| `error` | `string` | — | Error text; sets `aria-invalid` on the hex field and renders via `role="alert"`. |
| `name` | `string` | — | Name attribute for the hex text field (this is what submits). |
| `value` | `string` | `'#3b82f6'` | Initial colour for both inputs. A form default, not a style. |
| `required` | `boolean` | `false` | Marks the hex field (the one that submits) required. |
| `swatches` | `string[]` | — | Preset colours as selectable swatches (native radios); picking one sets both inputs. |
| `presets` | `string[]` | — | Suggestions for the native picker via `<datalist>` (zero JS; Chromium shows them in its popup). |
| `eyedropper` | `boolean` | `false` | A button that samples a colour from the screen; removed where the browser has no EyeDropper. |
| `class` | `string` | — | Extra classes, passed to the `Field` wrapper. |

## States

-   **Default** — swatch + hex field, in sync.
-   **Focus** — the hex field shows a visible `focus-visible` ring; the swatch uses the browser’s native focus indicator.
-   **Hint** — helper text under the field.
-   **Error** — hex field border turns `--border-critical`, `aria-invalid="true"`, and the error text shows under the hint.

```astro
<ColorPicker label="Brand color" hint="Used for buttons and links." />
<ColorPicker label="Brand color" value="#ff0000zz" error="Enter a valid hex color (#RRGGBB)." />
```

## Swatches, presets and eyedropper

```astro
<ColorPicker label="Label colour" name="label" swatches={['#0f766e', '#2563eb', '#7c3aed', '#db2777', '#ea580c']} eyedropper />
<ColorPicker label="Accent" presets={['#0f766e', '#2563eb', '#db2777']} />
```

## Examples

Without a label (still pass one where possible — see Accessibility):

```astro
<ColorPicker name="highlight" value="#22c55e" />
```

Required field, wired into a form:

```astro
<ColorPicker label="Theme color" name="theme" required />
```

## Accessibility

| Key | Action |
| --- | --- |
| `Tab` | Moves from the swatch to the hex field, the eyedropper, then the presets. |
| `Enter` `Space` | Open the system color picker from the swatch, or start the eyedropper. |
| `Esc` | Cancels the eyedropper and keeps the current color. |
| `←` `→` `↑` `↓` | Move between presets and select one. |

-   The swatch has `aria-label="Color picker"` by default — override it via a passthrough `aria-label` when the field’s `label` alone doesn’t say enough (e.g. multiple swatches on one page).
-   `hint` / `error` are wired to the hex field via `aria-describedby`; `error` also sets `aria-invalid="true"`.
-   **No JS:** the swatch and hex field still each work independently — the color picker opens natively, and the hex field still submits under `name` — they just don’t sync live with each other. See the note above.

## Tokens

| Token | Used for |
| --- | --- |
| `--border-neutral` | Swatch and hex field border |
| `--background-page` | Hex field background |
| `--foreground-neutral` | Hex field text |
| `--foreground-neutral-faded` | Hint text |
| `--border-critical` / `--foreground-critical` | Error border / error text |
| `--outline-color-focus` | Hex field focus ring |

## Do / Don’t

-   **Do** always pass `label` — a bare swatch/hex pair isn’t self-describing without one.
-   **Do** validate the submitted hex value server-side; the hex field accepts any typed text, not just a valid hex string.
-   **Don’t** rely on the swatch alone for keyboard or AT users — the hex field is the reliable keyboard path.
-   **Don’t** clear `error` without fixing the underlying value — `aria-invalid` on its own isn’t a fix.

## Content

-   Label names what the color controls (“Accent color”), not the control itself (“Color”).
-   Error copy states the expected format, e.g. “Enter a valid hex color (#RRGGBB).”

## Related

[

InputA labeled text field with hint/error messaging, wired for accessibility — plus a pure-CSS floating label.

](https://apogee.avaui.dev/docs/input)[

FieldThe labeled-field scaffold — label, control, and hint/error — wired for accessibility.

](https://apogee.avaui.dev/docs/field)[

SliderA styled native range input with a live percentage badge.

](https://apogee.avaui.dev/docs/slider)
