Color Input
v0.0.10
mix mishka.ui.gen.headless color_input
Overview of the Phoenix and LiveView Headless Color Input Component
The Mishka Chelekom Headless Color Input is an unstyled Phoenix function component made of a hex text field, a swatch and a button that opens a color picker in a popover. It reuses the engine of the Headless Color Picker. A saturation and brightness area and a hue slider sit in a panel that is toggled with Phoenix LiveView JS commands, so the panel needs no extra hook. The swatch and the text field stay visible in a control row.
Typing a valid hex value updates the picker, and moving the picker updates the field. A hidden input carries the hex for form submission and fires an input event, so a surrounding form with phx-change sees every change. Each part takes a *_class attribute: control_class, preview_class, text_class, trigger_class, panel_class, area_class, thumb_class, controls_class and hue_class.
Base UI Color Input example - hex field with a picker popover (Phoenix headless)
Base UI Color Input example - hex field with a picker popover (Phoenix headless)
<.color_input
id="baseui-color-input"
value="#0ea5e9"
label="Color"
class="relative inline-block w-56"
control_class="flex items-center gap-2 rounded-lg border border-neutral-300 bg-white px-2 py-1.5 focus-within:ring-2 focus-within:ring-neutral-400 dark:border-neutral-700 dark:bg-neutral-900"
preview_class="size-5 shrink-0 rounded border border-neutral-300 dark:border-neutral-600"
text_class="w-24 bg-transparent font-mono text-sm text-neutral-900 outline-none dark:text-neutral-100"
trigger_class="ml-auto rounded p-1 text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-800"
panel_class="absolute left-0 z-20 mt-2 w-56 rounded-lg border border-neutral-200 bg-white p-3 shadow-lg dark:border-neutral-700 dark:bg-neutral-800"
area_class="relative h-36 w-full cursor-crosshair rounded-md"
thumb_class="block size-3 rounded-full border-2 border-white shadow"
hue_class="mt-3 w-full"
/>Using it in a form
Pass name and the component renders a hidden input with that name. It always holds the current color as #rrggbb. form takes the id of a form when the component sits outside it. Because the hidden input fires input, phx-change on the form runs while the user drags in the area, moves the hue or types a hex.
<.form for={@form} id="color-form" phx-change="validate" phx-submit="save">
<.color_input
id="brand-color"
name="color_demo[color]"
value={@color}
label="Brand color"
/>
<button type="submit">Save</button>
</.form>def handle_event("validate", %{"color_demo" => %{"color" => color}}, socket) do
{:noreply, assign(socket, color: color, form: to_form(%{"color" => color}, as: :color_demo))}
end
def handle_event("save", %{"color_demo" => %{"color" => color}}, socket) do
{:noreply, assign(socket, :saved, color)}
endValues and events
value is the starting color as a hex string and defaults to #3b82f6. The text field accepts three or six digits, with or without the #. Text that is not a valid hex is ignored until it parses. The hidden input and on_change always carry the color as lowercase #rrggbb. label becomes the aria-label of the text field and defaults to Hex color.
on_change names a LiveView event. It is pushed with %{"value" => "#rrggbb"} each time the user changes the color in the area, with the hue slider or by typing a valid hex. It goes to the LiveView or LiveComponent that renders the input. If the server later sends a different value, the picker adopts it.
Parts & data attributes
The root carries data-part="root", data-value with the current hex, data-on-change, and data-dragging while the pointer is down in the area. The control row holds preview (a span with role="img"), text (the hex field) and trigger (a button). The panel holds the area, its area-thumb, and a controls element with the hue range input, which runs from 0 to 360. The hidden input exists only when you pass name.
The panel is closed until the trigger is pressed. The trigger toggles it with Phoenix.LiveView.JS and keeps aria-expanded in step. Clicking outside the component or pressing Escape closes it.
Accessibility
The trigger is a native button with aria-haspopup="dialog", aria-expanded and aria-controls pointing at the panel, and the panel has role="dialog". The swatch has role="img" and an aria-label with the color it was rendered with. The hue slider is a native range input, so the arrow keys work. The saturation and brightness area responds to the pointer only. Keyboard users can set a color by typing a hex value in the text field or moving the hue slider. This follows the WAI-ARIA Disclosure pattern.
Attributes
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
id
|
string
| — | — | Yes | Unique id (carries the ColorPicker hook) |
name
|
string
| — | — | No | Name for the hidden hex input |
value
|
string
|
#3b82f6
| — | No | Initial color (hex) |
form
|
string
| — | — | No | Form id owning the hidden input |
on_change
|
string
| — | — | No | LiveView event pushed on change ({value}) |
label
|
string
| — | — | No | Accessible label for the hex field |
class
|
any
| — | — | No | — |
control_class
|
any
| — | — | No | — |
preview_class
|
any
| — | — | No | — |
text_class
|
any
| — | — | No | — |
trigger_class
|
any
| — | — | No | — |
panel_class
|
any
| — | — | No | — |
area_class
|
any
| — | — | No | — |
thumb_class
|
any
| — | — | No | — |
hue_class
|
any
| — | — | No | — |
controls_class
|
any
| — | — | No | — |
rest
|
global
| — | — | No | — |
Options
Each option takes one or more of its values, separated by commas.
| Option | Values |
|---|---|
--type
|
color_input
|
--module
| A module name of your own |
What it generates
mix mishka.ui.gen.headless color_input