Color Picker

v0.0.10

v0.0.10StableHeadlessStatelessAccessibleJSApache-2.0
Installation mix mishka.ui.gen.headless color_picker
Source View Source

Overview of the Phoenix and LiveView Headless Color Picker Component

The Mishka Chelekom Headless Color Picker lets people choose a color from a saturation and brightness area and a hue slider. It ships no styling. The root holds the area with its thumb, a controls row with a preview swatch and a native hue range input, and an optional hidden input for forms. The ColorPicker hook turns the pointer position into a hex value.

Dragging in the area sets saturation from left to right and brightness from bottom to top. The hook paints the area background and the preview, moves the thumb, writes the hex into the hidden input and keeps it in data-value on the root. In a Phoenix form the hex posts with the other fields, and in LiveView on_change pushes it to the server. Each part takes its own class attribute: class, area_class, thumb_class, controls_class, preview_class and hue_class.

Base UI Color Picker example - area, hue slider and preview (Phoenix headless)

Base UI Color Picker example - area, hue slider and preview (Phoenix headless)

heex
<.color_picker
  id="baseui-color-picker"
  value="#e8590c"
  class="w-56 space-y-3"
  area_class="relative h-36 w-full cursor-crosshair rounded-lg ring-1 ring-black/10"
  thumb_class="size-3.5 rounded-full border-2 border-white shadow ring-1 ring-black/30"
  controls_class="flex items-center gap-3"
  preview_class="size-8 shrink-0 rounded-full ring-1 ring-black/10"
  hue_class="h-3 w-full cursor-pointer appearance-none rounded-full [background:linear-gradient(to_right,#f00,#ff0,#0f0,#0ff,#00f,#f0f,#f00)]"
/>

Using the value in forms and LiveView

value is the starting color as a hex string such as #e8590c, with three or six digits. A hex string of any other length falls back to a blue. Give the picker a name and it renders a hidden input with the current hex, so the color posts with its form. The hook dispatches a bubbling input event from that input when the color changes, so phx-change on the form fires. Use form to attach the input to a form elsewhere on the page.

Set on_change to a LiveView event name and the hook pushes it with a payload like %{"value" => "#8b5cf6"} as the color changes. The event goes to the LiveView, or to the LiveComponent the picker is rendered in. If the server renders a different value later, the picker adopts it.

heex
<.form for={@form} phx-submit="save">
  <.color_picker
    id="brand-color"
    name="theme[color]"
    value={@color}
    on_change="pick"
  />
</.form>
elixir
def handle_event("pick", %{"value" => hex}, socket) do
  {:noreply, assign(socket, :color, hex)}
end

Parts & data attributes

Each part carries a stable data-part and a chelekom-color-picker__* class you can target. The root also gets data-dragging while the pointer is down in the area.

Partdata-partState / ARIAPurpose
Rootrootdata-value, data-draggingCarries the ColorPicker hook. data-value is the current hex.
Areaarea-The saturation and brightness surface. Press and drag to pick. The hook sets its background.
Area thumbarea-thumb-The handle inside the area, placed by the hook.
Controlscontrols-Wraps the preview and the hue input.
Previewpreviewrole="img", aria-labelA swatch painted with the current color.
Huehuetype="range", min 0, max 360, aria-label="Hue"A native range input for the hue.
Inputinput-Hidden hex value for form submission, rendered only when name is set.

Accessibility

There is no formal WAI-ARIA pattern for a color picker. The hue control is a native range input named "Hue", so the arrow keys, Home and End work on it. The area responds to the pointer only: the hook gives it no keyboard handling. The preview is an image with an aria-label that names the value the server rendered, so it changes when the server sends a new value and not while the user drags. If people need to enter an exact color, offer another input for it.