# ColorField

Popover-wrapped colour selector — a pill trigger with a circular icon-swatch
and a label that opens a `ColorPicker` in a dialog popover. This is the
reusable field-level control for colour inputs; the picker itself is the panel.

```tsx
import { ColorField } from '@tangible/ui';
```

Interactive docs and examples: [ColorField in Storybook](https://storybook-tangible-ui.pages.dev/?path=/docs/forms-colorfield--docs).

## ColorField props

| Prop | Type | Default | Description |
| - | - | - | - |
| `align?` | `"center" \| "end" \| "start"` | `'start'` | Popover alignment. |
| `alpha?` | `boolean` | `true` | |
| `aria-label?` | `string` | | |
| `aria-labelledby?` | `string` | | Identifies the element (or elements) that labels the current element. |
| `className?` | `string` | | |
| `defaultFormat?` | `"hex" \| "hsl" \| "hwb" \| "oklch" \| "rgb"` | `'hex'` | |
| `defaultOpen?` | `boolean` | | |
| `defaultValue?` | `string` | `'#000000'` | Uncontrolled initial value. |
| `disabled?` | `boolean` | | |
| `dynamic?` | `boolean` | `false` | Dynamic-data treatment — a neutral swatch with a bolt icon, for values bound to dynamic sources rather than a fixed colour. |
| `format?` | `"hex" \| "hsl" \| "hwb" \| "oklch" \| "rgb"` | | |
| `formats?` | `ColorFormat[]` | `all` | Formats offered in the picker's format selector, in order. |
| `gradient?` | `boolean` | `false` | Offer a Gradient tab beside the solid picker. The value may then also be a CSS gradient string; the active tab is derived from the value, and switching tabs converts it. Solid → gradient is benign (a two-stop gradient of the colour, or the session's last gradient restored); gradient → solid is DESTRUCTIVE — the gradient collapses to its first stop's colour (recoverable only while the popover stays open). |
| `icon?` | `IconName` | ``'system/edit-small' ('system/bolt-fill' when `dynamic`)`` | Override the swatch icon. |
| `id?` | `string` | | |
| `label?` | `React.ReactNode` | | Human-readable name shown on the trigger (e.g. "Primary"). Falls back to the current value / variable name when omitted. |
| `labels?` | `ColorFieldLabels` | | Override any user-facing string for i18n (picker strings + `trigger`). |
| `onFormatChange?` | `(format: ColorFormat) => void` | | |
| `onOpenChange?` | `(open: boolean) => void` | | |
| `onValueChange?` | `(value: string) => void` | | |
| `open?` | `boolean` | | |
| `showLabel?` | `boolean` | `true` | Show the label text beside the swatch. |
| `showSwatch?` | `boolean` | `true` | Show the icon-swatch before the label. Opt out for a text-only trigger (a dense settings row where the value text is the affordance). Hiding the swatch also hides the `dynamic` bolt and any `icon` — the accessible name still carries the dynamic state. With both this and `showLabel` off the trigger would be empty, so the label is kept and a dev warning fires. |
| `side?` | `"bottom" \| "left" \| "right" \| "top"` | `'bottom'` | Popover side relative to the trigger. |
| `size?` | `"lg" \| "md" \| "sm"` | `'md'` | |
| `value?` | `string` | | Controlled value — a CSS colour, a `var(--x[, fallback])` reference, or (with `gradient`) a CSS gradient string. |
| `variant?` | `"bare" \| "field"` | `'field'` | Trigger chrome. `bare` removes the field box and sizes the swatch to the control height (an IconButton-shaped tile), keeping the focus ring and the swatch's own edge. Composes with `showLabel={false}` for a swatch-only tile, or with a label for a chrome-less swatch + text row. |

Also accepts the standard attributes of `ButtonHTMLAttributes` (`data-*`, `aria-*`, event handlers), passed to the element it renders.

## Examples

- [Labelled rows](https://storybook-tangible-ui.pages.dev/?path=/story/forms-colorfield--field-labelled): Compose with `Field` for row labels — `Field.Control` wires `aria-labelledby` (and `id`) onto the trigger. Pass a human-readable `label` (e.g. "Primary") for the pill text instead of the raw value. Set `dynamic` for values bound to dynamic data — a neutral swatch with a bolt icon. The swatch icon is contrast-picked so it stays legible on any colour.

- [Label only](https://storybook-tangible-ui.pages.dev/?path=/story/forms-colorfield--field-label-only): `showSwatch={false}` drops the icon-swatch for a text-only trigger. Note the `dynamic` bolt goes with it — the accessible name still says "(dynamic)". Turning both slots off is guarded (the label is kept and a dev warning fires) — see the `showSwatch` prop docs.

- [Bare (swatch tiles)](https://storybook-tangible-ui.pages.dev/?path=/story/forms-colorfield--field-bare): `variant="bare"` drops the field box so the swatch is the control, sized to the control height like an `IconButton` of the same `size` (a bare `sm` tile is 32px, clearing the 24px target minimum). Built for colour-ladder strips and swatch grids; pair with `showLabel={false}` for tiles, or keep the label for a chrome-less swatch + text row.

- [Gradient mode (Solid | Gradient tabs)](https://storybook-tangible-ui.pages.dev/?path=/story/forms-colorfield--gradient-mode): With `gradient`, the popover gains Solid | Gradient tabs. The active tab derives from the value; switching converts it (solid → two-stop gradient of itself, gradient → first stop). The trigger swatch renders the gradient.

- [Gradient mode, solid value](https://storybook-tangible-ui.pages.dev/?path=/story/forms-colorfield--gradient-mode-solid-start): The same field starting from a solid value — the Solid tab is active until the user switches, which converts the colour into a two-stop gradient of itself.

Other stories: ColorField (popover), Swatch only, Alpha values, var() value, Gradient swatch triggers ([interactive page](https://storybook-tangible-ui.pages.dev/?path=/docs/forms-colorfield--docs)).

Component API tokens: shared with [ColorPicker](https://storybook-tangible-ui.pages.dev/docs/components/colorpicker.md).

---

Interactive version: https://storybook-tangible-ui.pages.dev/?path=/docs/forms-colorfield--docs
