Floating Indicator

v0.0.10

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

Overview of the Phoenix and LiveView Headless Floating Indicator Component

The Mishka Chelekom Headless Floating Indicator is a Phoenix function component that draws one highlight box and slides it over whichever target is active, for segmented switches and tab-like strips in LiveView. The root is a div with role="group" that holds the indicator span and one native button per target. Each target has aria-pressed.

A FloatingIndicator hook measures the active target and moves the indicator onto it with a transform, a width and a height. It listens for clicks and watches for resizes with a ResizeObserver, so the highlight stays aligned when the layout changes. Style the parts with class, indicator_class and target_class.

Base UI Floating Indicator example - segmented switch (Phoenix headless)

Base UI Floating Indicator example - segmented switch (Phoenix headless)

heex
<.floating_indicator
  id="baseui-floating-indicator"
  active="day"
  label="Range"
  class="relative inline-flex gap-1 rounded-lg bg-neutral-100 p-1 dark:bg-neutral-800"
  indicator_class="absolute left-0 top-0 rounded-md bg-white shadow transition-all duration-200 ease-out motion-reduce:transition-none dark:bg-neutral-600"
  target_class="relative z-10 rounded-md px-3 py-1.5 text-sm font-medium text-neutral-500 transition-colors focus-visible:outline-2 focus-visible:outline-neutral-950 aria-pressed:text-neutral-900 dark:focus-visible:outline-white dark:aria-pressed:text-white"
>
  <:target value="day">Day</:target>
  <:target value="week">Week</:target>
  <:target value="month">Month</:target>
</.floating_indicator>

Attributes, slots and events

id is required because it carries the hook. active is the value of the target that starts as active. When it is missing or matches no target, the first target is used. label is the aria-label of the group.

Each :target entry needs a value, and its content becomes the content of the button. Clicking a target moves the indicator to it, sets data-active and aria-pressed="true" on it, and sets aria-pressed="false" on the others. on_change is a LiveView event name. The hook pushes it with %{"value" => "week"} after a click.

The server can drive the indicator as well: render the component with a different active and the highlight moves. If your LiveView re-renders the component, keep active equal to the current value by setting it in the on_change handler, or the patch can put the highlight back on the old target.

Parts & data attributes

Each part carries a stable data-part and a structural chelekom-floating-indicator__* class you can target.

Partdata-partState / ARIAPurpose
Root-role="group", aria-label, data-active, data-on-changeCarries the hook. It must be positioned.
Indicatorindicatoraria-hidden="true", inline transform, width and height set by the hookThe moving highlight.
Targettargettype="button", data-value, data-active, aria-pressedOne option. It is a native button.

Accessibility

There is no formal ARIA pattern for this control. The targets are native buttons with aria-pressed, so Tab moves between them and Enter or Space selects one. The group is named by label, and the indicator is aria-hidden. Give the targets a visible focus style, as the example does with focus-visible, because the highlight shows the active target, not the focused one.