Skip to content
Kappa
Toolbar
@dicehub/kappav0.4.2

Toolbar

Groups actions, links, and fields into a keyboard-friendly control surface.

<script setup>
import { Toolbar } from "@dicehub/kappa/components/toolbar";
import { InputGroup } from "@dicehub/kappa/components/input-group";
import { ListFilter, Search, Settings2 } from "@lucide/vue";
</script>

<template>
  <Toolbar aria-label="Record tools" class="record-toolbar">
    <Toolbar.InputGroup aria-label="Search records" class="record-toolbar__search">
      <InputGroup.Addon><Search aria-hidden="true" /></InputGroup.Addon>
      <InputGroup.Input placeholder="Search records" />
    </Toolbar.InputGroup>
    <Toolbar.Button :icon="ListFilter" aria-label="Filter" />
    <Toolbar.Button :icon="Settings2" aria-label="Settings" />
  </Toolbar>
</template>

<style scoped>
.record-toolbar { width: min(100%, 28rem); }
.record-toolbar__search { flex: 1 1 auto; }
</style>

Installation

Toolbar is part of the main Kappa package and has no runtime dependency beyond Vue.

Barrel

import { Toolbar } from "@dicehub/kappa";

Granular

import { Toolbar } from "@dicehub/kappa/components/toolbar";

Usage

Use Toolbar when several controls must read as one compact action or filter group. Give the root a short accessible name. Use Toolbar.Button, Toolbar.Link,Toolbar.Input, and Toolbar.InputGroup for grouped presentation and shared keyboard movement.

<script setup>
import { Toolbar } from "@dicehub/kappa/components/toolbar";
import { InputGroup } from "@dicehub/kappa/components/input-group";
import { ListFilter, Search } from "@lucide/vue";
</script>

<template>
  <Toolbar aria-label="Record tools">
    <Toolbar.InputGroup aria-label="Search records">
      <InputGroup.Addon><Search aria-hidden="true" /></InputGroup.Addon>
      <InputGroup.Input placeholder="Search records" />
    </Toolbar.InputGroup>
    <Toolbar.Button :icon="ListFilter" aria-label="Filter" />
  </Toolbar>
</template>

Composition

Typical part hierarchy
Toolbar.Root
├── Toolbar.Button
├── Toolbar.Separator
├── Toolbar.Link
├── Toolbar.Input
└── Toolbar.InputGroup

The root renders a native div with role="toolbar". Supported parts inherit the root size and mark one focusable control as the tab stop. A native button, link, input, or select may also be composed directly and is discovered for keyboard movement.

  • Toolbar.Button uses quiet button styling and defaults to type="button".
  • Toolbar.Link preserves link semantics and uses Kappa LinkButton behavior.
  • Each focusable control inside Toolbar.InputGroup participates in toolbar focus movement.
  • Wrap Toolbar.Button with Select.Trigger as-child to add a Select without changing its popup behavior.
  • Wrap Toolbar.Input with Combobox.TriggerInput as-child to add editable Combobox behavior.
  • Toolbar.Separator is decorative and defaults to the toolbar cross-axis.
  • Set orientation="vertical" for a stacked toolbar. Set loopFocus="false" to stop at either edge.

Examples

Select

Wrap a Toolbar.Button with Select.Trigger and as-child to keep Select behavior in the grouped control.

<script setup>
import { Toolbar } from "@dicehub/kappa/components/toolbar";
import { Select } from "@dicehub/kappa/components/select";
import { ListFilter, Settings2 } from "@lucide/vue";

const sortItems = ["Name", "Created date", "Status"];
</script>

<template>
  <Toolbar aria-label="Filter and sort records">
    <Toolbar.Button :icon="ListFilter">Filter</Toolbar.Button>
    <Select
      id="toolbar-sort-records"
      aria-label="Sort records"
      :default-value="['Name']"
      :items="sortItems"
      :positioning="{ sameWidth: false }"
    >
      <Select.Trigger as-child>
        <Toolbar.Button><Select.ValueText /></Toolbar.Button>
      </Select.Trigger>
      <Select.Positioner>
        <Select.Content class="sort-menu">
          <Select.List>
            <template #default="{ item }">
              <Select.Item :item="item">{{ item }}</Select.Item>
            </template>
          </Select.List>
        </Select.Content>
      </Select.Positioner>
    </Select>
    <Toolbar.Button :icon="Settings2" aria-label="View settings" />
  </Toolbar>
</template>

<style scoped>
.sort-menu { min-width: 10rem; }
</style>

Combobox

Use Combobox.TriggerInput with as-child to make a Toolbar.Input an editable popup trigger.

<script setup>
import { Toolbar } from "@dicehub/kappa/components/toolbar";
import { Combobox } from "@dicehub/kappa/components/combobox";
import { ListFilter, Settings2 } from "@lucide/vue";

const statusItems = ["All records", "Active", "Paused", "Failed"];
</script>

<template>
  <Toolbar aria-label="Filter record status" class="status-toolbar">
    <Toolbar.Button :icon="ListFilter">Status</Toolbar.Button>
    <Combobox
      id="toolbar-filter-status"
      :default-value="['All records']"
      :items="statusItems"
    >
      <Combobox.TriggerInput as-child>
        <Toolbar.Input
          aria-label="Filter status"
          class="status-toolbar__input"
          placeholder="Filter status…"
        />
      </Combobox.TriggerInput>
      <Combobox.Content>
        <Combobox.Empty>No matching status.</Combobox.Empty>
        <Combobox.List>
          <template #default="{ item }">
            <Combobox.Item :item="item">{{ item }}</Combobox.Item>
          </template>
        </Combobox.List>
      </Combobox.Content>
    </Combobox>
    <Toolbar.Button :icon="Settings2" aria-label="Status settings" />
  </Toolbar>
</template>

<style scoped>
.status-toolbar { width: min(100%, 28rem); }
.status-toolbar__input { flex: 1 1 auto; }
</style>

Input shorthand

Use Toolbar.Input for simple text input that does not need an addon or suffix.

<script setup>
import { Toolbar } from "@dicehub/kappa/components/toolbar";
import { ListFilter, Settings2 } from "@lucide/vue";
</script>

<template>
  <Toolbar aria-label="Search records" class="search-toolbar">
    <Toolbar.Input
      aria-label="Search records"
      class="search-toolbar__input"
      placeholder="Search records"
    />
    <Toolbar.Button :icon="ListFilter" aria-label="Filter" />
    <Toolbar.Button :icon="Settings2" aria-label="Settings" />
  </Toolbar>
</template>

<style scoped>
.search-toolbar { width: min(100%, 28rem); }
.search-toolbar__input { flex: 1 1 auto; }
</style>

Input group

Use Toolbar.InputGroup when one toolbar item needs an inline addon or suffix.

<script setup>
import { Toolbar } from "@dicehub/kappa/components/toolbar";
import { InputGroup } from "@dicehub/kappa/components/input-group";
</script>

<template>
  <Toolbar aria-label="Open a site" class="site-toolbar">
    <Toolbar.InputGroup aria-label="Site address" class="site-toolbar__input">
      <InputGroup.Input aria-label="Site subdomain" placeholder="docs" />
      <InputGroup.Addon align="inline-end">
        <InputGroup.Text>.example.com</InputGroup.Text>
      </InputGroup.Addon>
    </Toolbar.InputGroup>
    <Toolbar.Button>Open</Toolbar.Button>
  </Toolbar>
</template>

<style scoped>
.site-toolbar { width: min(100%, 32rem); }
.site-toolbar__input { flex: 1 1 auto; }
</style>

Sizes

Set size on the root to give every supported toolbar item the same density.

xs
sm
base
lg
<script setup>
import { Toolbar, type ToolbarSize } from "@dicehub/kappa/components/toolbar";

const sizes: ToolbarSize[] = ["xs", "sm", "base", "lg"];
</script>

<template>
  <div class="toolbar-sizes">
    <div v-for="size in sizes" :key="size" class="toolbar-size-row">
      <span class="toolbar-size-label">{{ size }}</span>
      <Toolbar :aria-label="`${size} toolbar`" :size="size">
        <Toolbar.Input :aria-label="`${size} search`" placeholder="Search…" />
        <Toolbar.Button>Apply</Toolbar.Button>
      </Toolbar>
    </div>
  </div>
</template>

<style scoped>
.toolbar-sizes { display: grid; gap: 0.75rem; }
.toolbar-size-row { display: flex; align-items: center; gap: 0.75rem; }
.toolbar-size-label { width: 2.5rem; }
</style>

Button actions

Use quiet toolbar buttons for related actions that have the same priority.

<script setup>
import { Toolbar } from "@dicehub/kappa/components/toolbar";
import { Download, Upload } from "@lucide/vue";
</script>

<template>
  <Toolbar aria-label="File actions">
    <Toolbar.Button :icon="Upload">Upload</Toolbar.Button>
    <Toolbar.Button :icon="Download">Download</Toolbar.Button>
  </Toolbar>
</template>

Use Toolbar.Link for navigation. It keeps link semantics and joins arrow-key navigation.

Accessible labels

Use aria-label for compact controls that do not have visible text.

<script setup>
import { Toolbar } from "@dicehub/kappa/components/toolbar";
import { Search } from "@lucide/vue";
</script>

<template>
  <Toolbar aria-label="Search records">
    <Toolbar.Input aria-label="Search records" placeholder="Search" />
    <Toolbar.Button :icon="Search" aria-label="Search" />
  </Toolbar>
</template>

Accessibility

  • Name the toolbar with a visible label, aria-label, or aria-labelledby.
  • Use native buttons for actions and links for navigation. Add an accessible name to every icon-only button and input.
  • Disabled controls use native disabled semantics and are skipped by Tab and arrow-key movement.
  • Use aria-pressed for formatting toggles; Toolbar does not invent action state.
  • Focus indicators remain visible in light, dark, and forced-colors modes.

Keyboard support

KeyBehavior
TabEnter the toolbar at its first enabled item; leave the toolbar normally.
Arrow Left / RightMove between items in a horizontal toolbar. Direction follows RTL layout.
Arrow Up / DownMove between items in a vertical toolbar.
Home / EndMove to the first or last enabled item when the focused control has no native movement.
EnterActivate the focused native button or link.
SpaceActivate the focused native button; inputs retain Space for editing.

API Reference

Toolbar.Root

Prop / SlotTypeDefaultDescription
orientation"horizontal" | "vertical""horizontal"Sets the layout axis and the arrow-key direction.
size"xs" | "sm" | "base" | "lg""base"Sets the shared density for Toolbar.Button, Toolbar.Input, and Toolbar.InputGroup.
disabledbooleanfalseDisables the toolbar and propagates native disabled behavior to its compound controls.
loopFocusbooleantrueWraps arrow-key focus from the first enabled item to the last and back.
default slotslot-Toolbar parts or native controls. Native controls are discovered for roving focus.

Parts

PartTypeDescription
Toolbar.ButtonToolbarButtonPropsButton action with quiet toolbar styling and inherited size.
Toolbar.LinkToolbarLinkPropsLinkButton-based navigation control with inherited size.
Toolbar.InputToolbarInputPropsText input with inherited size and native input events.
Toolbar.InputGroupToolbarInputGroupPropsEach focusable InputGroup control participates in toolbar focus movement.
Toolbar.SeparatorToolbarSeparatorPropsDecorative divider; defaults to the cross-axis of the toolbar.

Keyboard

The root handles navigation for enabled toolbar items and preserves native text editing keys.

KeyBehavior
TabEnter the toolbar at its first enabled item; leave the toolbar normally.
Arrow Left / RightMove between items in a horizontal toolbar. Direction follows RTL layout.
Arrow Up / DownMove between items in a vertical toolbar.
Home / EndMove to the first or last enabled item when the focused control has no native movement.
EnterActivate the focused native button or link.
SpaceActivate the focused native button; inputs retain Space for editing.

Data attributes

AttributeValueDescription
data-slot"toolbar"Identifies the toolbar root.
data-orientation"horizontal" | "vertical"Resolved toolbar orientation.
data-size"xs" | "sm" | "base" | "lg"Resolved toolbar density.
data-disabledpresentAppears when the root is disabled.

Exports

ExportDescription
ToolbarCompound toolbar root with Button, Link, Input, InputGroup, and Separator parts.
ToolbarRoot / ToolbarButton / ToolbarLinkNamed Vue component exports for granular composition.
ToolbarInput / ToolbarInputGroup / ToolbarSeparatorNamed input and divider component exports.
ToolbarProps and part prop typesPublic prop and slot contracts.
ToolbarOrientation / ToolbarSizeSupported orientation and density values.
resolveToolbarOrientation / resolveToolbarSizeSafe resolvers with Kappa defaults.