Overview of the Phoenix and LiveView Headless Menubar Component
The Mishka Chelekom Headless Menubar
ships zero opinionated styling
- only the semantic markup, full WAI-ARIA
wiring, roving tabindex across the top-level menus, and the defining menubar
behaviour - switching the open menu when you hover or arrow to another trigger while
one is already open. Every part - menu, trigger
and popup
- is addressable through a
*_class
attribute, so you style it entirely with your own Tailwind
classes while the accessibility and behavior keep working underneath.
Base UI Menubar example (Phoenix headless)
<.menubar id="baseui-menubar-hero" class="flex items-center" menu_class="relative" trigger_class="h-8 border-0 bg-transparent px-3 text-sm font-normal text-neutral-950 select-none data-[popup-open]:bg-neutral-100 data-[pressed]:bg-neutral-100 data-[disabled]:text-neutral-500 dark:text-white dark:focus-visible:bg-neutral-800 dark:data-[popup-open]:bg-neutral-800 dark:data-[pressed]:bg-neutral-800 dark:data-[disabled]:text-neutral-400 focus-visible:bg-neutral-100 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-neutral-950 focus-visible:relative dark:focus-visible:outline-white" popup_class="absolute left-0 top-full mt-1 z-10 origin-[var(--transform-origin)] border border-neutral-950 bg-white py-1 text-neutral-950 shadow-[0.25rem_0.25rem_0] shadow-black/12 outline-none transition-[scale,opacity] duration-100 ease-out data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0 data-[instant]:transition-none data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0 data-[closed]:hidden dark:border-white dark:bg-neutral-950 dark:text-white dark:shadow-none" > <:menu label="File"> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-4 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > New </button> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-4 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Open </button> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-4 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Save </button> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-2 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Export <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style="display: block;"> <path d="M6 12V4l4.5 4z" /> </svg> </button> <div data-part="separator" role="separator" class="mx-1 my-1 h-px bg-neutral-950 dark:bg-white" > </div> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-4 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Print </button> </:menu> <:menu label="Edit"> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-4 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Cut </button> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-4 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Copy </button> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-4 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Paste </button> </:menu> <:menu label="View"> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-4 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Zoom In </button> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-4 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Zoom Out </button> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-2 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Layout <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style="display: block;"> <path d="M6 12V4l4.5 4z" /> </svg> </button> <div data-part="separator" role="separator" class="mx-1 my-1 h-px bg-neutral-950 dark:bg-white" > </div> <button type="button" role="menuitem" class="flex w-full cursor-default items-center justify-between gap-4 py-2 pr-4 pl-4 text-left text-sm leading-4 font-normal outline-none select-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-white data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:bg-neutral-950 data-[highlighted]:before:content-[''] dark:data-[highlighted]:text-neutral-950 dark:data-[highlighted]:before:bg-white" > Full Screen </button> </:menu> <:menu label="Help" disabled></:menu> </.menubar>
Parts & data attributes
Each part carries a stable
data-part
and a structural
chelekom-menubar__*
class you can target, plus paired-presence state for open/closed menu popups.
Accessibility
Follows the WAI-ARIA Menubar pattern : arrow keys move focus between top-level menus along the configured orientation (looping past the ends when enabled), Home/End jump to the first/last menu, Enter/Space or a cross-axis arrow opens a menu, Escape closes it, and once a menu is open, hovering or arrowing to another trigger switches the open menu. Arrow Up/Down move between items inside an open menu.