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
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
#abwithout seeing the error flicker. - The emit value is always lowercase hex. Output is
#rrggbbunlessalpha={true}and the parsed value has partial alpha — then it's#rrggbbaa. Opaque inputs are never padded toff. - When
alpha={false}(default),#RRGGBBAA,rgba(), andhsla()are accepted as input, but alpha is stripped on emit. - The trailing color swatch reads
committedHexonly. 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 callsform.requestSubmit()on the associated form. WithenterBehavior='commit-only', submission is suppressed.
Value ownership
- Bindable state:
bind:valueupdates only after a successful commit. Intermediate keystrokes stay local so users can type partial values like#abwithout pushing invalid state to the parent. - Explicit commit handling:
onchangefires when a successful commit changes the canonical hex. It is not forwarded to the inner native<input>. - External updates: setting
valuefrom 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)andhsl(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-submitselects 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 useenterBehavior='commit-only'and orchestraterequestSubmit(submitter)themselves.- The component contributes a form value only when
nameis set — just like a native<input>withoutname. Pressing Enter on a field without anamestill commits and submits, but no color appears inFormData. oninputis 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
ColorFieldand rendered by the innerInput. The native<input>carriesaria-invalid="true"and anaria-describedbythat references the inline error message. - When wrapped in a
FormFieldwith its ownerror="...", both error texts render and both ids appear inaria-describedbywithout collision — theInputallocates 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".
When to use
- 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.
- Letting users graze visually across a color space — use color-picker instead.
- Constraining selection to a fixed brand palette — use color-swatch-picker instead.
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.
Props
| 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>. |