Forms

ColorField

Text input that validates and normalizes hex, rgb(), and hsl() color strings into a canonical hex value emitted on blur.

import { ColorField } from '@lostgradient/cinder/color-field';
formcolor
01

Overview

A text input that validates and normalizes hex, rgb(), and hsl() color strings into a canonical hex value emitted on blur. Compose it with FormField for label and external-error display, and pair it with ColorPicker when users need both visual selection and exact entry.

Usage

svelte
<script lang="ts">
  import ColorField from '@lostgradient/cinder/color-field';
  import FormField from '@lostgradient/cinder/form-field';

  let color = $state('#3366ff');
</script>

<FormField id="accent" label="Accent color">
  <ColorField id="accent" value={color} onchange={(next) => (color = next)} />
</FormField>

value is bindable. Use bind:value when parent state should track the committed canonical hex value, or pass value with onchange when you want to persist commits explicitly.

Behavior

  • Validation happens on blur, not on every keystroke, so users can type intermediate values like #ab without seeing the error flicker.
  • The emit value is always lowercase hex. Output is #rrggbb unless alpha={true} and the parsed value has partial alpha — then it's #rrggbbaa. Opaque inputs are never padded to ff.
  • When alpha={false} (default), #RRGGBBAA, rgba(), and hsla() are accepted as input, but alpha is stripped on emit.
  • The trailing color swatch reads committedHex only. It never reflects unparsed text, so a malformed string never paints arbitrary content into the DOM.
  • Pressing Enter commits the value. With enterBehavior='commit-then-submit' (default), the field then calls form.requestSubmit() on the associated form. With enterBehavior='commit-only', submission is suppressed.

Value ownership

  • Bindable state: bind:value updates only after a successful commit. Intermediate keystrokes stay local so users can type partial values like #ab without pushing invalid state to the parent.
  • Explicit commit handling: onchange fires when a successful commit changes the canonical hex. It is not forwarded to the inner native <input>.
  • External updates: setting value from the parent reconciles the visible text. Invalid external strings remain visible and raise the parse error instead of silently clearing.

Form participation

The component renders a single sibling <input type="hidden"> that serves two purposes. When name is set, that input carries the name attribute and mirrors the current committed hex so the value participates in native form submission. When name is not set, the same input still renders (without a name) and acts purely as the anchor used to attach a reset listener to the surrounding form. Either way, uncontrolled fields revert to value on form reset (no onchange is fired; reset is observable through native form events). Controlled fields do nothing on reset internally; the parent's reset handler updates value and the effect reconciles.

Parse errors propagate to the visible <input> via setCustomValidity, so invalid text participates in HTML constraint validation whether the user pressed Enter or clicked a submit button.

Moving the component across forms at runtime is not supported in v1.

Limitations

  • Modern rgb(r g b / a) and hsl(h s l / a) slash syntax is rejected by the parser. Only legacy comma-separated forms parse.
  • rgb() percent components are rounded to the nearest 0–255 byte.
  • commit-then-submit selects the first non-disabled [type="submit"] (or unmarked <button>) in document order, matching the common case but not every native browser nuance. Forms needing full fidelity should use enterBehavior='commit-only' and orchestrate requestSubmit(submitter) themselves.
  • The component contributes a form value only when name is set — just like a native <input> without name. Pressing Enter on a field without a name still commits and submits, but no color appears in FormData.
  • oninput is not exposed. The component owns the blur-time commit pipeline; intermediate keystrokes are intentionally not surfaced as a value callback.

Errors and accessibility

  • Parse errors are owned by ColorField and rendered by the inner Input. The native <input> carries aria-invalid="true" and an aria-describedby that references the inline error message.
  • When wrapped in a FormField with its own error="...", both error texts render and both ids appear in aria-describedby without collision — the Input allocates a distinct id when its own error would collide with the context's error id.
  • The trailing swatch is decorative: it is aria-hidden="true".
Live preview
02

When to use

Use when
  • Accepting an exact color value via keyboard entry, including pasted hex, rgb(), or hsl() strings.
  • Pairing with color-picker for combined visual selection and text-based entry.
Avoid when
  • Letting users graze visually across a color space — use color-picker instead.
  • Constraining selection to a fixed brand palette — use color-swatch-picker instead.
03

Examples

Basic color field

An uncontrolled color field that accepts hex, rgb(), and hsl() strings and normalizes them to hex on commit.

Disabled and readonly states

A disabled color field rejects all interaction; a readonly field displays the value but blocks editing.

04

Props

Props for color-field
Name Type Default Required Bindable Description
id text req Inner <input> id. Required (mirrors Input).
value text '' bind Bindable value as a hex string. Accepts any color string the configured formats allow when set externally.
alpha boolean false Accept and emit alpha when the parsed value has partial alpha. When false (default), #RRGGBBAA and rgba()/hsla() inputs are parsed but alpha is stripped on emit.
formats readonly ColorFieldFormat[] ["hex","rgb","hsl"] Accepted *input* formats. Defaults to ['hex', 'rgb', 'hsl']. Output is always hex.
disabled boolean false Disable the input.
required boolean false Mark the input as required for form submission and a11y.
readonly boolean false Render the inner <input> as read-only.
name text Form field name. When set, the hidden mirror input contributes the current committed hex value to native form submission.
placeholder text Placeholder text for the inner <input>.
errorMessage text Override the default parse-failure error message.
enterBehavior 'commit-then-submit' | 'commit-only' 'commit-then-submit' Commit-on-Enter behavior. Default 'commit-then-submit': - 'commit-then-submit': Enter commits the value, then lets the form's native submission proceed via requestSubmit. - 'commit-only': Enter commits and preventDefault()s, suppressing form submission (useful in dialogs / multi-field flows where Enter must not submit).
onchange (value: string) => void Fires on successful blur-time commit when the canonical hex actually changes. Value callback by repo convention — not forwarded to the inner native <input>.