Angle Slider
v0.0.10
mix mishka.ui.gen.headless angle_slider
Overview of the Phoenix and LiveView Headless Angle Slider Component
The Mishka Chelekom Headless Angle Slider is a Phoenix function component for choosing an angle on a circle, from 0 to 360 degrees. Zero points up and the value grows clockwise. It renders one div with role="slider" and the matching aria-valuemin, aria-valuemax, aria-valuenow and aria-valuetext, and it ships no styling. The AngleSlider JS hook handles pointer dragging and the arrow keys, rotates the thumb to the current angle, and keeps the hidden input and the degree label up to date.
Four parts take Tailwind classes: the root (class), thumb_layer_class, thumb_class and value_class. The hook rotates the thumb layer, so make the layer fill the root and put the thumb on its top edge. A LiveView gets the value through a form field, an event, or both.
Base UI Angle Slider example - circular dial (Phoenix headless)
Base UI Angle Slider example - circular dial (Phoenix headless)
<.angle_slider
id="baseui-angle-slider"
value={135}
step={5}
label="Angle"
class="relative block size-28 rounded-full border-2 border-neutral-300 bg-white outline-none cursor-grab focus:ring-2 focus:ring-neutral-400 data-[dragging]:cursor-grabbing dark:border-neutral-700 dark:bg-neutral-900"
thumb_layer_class="absolute inset-0"
thumb_class="absolute left-1/2 top-0 size-4 -translate-x-1/2 -translate-y-1/2 rounded-full bg-neutral-900 dark:bg-neutral-100"
value_class="absolute inset-0 grid place-items-center text-sm font-medium text-neutral-700 dark:text-neutral-200"
/>Attributes
id is required because it carries the hook. value is the starting angle in degrees, wrapped into the 0 to 360 range. step is how far one key press moves the angle and the grid that dragging snaps to. It defaults to 1. label is the accessible name and defaults to "Angle". show_value draws the degree text in the value part and defaults to true. disabled ignores pointer and keyboard input and takes the dial out of the tab order.
Events and forms
Give the slider a name and it renders a hidden input with that name. The hook writes the angle into the input and fires a bubbling input event when the pointer is released and on every key press, so a surrounding <.form phx-change> sees each change.
on_change names an event that is pushed to the server at the same two moments. The payload carries the angle as a whole number of degrees under value. The event goes to the LiveView, or to the LiveComponent that renders the slider.
<.form for={@form} id="gradient-form" phx-submit="save">
<.angle_slider
id="gradient-angle"
name="gradient[angle]"
value={@angle}
on_change="angle"
class="relative block size-28 rounded-full border-2 border-neutral-300"
thumb_layer_class="absolute inset-0"
thumb_class="absolute left-1/2 top-0 size-4 -translate-x-1/2 -translate-y-1/2 rounded-full bg-neutral-900"
/>
</.form>def handle_event("angle", %{"value" => degrees}, socket) do
{:noreply, assign(socket, :angle, degrees)}
endParts & data attributes
The root carries data-value, data-step, data-disabled and, during a drag, data-dragging. The parts are marked with data-part: thumb-layer is the element the hook rotates, thumb is the handle inside it, value is the degree label and input is the hidden field that exists when name is set. Each part also has a structural class, such as chelekom-angle-slider__thumb, that you can target from a stylesheet.
Accessibility
The component follows the WAI-ARIA slider pattern. The root takes focus with tabindex="0". Arrow Right and Arrow Up add one step, Arrow Left and Arrow Down subtract one, Home sets 0 and End sets 360 minus one step. The value wraps, so stepping down from 0 lands on the last step. aria-valuetext reads as "135 degrees", so a screen reader announces the unit.
Attributes
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
id
|
string
| — | — | Yes | Unique id (carries the AngleSlider hook) |
value
|
integer
|
0
| — | No | Current angle in degrees (0–360) |
step
|
integer
|
1
| — | No | Degrees to snap/nudge by |
name
|
string
| — | — | No | Form field name (renders a hidden input) |
on_change
|
string
| — | — | No | Event pushed with %{value: deg} on change |
disabled
|
boolean
|
false
| — | No | Disable interaction |
show_value
|
boolean
|
true
| — | No | Render a centered degree label |
label
|
string
|
Angle
| — | No | Accessible label |
class
|
any
| — | — | No | Extra classes for the root |
thumb_class
|
any
| — | — | No | Extra classes for the thumb |
thumb_layer_class
|
any
| — | — | No | — |
value_class
|
any
| — | — | No | — |
rest
|
global
| — | — | No | — |
Options
Each option takes one or more of its values, separated by commas.
| Option | Values |
|---|---|
--type
|
angle_slider
|
--module
| A module name of your own |
What it generates
mix mishka.ui.gen.headless angle_slider