<script setup>
import { Copy, Download, FolderOpen, Trash2 } from "@lucide/vue";
import { Button } from "@dicehub/kappa/components/button";
import { Dropdown } from "@dicehub/kappa/components/dropdown";
</script>
<template>
<Dropdown.Root aria-label="Run actions">
<Dropdown.Trigger as-child>
<Button variant="outline">Run actions <Dropdown.Indicator /></Button>
</Dropdown.Trigger>
<Dropdown.Content>
<Dropdown.Group>
<Dropdown.Label>Run 4189</Dropdown.Label>
<Dropdown.Item value="open" value-text="Open results" :icon="FolderOpen">Open results</Dropdown.Item>
<Dropdown.Item value="copy" value-text="Copy run ID" :icon="Copy">
Copy run ID
<template #end><Dropdown.Shortcut>⌘C</Dropdown.Shortcut></template>
</Dropdown.Item>
<Dropdown.Item value="archive-bundle" value-text="Download archive" :icon="Download">
Download archive
</Dropdown.Item>
</Dropdown.Group>
<Dropdown.Separator />
<Dropdown.Item value="delete" value-text="Delete run" variant="destructive" :icon="Trash2">
Delete run
</Dropdown.Item>
</Dropdown.Content>
</Dropdown.Root>
</template>Installation
Barrel
import {
Dropdown,
DropdownRoot,
DropdownContent,
DropdownItem,
type DropdownProps,
type DropdownItemVariant,
} from "@dicehub/kappa";Granular
import {
Dropdown,
DropdownRoot,
DropdownContent,
DropdownItem,
type DropdownProps,
type DropdownItemVariant,
} from "@dicehub/kappa/components/dropdown";Usage
Put Trigger and Content inside Dropdown.Root. Give every item a stable, unique value. Handle actions from the rootselect event or from one item. Set valueText when the searchable label must differ from the rendered item text. Checkbox and radio options also need it when their visible label differs from value.
<script setup lang="ts">
import { Button } from "@dicehub/kappa/components/button";
import { Dropdown } from "@dicehub/kappa/components/dropdown";
const emit = defineEmits<{ action: [value: string] }>();
</script>
<template>
<Dropdown.Root @select="emit('action', $event.value)">
<Dropdown.Trigger as-child>
<Button variant="outline">Actions <Dropdown.Indicator /></Button>
</Dropdown.Trigger>
<Dropdown.Content>
<Dropdown.Item value="open">Open</Dropdown.Item>
<Dropdown.Item value="duplicate">Duplicate</Dropdown.Item>
<Dropdown.Separator />
<Dropdown.Item value="delete" variant="destructive">Delete</Dropdown.Item>
</Dropdown.Content>
</Dropdown.Root>
</template>Composition
Dropdown.Root
├── Dropdown.Trigger
└── Dropdown.Content
├── Dropdown.Group
│ ├── Dropdown.Label
│ ├── Dropdown.Item
│ ├── Dropdown.CheckboxItem
│ └── Dropdown.RadioGroup
│ └── Dropdown.RadioItem
├── Dropdown.Separator
└── Dropdown.Sub
├── Dropdown.SubTrigger
└── Dropdown.SubContent
└── Dropdown.ItemArk UI Menu owns open state, focus, roving highlight, typeahead, selection, dismissal, positioning, nested menus, and ARIA roles. Kappa owns the compound API, portalled surfaces, option state helpers, semantic tokens, dense layout, and motion.
DropdownandDropdown.Rootare the same root component.Contentsupplies Teleport, Positioner, and the menu surface. Pass:teleport="false"for in-place rendering.- Use
as-childwhen a Kappa Button or another semantic element owns the trigger. - An
as-childelement must not add capture-phase handlers that replace Kappa keyboard or disabled behavior. - Use
LinkItemfor navigation. Do not put an anchor inside a normal Item. - Pass Root
navigatewhen a client-side router must handle keyboard link activation. Native anchors work without it. - Do not set a custom DOM
idon Item. Ark UI uses its generated IDs for focus; usevalueas the stable action key.
<Dropdown.Root>
<Dropdown.Trigger />
<Dropdown.Content>
<Dropdown.Group>
<Dropdown.Label />
<Dropdown.Item />
<Dropdown.LinkItem />
<Dropdown.CheckboxItem />
<Dropdown.RadioGroup>
<Dropdown.RadioItem />
</Dropdown.RadioGroup>
</Dropdown.Group>
<Dropdown.Separator />
<Dropdown.Sub>
<Dropdown.SubTrigger />
<Dropdown.SubContent />
</Dropdown.Sub>
</Dropdown.Content>
</Dropdown.Root>Reference Differences
The public name is Dropdown, the danger treatment is nameddestructive to match Kappa Button, and Content owns its portal instead of requiring a separate Portal part. Kappa also adds uncontrolled checkbox and radio defaults, context-trigger support, explicit reduced motion, and forced-colors styles. Root ariaLabel is mirrored to Content because the popup is portalled, and keyboard-opened context menus restore focus after Escape.
Behavior and Selection
- Normal Item activates one action and closes by default. Set
closeOnSelectonly for a deliberate workflow. - CheckboxItem and RadioItem stay open by default so users can change more than one option.
- Use
v-model:checkedandv-modelfor controlled option state. UsedefaultCheckedanddefaultValuefor uncontrolled state. - A menu is not a form field. Use Select, Checkbox, or Radio when a value must be submitted with a form.
- Use a Dialog for destructive confirmation. A destructive Item communicates risk but does not request confirmation.
- Keep Root mounted while it is controlled. Use
lazyMountandunmountOnExitfor cleanup when needed.
Examples
Basic
Use a unique value for each action. Ark UI closes the menu after a normal item is selected.
<Dropdown.Root aria-label="Project actions">
<Dropdown.Trigger as-child><Button>Open menu <Dropdown.Indicator /></Button></Dropdown.Trigger>
<Dropdown.Content>
<Dropdown.Item value="new">New simulation</Dropdown.Item>
<Dropdown.Item value="duplicate">Duplicate project</Dropdown.Item>
<Dropdown.Item value="archive">Archive project</Dropdown.Item>
</Dropdown.Content>
</Dropdown.Root>Icons and Inset Alignment
Use icon for a component icon. Use inset to align text-only rows with icon-bearing rows.
<Dropdown.Item value="open" :icon="FolderOpen">Open</Dropdown.Item>
<Dropdown.Item value="copy" :icon="Copy">Copy path</Dropdown.Item>
<Dropdown.Separator />
<Dropdown.Group>
<Dropdown.Label inset>Without icons</Dropdown.Label>
<Dropdown.Item value="rename" inset>Rename</Dropdown.Item>
</Dropdown.Group>Action Callbacks
Listen to Root select for one action router, or listen to Item select for local behavior.
<script setup lang="ts">
import { ref } from "vue";
import { Button } from "@dicehub/kappa/components/button";
import { Dropdown } from "@dicehub/kappa/components/dropdown";
const lastAction = ref("No action selected");
</script>
<template>
<Dropdown.Root @select="lastAction = $event.value">
<Dropdown.Trigger as-child><Button>Choose action</Button></Dropdown.Trigger>
<Dropdown.Content>
<Dropdown.Item value="inspect">Inspect report</Dropdown.Item>
<Dropdown.Item value="share">Share result</Dropdown.Item>
<Dropdown.Item value="export">Export data</Dropdown.Item>
</Dropdown.Content>
</Dropdown.Root>
<output aria-live="polite">{{ lastAction }}</output>
</template>Checkbox Items
CheckboxItem supports controlled checked and uncontrolled defaultChecked state. It stays open by default for repeated changes.
<Dropdown.CheckboxItem v-model:checked="activityVisible" value="activity">
Activity
</Dropdown.CheckboxItem>
<Dropdown.CheckboxItem :default-checked="false" value="compact">
Compact rows
</Dropdown.CheckboxItem>Radio Group
Use RadioGroup for one choice. v-model is controlled; defaultValue supplies an uncontrolled initial choice.
<Dropdown.RadioGroup v-model="density">
<Dropdown.Label>Row density</Dropdown.Label>
<Dropdown.RadioItem value="compact">Compact</Dropdown.RadioItem>
<Dropdown.RadioItem value="comfortable">Comfortable</Dropdown.RadioItem>
<Dropdown.RadioItem value="spacious">Spacious</Dropdown.RadioItem>
</Dropdown.RadioGroup>Nested Submenu
Sub uses right-start positioning by default. Ark UI manages pointer intent, arrow keys, focus return, and nested dismissal.
Custom Trigger
Use as-child so a Kappa Button or another semantic button remains the only focusable trigger element.
<Dropdown.Trigger as-child>
<Button aria-label="Open account menu" shape="circle" variant="ghost">
<User aria-hidden="true" />
</Button>
</Dropdown.Trigger>Navigation Links
LinkItem renders one semantic anchor with menu-item behavior. Disabled links remove href. New-tab links receive noopener noreferrer by default.
Controlled Open State
Bind v-model:open when application state must observe or control visibility.
<Dropdown.Root v-model:open="open">
<Dropdown.Trigger as-child><Button>Controlled menu</Button></Dropdown.Trigger>
<Dropdown.Content>
<Dropdown.Item value="pause">Pause run</Dropdown.Item>
<Dropdown.Item value="resume">Resume run</Dropdown.Item>
</Dropdown.Content>
</Dropdown.Root>Destructive and Disabled Items
Destructive changes use the Kappa danger tokens. Disabled items stay visible and are skipped by keyboard navigation.
<Dropdown.Item value="details">View details</Dropdown.Item>
<Dropdown.Item value="restart" disabled>Restart while active</Dropdown.Item>
<Dropdown.Separator />
<Dropdown.Item value="delete" variant="destructive">Delete permanently</Dropdown.Item>Long List
Content respects the available viewport height and scrolls without moving the trigger or page.
<Dropdown.Content style="--kappa-dropdown-max-block-size: 14rem">
<Dropdown.Group>
<Dropdown.Label>Recent runs</Dropdown.Label>
<Dropdown.Item v-for="run in runs" :key="run.id" :value="run.id" :value-text="run.label">
<Dropdown.ItemText>{{ run.label }}</Dropdown.ItemText>
</Dropdown.Item>
</Dropdown.Group>
</Dropdown.Content>Context Menu
ContextTrigger opens from a right-click, Shift+F10, or a long press on supported touch and pen input.
Right-to-left
Set dir on Root. Logical spacing, submenu placement, the caret, and arrow keys then follow the reading direction.
<div dir="rtl">
<Dropdown.Root dir="rtl" aria-label="إجراءات التشغيل">
<Dropdown.Trigger as-child><Button>إجراءات التشغيل</Button></Dropdown.Trigger>
<Dropdown.Content>
<Dropdown.Item value="open">فتح النتائج</Dropdown.Item>
<Dropdown.Sub>
<Dropdown.SubTrigger>تصدير كـ</Dropdown.SubTrigger>
<Dropdown.SubContent>...</Dropdown.SubContent>
</Dropdown.Sub>
</Dropdown.Content>
</Dropdown.Root>
</div>Accessibility
- Give the trigger a visible name or an
aria-label. RootariaLabellabels the menu surface when its context is not clear. - Keep Label inside its Group or RadioGroup so Ark UI can associate them.
- Disabled items use
aria-disabled, remain visible, and are skipped by keyboard navigation. - Item and LinkItem typeahead falls back to rendered text. CheckboxItem and RadioItem fall back to
value. UsevalueTextfor an explicit search label. - CheckboxItem and RadioItem expose
aria-checked. Do not replace them with a visual selected Item. - Shortcut is visual text only. Register application shortcuts separately and document their platform behavior.
- ContextTrigger supports pointer and keyboard context-menu input. Always keep its target keyboard focusable.
- ContextTrigger renders
type="button"by default. WhenasChildsupplies a custom button, set its type explicitly. - Focus indicators remain visible. Open, close, and caret motion stop under reduced-motion preferences.
API Reference
Dropdown.Root
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel | string | — | Accessible menu label when visible context is not sufficient. |
dir | "ltr" | "rtl" | inherited | Reading direction for placement and keyboard behavior. |
open / defaultOpen | boolean | false | Controlled or initial open state. |
highlightedValue / defaultHighlightedValue | string | — | Controlled or initial highlighted item value. |
triggerValue / defaultTriggerValue | string | — | Controlled or initial active trigger in a multi-trigger menu. |
positioning | PositioningOptions | bottom-start, 6px | Ark UI floating position options. |
anchorPoint | Point | — | Virtual anchor point for context-positioned content. |
closeOnSelect | boolean | true | Default close behavior for normal items. |
composite | boolean | true | Use menu composite focus and keyboard behavior. |
loopFocus | boolean | false | Wrap arrow-key focus at the first and last item. |
typeahead | boolean | true | Enable printable-character item matching. |
navigate | (details) => void | native link | Override keyboard link navigation for an application router. |
id / ids | string / ElementIds | generated | Stable machine and part IDs for integration or testing. |
lazyMount / unmountOnExit | boolean | false | Control presence and cleanup of popup content. |
Dropdown.Content
| Prop | Type | Default | Description |
|---|---|---|---|
teleport | boolean | true | Teleport Positioner and Content to the target. |
teleportTo | Teleport target | "body" | Vue Teleport target. |
asChild | boolean | false | Merge Content behavior into one child element. |
Dropdown.Item and LinkItem
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | required | Unique action value used by selection. |
valueText | string | rendered text | Explicit searchable typeahead text for Item and LinkItem. |
variant | "default" | "destructive" | "default" | Semantic item treatment. |
disabled | boolean | false | Expose but skip an unavailable item. |
closeOnSelect | boolean | root value | Override close behavior for this item. |
icon / iconProps | Component / object | — | Optional decorative leading icon and its props. |
inset | boolean | false | Align a text-only row with icon-bearing rows. |
selected | boolean | false | Show a trailing visual check. Use RadioItem for a semantic single choice. |
asChild | boolean | false | Merge Item behavior into one custom child. Compose custom decoration inside that child. |
LinkItem.href | string | required | Anchor destination. It is removed when the link is disabled. |
LinkItem.value | string | href | Stable selection value for the link. |
LinkItem.target / rel | string | — | Native anchor attributes. target=_blank defaults rel to noopener noreferrer. |
SubTrigger.disabled | boolean | false | Expose but skip an unavailable submenu. |
Selection Parts
| Prop | Type | Default | Description |
|---|---|---|---|
CheckboxItem / RadioItem.value | string | required | Unique option value used by selection and as fallback typeahead text. |
CheckboxItem.checked / defaultChecked | boolean | false | Controlled or initial checkbox state. |
CheckboxItem.closeOnSelect | boolean | false | Keep the menu open for repeated option changes. |
RadioGroup.modelValue / defaultValue | string | — | Controlled or initial selected radio value. |
RadioItem.closeOnSelect | boolean | false | Keep the menu open while a radio choice changes. |
CheckboxItem / RadioItem.valueText | string | value | Searchable typeahead text for an option. |
CheckboxItem / RadioItem.disabled | boolean | false | Expose but skip an unavailable option. |
CheckboxItem / RadioItem.inset | boolean | false | Add logical start spacing after the option indicator. |
CheckboxItem / RadioItem.asChild | boolean | false | Merge option behavior into one custom child; compose its indicator manually. |
RadioGroup.id / asChild | string / boolean | generated / false | Group identity and custom-element composition. |
Parts
| Part | Element | Description |
|---|---|---|
Dropdown.Root | none | Owns open, highlight, selection, typeahead, and positioning state. |
Dropdown.RootProvider | none | Provides an API created with useDropdown. |
Dropdown.Trigger | button | Opens the menu and receives restored focus. |
Dropdown.Indicator | div | Optional open-state caret for the trigger. |
Dropdown.Content | div | Portalled positioner and scroll-safe menu surface. |
Dropdown.Item | div | One action menu item. |
Dropdown.LinkItem | a | Semantic navigation item. |
Dropdown.CheckboxItem | div | Multi-choice menu item with aria-checked. |
Dropdown.RadioGroup / RadioItem | div | Single-choice group and item with aria-checked. |
Dropdown.Group / Label | div | Associated group and visible group label. |
Dropdown.Separator | div | Semantic separator between action groups. |
Dropdown.Shortcut | span | Visual keyboard hint. It does not register a shortcut. |
Dropdown.Sub / SubTrigger / SubContent | mixed | Nested menu root, trigger item, and portalled surface. |
Dropdown.ContextTrigger | button | Opens the menu from context-menu input. |
Dropdown.Arrow | div | Optional positioned arrow and tip. |
Dropdown.ItemIndicator / RadioItemIndicator | div | Composable checked-state indicator for custom option items. |
Dropdown.ItemText | span | Composable item text with overflow handling. |
Dropdown.Context / ItemContext | none | Scoped slots for Ark menu and item state. |
Events
| Event | Payload | Description |
|---|---|---|
select | DropdownSelectionDetails | A menu item was selected; details.value identifies it. |
openChange | DropdownOpenChangeDetails | The menu opened or closed. |
update:open | boolean | Vue controlled-state update. |
highlightChange | DropdownHighlightChangeDetails | The roving highlight changed. |
update:highlightedValue | string | null | Vue controlled highlight update. |
triggerValueChange / update:triggerValue | details / string | null | The active multi-trigger value changed. |
escapeKeyDown | KeyboardEvent | Escape was pressed while the menu was open. |
exitComplete | void | The closing presence transition completed. |
focusOutside | DropdownFocusOutsideEvent | Focus moved outside the open menu. |
interactOutside | DropdownInteractOutsideEvent | Pointer or focus interaction occurred outside. |
pointerDownOutside | DropdownPointerDownOutsideEvent | Pointer input started outside the open menu. |
requestDismiss | DropdownRequestDismissEvent | A nested dismissable layer requested dismissal. |
checkedChange | boolean | CheckboxItem changed its state. |
update:checked | boolean | Vue controlled checkbox update. |
valueChange | string | RadioGroup changed its selected value. |
update:modelValue | string | Vue controlled radio-group update. |
Exports
| Export | Description |
|---|---|
Dropdown / DropdownRoot | Compound root and named root export. |
Dropdown* | Named exports for every compound part. |
DropdownProps / Dropdown*Props | Public root and part prop contracts. |
DROPDOWN_ITEM_VARIANTS | Supported semantic item variants. |
DROPDOWN_DEFAULT_ITEM_VARIANT | Default semantic item variant. |
DROPDOWN_DEFAULT_POSITIONING | Kappa root placement defaults. |
DROPDOWN_SUB_DEFAULT_POSITIONING | Kappa submenu placement defaults. |
isDropdownItemVariant / resolveDropdownItemVariant | Runtime item-variant guards and fallback resolver. |
useDropdown / useDropdownContext | Ark UI state-machine hooks under Kappa names. |
dropdownAnatomy | Ark UI menu anatomy under a Kappa name. |