Phoundry UI

Roving TabIndex

Manages tabindex for keyboard navigation within a group of items. Only the focused item is tabbable; arrow keys move focus between items.

import { createRovingTabIndex } from 'phoundry-ui';

Horizontal Toolbar

Tab into the toolbar, then use to navigate. Focused: Bold

Show code
<script lang="ts">
  import { createRovingTabIndex } from 'phoundry-ui';

  let focused = $state(0);
  const roving = createRovingTabIndex({
    orientation: 'horizontal',
    loop: true,
    onFocus: (_el, idx) => (focused = idx),
  });

  const items = ['Bold', 'Italic', 'Underline', 'Strikethrough', 'Code'];
</script>

<div use:roving.action class="flex gap-1" role="toolbar">
  {#each items as item, i}
    <button
      data-roving-item
      class="px-3 py-1.5 rounded text-xs"
      class:bg-accent-primary={focused === i}
    >
      {item}
    </button>
  {/each}
</div>

Both axes (grid)

orientation="both" wires ↑↓←→; loop=false stops wrapping at edges — good for spatial palettes.

Focused: A1

Show code
const roving = createRovingTabIndex({
  orientation: 'both',
  loop: false,
  itemSelector: '[data-roving-item]',
});

<div use:roving.action class="grid grid-cols-2 gap-1" role="group">
  {#each cells as cell}
    <button type="button" data-roving-item class="rounded px-2 py-1 text-xs">{cell}</button>
  {/each}
</div>

Vertical Menu

Use to navigate. Home / End jump to first/last.

Show code
<div use:roving.action class="flex flex-col" role="menu">
  {#each items as item}
    <button role="menuitem" class="px-3 py-2 text-left text-xs rounded hover:bg-surface-raised">
      {item}
    </button>
  {/each}
</div>

RovingTabIndexOptions

PropTypeDefaultDescription
orientation 'horizontal' | 'vertical' | 'both''vertical'Arrow key direction for navigation.
itemSelector string[role] / [data-roving-item]CSS selector for focusable items within the container.
loop booleantrueWrap around when reaching the start or end.
onFocus (element, index) => voidCalled when an item receives focus.

Return Value

PropTypeDefaultDescription
action ActionSvelte action to apply to the container element via use:roving.action.
focusItem(index) (number) => voidProgrammatically focus a specific item by index.
focusedIndex number (readonly)The currently focused item index.

Usage tips

  • Items are matched by [role="tab"], [role="menuitem"], [role="option"], [role="treeitem"], or [data-roving-item] by default.
  • Disabled items (disabled or aria-disabled="true") are skipped.
  • Use for toolbars, radio groups, tab lists, and menu navigation patterns.
  • Home and End keys are supported for jumping to first/last items.
  • Call focusItem(index) to programmatically move focus (e.g., after adding/removing items).