Menu
Menu container primitive that manages focus and selection across ui-menu-item children.
Inline menu
Inline keeps a menu visible in the page. Use for an embedded set of actions; an anchored menu uses for and opens from its trigger.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-menu
aria-label="Actions"
inline
>
<ui-menu-item
value="first"
>First item</ui-menu-item>
<ui-menu-item
value="second"
>Second item</ui-menu-item>
</ui-menu><script setup>
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiMenu
aria-label="Actions"
:inline="true"
>
<UiMenuItem
value="first"
>First item</UiMenuItem>
<UiMenuItem
value="second"
>Second item</UiMenuItem>
</UiMenu>
</template>import { UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
aria-label="Actions"
inline
>
<UiMenuItem
value="first"
>First item</UiMenuItem>
<UiMenuItem
value="second"
>Second item</UiMenuItem>
</UiMenu>;
}/** @jsxImportSource preact */
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
aria-label="Actions"
inline
>
<UiMenuItem
value="first"
>First item</UiMenuItem>
<UiMenuItem
value="second"
>Second item</UiMenuItem>
</UiMenu>;
}<script>
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<UiMenu
aria-label="Actions"
inline
>
<UiMenuItem
value="first"
>First item</UiMenuItem>
<UiMenuItem
value="second"
>Second item</UiMenuItem>
</UiMenu>import { Component } from "@angular/core";
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiMenu, UiMenuItem],
template: `
<div
ui-menu
aria-label="Actions"
[inline]="true"
>
<ng-template
ui-menu-item
[value]="'first'"
>First item</ng-template>
<ng-template
ui-menu-item
[value]="'second'"
>Second item</ng-template>
</div>
`,
})
export default class Example {}for
For associates the menu with the control that toggles and anchors it.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-icon-button/ui-icon-button.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-icon/ui-icon.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-icon-button
id="menu-trigger"
label="Open menu"
>
<ui-icon
name="ellipsis"
></ui-icon>
</ui-icon-button>
<ui-menu
for="menu-trigger"
aria-label="Document actions"
>
<ui-menu-item
value="rename"
>Rename</ui-menu-item>
<ui-menu-item
value="share"
><ui-icon
name="link"
></ui-icon>Share</ui-menu-item>
</ui-menu><script setup>
import { UiIcon, UiIconButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiIconButton
id="menu-trigger"
label="Open menu"
>
<UiIcon
name="ellipsis"
/>
</UiIconButton>
<UiMenu
for="menu-trigger"
aria-label="Document actions"
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="share"
><UiIcon
name="link"
/>Share</UiMenuItem>
</UiMenu>
</template>import { UiIcon, UiIconButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <>
<UiIconButton
id="menu-trigger"
label="Open menu"
>
<UiIcon
name="ellipsis"
/>
</UiIconButton>
<UiMenu
htmlFor="menu-trigger"
aria-label="Document actions"
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="share"
><UiIcon
name="link"
/>Share</UiMenuItem>
</UiMenu>
</>;
}/** @jsxImportSource preact */
import { UiIcon, UiIconButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <>
<UiIconButton
id="menu-trigger"
label="Open menu"
>
<UiIcon
name="ellipsis"
/>
</UiIconButton>
<UiMenu
htmlFor="menu-trigger"
aria-label="Document actions"
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="share"
><UiIcon
name="link"
/>Share</UiMenuItem>
</UiMenu>
</>;
}<script>
import { UiIcon, UiIconButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<UiIconButton
id="menu-trigger"
label="Open menu"
>
<UiIcon
name="ellipsis"
/>
</UiIconButton>
<UiMenu
for="menu-trigger"
aria-label="Document actions"
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="share"
><UiIcon
name="link"
/>Share</UiMenuItem>
</UiMenu>import { Component } from "@angular/core";
import { UiIcon, UiIconButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiIconButton, UiIcon, UiMenu, UiMenuItem],
template: `
<button
ui-icon-button
id="menu-trigger"
[label]="'Open menu'"
>
<span
ui-icon
name="ellipsis"
></span>
</button>
<div
ui-menu
[for]="'menu-trigger'"
aria-label="Document actions"
>
<ng-template
ui-menu-item
[value]="'rename'"
>Rename</ng-template>
<ng-template
ui-menu-item
[value]="'share'"
><span
ui-icon
name="link"
></span>Share</ng-template>
</div>
`,
})
export default class Example {}disabled items
Disabled on a child item keeps it discoverable while removing it from selection.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-menu
role="menu"
aria-label="Document actions"
open
>
<ui-menu-item
value="rename"
>Rename</ui-menu-item>
<ui-menu-item
value="share"
>Share</ui-menu-item>
<ui-menu-item
value="delete"
disabled
>Delete (locked)</ui-menu-item>
</ui-menu><script setup>
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiMenu
role="menu"
aria-label="Document actions"
:open="true"
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="share"
>Share</UiMenuItem>
<UiMenuItem
value="delete"
:disabled="true"
>Delete (locked)</UiMenuItem>
</UiMenu>
</template>import { UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
role="menu"
aria-label="Document actions"
open
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="share"
>Share</UiMenuItem>
<UiMenuItem
value="delete"
disabled
>Delete (locked)</UiMenuItem>
</UiMenu>;
}/** @jsxImportSource preact */
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
role="menu"
aria-label="Document actions"
open
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="share"
>Share</UiMenuItem>
<UiMenuItem
value="delete"
disabled
>Delete (locked)</UiMenuItem>
</UiMenu>;
}<script>
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<UiMenu
role="menu"
aria-label="Document actions"
open
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="share"
>Share</UiMenuItem>
<UiMenuItem
value="delete"
disabled
>Delete (locked)</UiMenuItem>
</UiMenu>import { Component } from "@angular/core";
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiMenu, UiMenuItem],
template: `
<div
ui-menu
role="menu"
aria-label="Document actions"
[open]="true"
>
<ng-template
ui-menu-item
[value]="'rename'"
>Rename</ng-template>
<ng-template
ui-menu-item
[value]="'share'"
>Share</ng-template>
<ng-template
ui-menu-item
[value]="'delete'"
[disabled]="true"
>Delete (locked)</ng-template>
</div>
`,
})
export default class Example {}placement="top-end"
Placement sets the preferred edge and alignment against the trigger.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-button
id="menu-placed-trigger"
>Open above</ui-button>
<ui-menu
for="menu-placed-trigger"
placement="top-end"
>
<ui-menu-item
value="rename"
>Rename</ui-menu-item>
<ui-menu-item
value="duplicate"
>Duplicate</ui-menu-item>
</ui-menu><script setup>
import { UiButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiButton
id="menu-placed-trigger"
>Open above</UiButton>
<UiMenu
for="menu-placed-trigger"
placement="top-end"
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="duplicate"
>Duplicate</UiMenuItem>
</UiMenu>
</template>import { UiButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="menu-placed-trigger"
>Open above</UiButton>
<UiMenu
htmlFor="menu-placed-trigger"
placement="top-end"
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="duplicate"
>Duplicate</UiMenuItem>
</UiMenu>
</>;
}/** @jsxImportSource preact */
import { UiButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="menu-placed-trigger"
>Open above</UiButton>
<UiMenu
htmlFor="menu-placed-trigger"
placement="top-end"
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="duplicate"
>Duplicate</UiMenuItem>
</UiMenu>
</>;
}<script>
import { UiButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<UiButton
id="menu-placed-trigger"
>Open above</UiButton>
<UiMenu
for="menu-placed-trigger"
placement="top-end"
>
<UiMenuItem
value="rename"
>Rename</UiMenuItem>
<UiMenuItem
value="duplicate"
>Duplicate</UiMenuItem>
</UiMenu>import { Component } from "@angular/core";
import { UiButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiButton, UiMenu, UiMenuItem],
template: `
<ng-template
ui-button
[hnAttributes]="{ id: 'menu-placed-trigger' }"
>Open above</ng-template>
<div
ui-menu
[for]="'menu-placed-trigger'"
[placement]="'top-end'"
>
<ng-template
ui-menu-item
[value]="'rename'"
>Rename</ng-template>
<ng-template
ui-menu-item
[value]="'duplicate'"
>Duplicate</ng-template>
</div>
`,
})
export default class Example {}Groups, links, and shortcuts
Group headings, native separators, real links, and display-only shortcut hints make larger menus easier to scan.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-group/ui-menu-group.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-button
id="open-file-menu"
>File</ui-button>
<ui-menu
for="open-file-menu"
aria-label="File actions"
>
<ui-menu-group
label="Create"
>
<ui-menu-item
value="new"
>
New file
<kbd
slot="shortcut"
>⌘N</kbd>
</ui-menu-item>
<ui-menu-item
value="template"
href="#templates"
>From template</ui-menu-item>
</ui-menu-group>
<hr>
<ui-menu-item
value="settings"
>Settings</ui-menu-item>
</ui-menu><script setup>
import { UiButton, UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiButton
id="open-file-menu"
>File</UiButton>
<UiMenu
for="open-file-menu"
aria-label="File actions"
>
<UiMenuGroup
label="Create"
>
<UiMenuItem
value="new"
>New file <template #shortcut>
<kbd>⌘N</kbd>
</template></UiMenuItem>
<UiMenuItem
value="template"
href="#templates"
>From template</UiMenuItem>
</UiMenuGroup>
<hr>
<UiMenuItem
value="settings"
>Settings</UiMenuItem>
</UiMenu>
</template>import { UiButton, UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="open-file-menu"
>File</UiButton>
<UiMenu
htmlFor="open-file-menu"
aria-label="File actions"
>
<UiMenuGroup
label="Create"
>
<UiMenuItem
value="new"
slots={{
shortcut: <kbd>⌘N</kbd>,
}}
>New file </UiMenuItem>
<UiMenuItem
value="template"
href="#templates"
>From template</UiMenuItem>
</UiMenuGroup>
<hr />
<UiMenuItem
value="settings"
>Settings</UiMenuItem>
</UiMenu>
</>;
}/** @jsxImportSource preact */
import { UiButton, UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="open-file-menu"
>File</UiButton>
<UiMenu
htmlFor="open-file-menu"
aria-label="File actions"
>
<UiMenuGroup
label="Create"
>
<UiMenuItem
value="new"
slots={{
shortcut: <kbd>⌘N</kbd>,
}}
>New file </UiMenuItem>
<UiMenuItem
value="template"
href="#templates"
>From template</UiMenuItem>
</UiMenuGroup>
<hr />
<UiMenuItem
value="settings"
>Settings</UiMenuItem>
</UiMenu>
</>;
}<script>
import { UiButton, UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
</script>
{#snippet shortcut()}
<kbd>⌘N</kbd>
{/snippet}
<UiButton
id="open-file-menu"
>File</UiButton>
<UiMenu
for="open-file-menu"
aria-label="File actions"
>
<UiMenuGroup
label="Create"
>
<UiMenuItem
value="new"
slots={{ shortcut }}
>New file </UiMenuItem>
<UiMenuItem
value="template"
href="#templates"
>From template</UiMenuItem>
</UiMenuGroup>
<hr>
<UiMenuItem
value="settings"
>Settings</UiMenuItem>
</UiMenu>import { Component } from "@angular/core";
import { UiButton, UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiButton, UiMenu, UiMenuGroup, UiMenuItem],
template: `
<ng-template
ui-button
[hnAttributes]="{ id: 'open-file-menu' }"
>File</ng-template>
<div
ui-menu
[for]="'open-file-menu'"
aria-label="File actions"
>
<div
ui-menu-group
[label]="'Create'"
>
<ng-template #shortcut>
<kbd>⌘N</kbd>
</ng-template>
<ng-template
ui-menu-item
[value]="'new'"
[slots]="{ shortcut: shortcut }"
>New file </ng-template>
<ng-template
ui-menu-item
[value]="'template'"
[href]="'#templates'"
>From template</ng-template>
</div>
<hr>
<ng-template
ui-menu-item
[value]="'settings'"
>Settings</ng-template>
</div>
`,
})
export default class Example {}Checkable view options
Checkbox items toggle independently, while radio items choose one option within their named group. They keep the menu open so another choice can be made.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-group/ui-menu-group.html"
>
<ui-button
id="open-view-menu"
>View</ui-button>
<ui-menu
for="open-view-menu"
aria-label="View options"
>
<ui-menu-item
type="checkbox"
value="grid"
checked
>Show grid</ui-menu-item>
<hr>
<ui-menu-group
label="Sort by"
>
<ui-menu-item
type="radio"
value="name"
checked
>Name</ui-menu-item>
<ui-menu-item
type="radio"
value="date"
>Date</ui-menu-item>
</ui-menu-group>
</ui-menu><script setup>
import { UiButton, UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiButton
id="open-view-menu"
>View</UiButton>
<UiMenu
for="open-view-menu"
aria-label="View options"
>
<UiMenuItem
type="checkbox"
value="grid"
:checked="true"
>Show grid</UiMenuItem>
<hr>
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
:checked="true"
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>
</template>import { UiButton, UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="open-view-menu"
>View</UiButton>
<UiMenu
htmlFor="open-view-menu"
aria-label="View options"
>
<UiMenuItem
type="checkbox"
value="grid"
checked
>Show grid</UiMenuItem>
<hr />
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
checked
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>
</>;
}/** @jsxImportSource preact */
import { UiButton, UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="open-view-menu"
>View</UiButton>
<UiMenu
htmlFor="open-view-menu"
aria-label="View options"
>
<UiMenuItem
type="checkbox"
value="grid"
checked
>Show grid</UiMenuItem>
<hr />
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
checked
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>
</>;
}<script>
import { UiButton, UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
</script>
<UiButton
id="open-view-menu"
>View</UiButton>
<UiMenu
for="open-view-menu"
aria-label="View options"
>
<UiMenuItem
type="checkbox"
value="grid"
checked
>Show grid</UiMenuItem>
<hr>
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
checked
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>import { Component } from "@angular/core";
import { UiButton, UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiButton, UiMenu, UiMenuItem, UiMenuGroup],
template: `
<ng-template
ui-button
[hnAttributes]="{ id: 'open-view-menu' }"
>View</ng-template>
<div
ui-menu
[for]="'open-view-menu'"
aria-label="View options"
>
<ng-template
ui-menu-item
[type]="'checkbox'"
[value]="'grid'"
[checked]="true"
>Show grid</ng-template>
<hr>
<div
ui-menu-group
[label]="'Sort by'"
>
<ng-template
ui-menu-item
[type]="'radio'"
[value]="'name'"
[checked]="true"
>Name</ng-template>
<ng-template
ui-menu-item
[type]="'radio'"
[value]="'date'"
>Date</ng-template>
</div>
</div>
`,
})
export default class Example {}Small menu
size="sm" matches a menu to a small trigger, such as an xs or sm button. The chosen radio item shows a check, as in every menu.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-button
id="open-small-menu"
variant="ghost"
tone="neutral"
size="xs"
shape="pill"
>Sort: Name</ui-button>
<ui-menu
for="open-small-menu"
size="sm"
aria-label="Sort by"
>
<ui-menu-item
type="radio"
value="name"
checked
>Name</ui-menu-item>
<ui-menu-item
type="radio"
value="date"
>Date</ui-menu-item>
<ui-menu-item
type="radio"
value="size"
>Size</ui-menu-item>
</ui-menu><script setup>
import { UiButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiButton
id="open-small-menu"
variant="ghost"
tone="neutral"
size="xs"
shape="pill"
>Sort: Name</UiButton>
<UiMenu
for="open-small-menu"
size="sm"
aria-label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
:checked="true"
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
<UiMenuItem
type="radio"
value="size"
>Size</UiMenuItem>
</UiMenu>
</template>import { UiButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="open-small-menu"
variant="ghost"
tone="neutral"
size="xs"
shape="pill"
>Sort: Name</UiButton>
<UiMenu
htmlFor="open-small-menu"
size="sm"
aria-label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
checked
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
<UiMenuItem
type="radio"
value="size"
>Size</UiMenuItem>
</UiMenu>
</>;
}/** @jsxImportSource preact */
import { UiButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="open-small-menu"
variant="ghost"
tone="neutral"
size="xs"
shape="pill"
>Sort: Name</UiButton>
<UiMenu
htmlFor="open-small-menu"
size="sm"
aria-label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
checked
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
<UiMenuItem
type="radio"
value="size"
>Size</UiMenuItem>
</UiMenu>
</>;
}<script>
import { UiButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<UiButton
id="open-small-menu"
variant="ghost"
tone="neutral"
size="xs"
shape="pill"
>Sort: Name</UiButton>
<UiMenu
for="open-small-menu"
size="sm"
aria-label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
checked
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
<UiMenuItem
type="radio"
value="size"
>Size</UiMenuItem>
</UiMenu>import { Component } from "@angular/core";
import { UiButton, UiMenu, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiButton, UiMenu, UiMenuItem],
template: `
<ng-template
ui-button
[variant]="'ghost'"
[tone]="'neutral'"
[size]="'xs'"
[shape]="'pill'"
[hnAttributes]="{ id: 'open-small-menu' }"
>Sort: Name</ng-template>
<div
ui-menu
[for]="'open-small-menu'"
[size]="'sm'"
aria-label="Sort by"
>
<ng-template
ui-menu-item
[type]="'radio'"
[value]="'name'"
[checked]="true"
>Name</ng-template>
<ng-template
ui-menu-item
[type]="'radio'"
[value]="'date'"
>Date</ng-template>
<ng-template
ui-menu-item
[type]="'radio'"
[value]="'size'"
>Size</ng-template>
</div>
`,
})
export default class Example {}Aligned icon columns
Icon-bearing and text-only menu actions share a label column; destructive actions keep their danger tone.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-icon/ui-icon.html"
>
<ui-menu
inline
icons
aria-label="File actions"
>
<ui-menu-item
value="edit"
>
<ui-icon
slot="icon"
name="pencil"
></ui-icon>
Edit
<span
slot="shortcut"
>Ctrl E</span>
</ui-menu-item>
<ui-menu-item
value="comments"
>Comments</ui-menu-item>
<ui-menu-item
value="history"
href="#history"
>
<ui-icon
slot="icon"
name="history"
></ui-icon>
History
</ui-menu-item>
<ui-menu-item
value="delete"
tone="danger"
>
<ui-icon
slot="icon"
name="trash-2"
></ui-icon>
Delete
</ui-menu-item>
</ui-menu><script setup>
import { UiIcon, UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiMenu
:inline="true"
:icons="true"
aria-label="File actions"
>
<UiMenuItem
value="edit"
><template #icon>
<UiIcon
name="pencil"
/>
</template>Edit<template #shortcut>
<span>Ctrl E</span>
</template></UiMenuItem>
<UiMenuItem
value="comments"
>Comments</UiMenuItem>
<UiMenuItem
value="history"
href="#history"
><template #icon>
<UiIcon
name="history"
/>
</template>History</UiMenuItem>
<UiMenuItem
value="delete"
tone="danger"
><template #icon>
<UiIcon
name="trash-2"
/>
</template>Delete</UiMenuItem>
</UiMenu>
</template>import { UiIcon, UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
inline
icons
aria-label="File actions"
>
<UiMenuItem
value="edit"
slots={{
icon: <UiIcon
name="pencil"
/>,
shortcut: <span>Ctrl E</span>,
}}
>Edit</UiMenuItem>
<UiMenuItem
value="comments"
>Comments</UiMenuItem>
<UiMenuItem
value="history"
href="#history"
slots={{
icon: <UiIcon
name="history"
/>,
}}
>History</UiMenuItem>
<UiMenuItem
value="delete"
tone="danger"
slots={{
icon: <UiIcon
name="trash-2"
/>,
}}
>Delete</UiMenuItem>
</UiMenu>;
}/** @jsxImportSource preact */
import { UiIcon, UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
inline
icons
aria-label="File actions"
>
<UiMenuItem
value="edit"
slots={{
icon: <UiIcon
name="pencil"
/>,
shortcut: <span>Ctrl E</span>,
}}
>Edit</UiMenuItem>
<UiMenuItem
value="comments"
>Comments</UiMenuItem>
<UiMenuItem
value="history"
href="#history"
slots={{
icon: <UiIcon
name="history"
/>,
}}
>History</UiMenuItem>
<UiMenuItem
value="delete"
tone="danger"
slots={{
icon: <UiIcon
name="trash-2"
/>,
}}
>Delete</UiMenuItem>
</UiMenu>;
}<script>
import { UiIcon, UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
{#snippet icon()}
<UiIcon
name="pencil"
/>
{/snippet}
{#snippet shortcut()}
<span>Ctrl E</span>
{/snippet}
{#snippet menuItemIcon()}
<UiIcon
name="history"
/>
{/snippet}
{#snippet menuItemIcon2()}
<UiIcon
name="trash-2"
/>
{/snippet}
<UiMenu
inline
icons
aria-label="File actions"
>
<UiMenuItem
value="edit"
slots={{ icon, shortcut }}
>Edit</UiMenuItem>
<UiMenuItem
value="comments"
>Comments</UiMenuItem>
<UiMenuItem
value="history"
href="#history"
slots={{ icon: menuItemIcon }}
>History</UiMenuItem>
<UiMenuItem
value="delete"
tone="danger"
slots={{ icon: menuItemIcon2 }}
>Delete</UiMenuItem>
</UiMenu>import { Component } from "@angular/core";
import { UiIcon, UiMenu, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiMenu, UiMenuItem, UiIcon],
template: `
<div
ui-menu
[inline]="true"
[icons]="true"
aria-label="File actions"
>
<ng-template #icon>
<span
ui-icon
name="pencil"
></span>
</ng-template>
<ng-template #shortcut>
<span>Ctrl E</span>
</ng-template>
<ng-template
ui-menu-item
[value]="'edit'"
[slots]="{ icon: icon, shortcut: shortcut }"
>Edit</ng-template>
<ng-template
ui-menu-item
[value]="'comments'"
>Comments</ng-template>
<ng-template #menuItemIcon>
<span
ui-icon
name="history"
></span>
</ng-template>
<ng-template
ui-menu-item
[value]="'history'"
[href]="'#history'"
[slots]="{ icon: menuItemIcon }"
>History</ng-template>
<ng-template #menuItemIcon2>
<span
ui-icon
name="trash-2"
></span>
</ng-template>
<ng-template
ui-menu-item
[value]="'delete'"
[tone]="'danger'"
[slots]="{ icon: menuItemIcon2 }"
>Delete</ng-template>
</div>
`,
})
export default class Example {}Named group
A visible group heading also names its grouped menu items for assistive technology.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-group/ui-menu-group.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-menu
open
aria-label="Sort options"
>
<ui-menu-group
label="Sort by"
>
<ui-menu-item
value="name"
>Name</ui-menu-item>
<ui-menu-item
value="date"
>Date</ui-menu-item>
</ui-menu-group>
</ui-menu><script setup>
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiMenu
:open="true"
aria-label="Sort options"
>
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
value="name"
>Name</UiMenuItem>
<UiMenuItem
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>
</template>import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
open
aria-label="Sort options"
>
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
value="name"
>Name</UiMenuItem>
<UiMenuItem
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>;
}/** @jsxImportSource preact */
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
open
aria-label="Sort options"
>
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
value="name"
>Name</UiMenuItem>
<UiMenuItem
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>;
}<script>
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
</script>
<UiMenu
open
aria-label="Sort options"
>
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
value="name"
>Name</UiMenuItem>
<UiMenuItem
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>import { Component } from "@angular/core";
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiMenu, UiMenuGroup, UiMenuItem],
template: `
<div
ui-menu
[open]="true"
aria-label="Sort options"
>
<div
ui-menu-group
[label]="'Sort by'"
>
<ng-template
ui-menu-item
[value]="'name'"
>Name</ng-template>
<ng-template
ui-menu-item
[value]="'date'"
>Date</ng-template>
</div>
</div>
`,
})
export default class Example {}Radio choices
Radio items are exclusive within their nearest named group.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-group/ui-menu-group.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-menu
open
aria-label="View options"
>
<ui-menu-group
label="Density"
>
<ui-menu-item
type="radio"
name="density"
value="comfortable"
checked
>Comfortable</ui-menu-item>
<ui-menu-item
type="radio"
name="density"
value="compact"
>Compact</ui-menu-item>
</ui-menu-group>
</ui-menu><script setup>
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiMenu
:open="true"
aria-label="View options"
>
<UiMenuGroup
label="Density"
>
<UiMenuItem
type="radio"
name="density"
value="comfortable"
:checked="true"
>Comfortable</UiMenuItem>
<UiMenuItem
type="radio"
name="density"
value="compact"
>Compact</UiMenuItem>
</UiMenuGroup>
</UiMenu>
</template>import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
open
aria-label="View options"
>
<UiMenuGroup
label="Density"
>
<UiMenuItem
type="radio"
name="density"
value="comfortable"
checked
>Comfortable</UiMenuItem>
<UiMenuItem
type="radio"
name="density"
value="compact"
>Compact</UiMenuItem>
</UiMenuGroup>
</UiMenu>;
}/** @jsxImportSource preact */
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
open
aria-label="View options"
>
<UiMenuGroup
label="Density"
>
<UiMenuItem
type="radio"
name="density"
value="comfortable"
checked
>Comfortable</UiMenuItem>
<UiMenuItem
type="radio"
name="density"
value="compact"
>Compact</UiMenuItem>
</UiMenuGroup>
</UiMenu>;
}<script>
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
</script>
<UiMenu
open
aria-label="View options"
>
<UiMenuGroup
label="Density"
>
<UiMenuItem
type="radio"
name="density"
value="comfortable"
checked
>Comfortable</UiMenuItem>
<UiMenuItem
type="radio"
name="density"
value="compact"
>Compact</UiMenuItem>
</UiMenuGroup>
</UiMenu>import { Component } from "@angular/core";
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiMenu, UiMenuGroup, UiMenuItem],
template: `
<div
ui-menu
[open]="true"
aria-label="View options"
>
<div
ui-menu-group
[label]="'Density'"
>
<ng-template
ui-menu-item
[type]="'radio'"
[value]="'comfortable'"
[checked]="true"
[hnAttributes]="{ name: 'density' }"
>Comfortable</ng-template>
<ng-template
ui-menu-item
[type]="'radio'"
[value]="'compact'"
[hnAttributes]="{ name: 'density' }"
>Compact</ng-template>
</div>
</div>
`,
})
export default class Example {}Default
With disabled omitted, Menu Item is selectable inside its required menu context.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-menu
role="menu"
aria-label="Actions"
open
>
<ui-menu-item
value="item"
>Menu item</ui-menu-item>
</ui-menu><script setup>
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiMenu
role="menu"
aria-label="Actions"
:open="true"
>
<UiMenuItem
value="item"
>Menu item</UiMenuItem>
</UiMenu>
</template>import { UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
role="menu"
aria-label="Actions"
open
>
<UiMenuItem
value="item"
>Menu item</UiMenuItem>
</UiMenu>;
}/** @jsxImportSource preact */
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
role="menu"
aria-label="Actions"
open
>
<UiMenuItem
value="item"
>Menu item</UiMenuItem>
</UiMenu>;
}<script>
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<UiMenu
role="menu"
aria-label="Actions"
open
>
<UiMenuItem
value="item"
>Menu item</UiMenuItem>
</UiMenu>import { Component } from "@angular/core";
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiMenu, UiMenuItem],
template: `
<div
ui-menu
role="menu"
aria-label="Actions"
[open]="true"
>
<ng-template
ui-menu-item
[value]="'item'"
>Menu item</ng-template>
</div>
`,
})
export default class Example {}disabled
Disabled retains the row label while removing the item from selection.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-menu
role="menu"
aria-label="Publishing actions"
open
>
<ui-menu-item
value="publish"
>Publish now</ui-menu-item>
<ui-menu-item
value="schedule"
disabled
>Schedule publishing</ui-menu-item>
</ui-menu><script setup>
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiMenu
role="menu"
aria-label="Publishing actions"
:open="true"
>
<UiMenuItem
value="publish"
>Publish now</UiMenuItem>
<UiMenuItem
value="schedule"
:disabled="true"
>Schedule publishing</UiMenuItem>
</UiMenu>
</template>import { UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
role="menu"
aria-label="Publishing actions"
open
>
<UiMenuItem
value="publish"
>Publish now</UiMenuItem>
<UiMenuItem
value="schedule"
disabled
>Schedule publishing</UiMenuItem>
</UiMenu>;
}/** @jsxImportSource preact */
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
role="menu"
aria-label="Publishing actions"
open
>
<UiMenuItem
value="publish"
>Publish now</UiMenuItem>
<UiMenuItem
value="schedule"
disabled
>Schedule publishing</UiMenuItem>
</UiMenu>;
}<script>
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<UiMenu
role="menu"
aria-label="Publishing actions"
open
>
<UiMenuItem
value="publish"
>Publish now</UiMenuItem>
<UiMenuItem
value="schedule"
disabled
>Schedule publishing</UiMenuItem>
</UiMenu>import { Component } from "@angular/core";
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiMenu, UiMenuItem],
template: `
<div
ui-menu
role="menu"
aria-label="Publishing actions"
[open]="true"
>
<ng-template
ui-menu-item
[value]="'publish'"
>Publish now</ng-template>
<ng-template
ui-menu-item
[value]="'schedule'"
[disabled]="true"
>Schedule publishing</ng-template>
</div>
`,
})
export default class Example {}Link and shortcut
A destination renders a native anchor, while the shortcut slot is a display hint and does not bind a key.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<ui-menu
open
aria-label="File actions"
>
<ui-menu-item
value="templates"
href="#templates"
>Browse templates</ui-menu-item>
<ui-menu-item
value="new"
>
New file
<kbd
slot="shortcut"
>⌘N</kbd>
</ui-menu-item>
</ui-menu><script setup>
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiMenu
:open="true"
aria-label="File actions"
>
<UiMenuItem
value="templates"
href="#templates"
>Browse templates</UiMenuItem>
<UiMenuItem
value="new"
>New file <template #shortcut>
<kbd>⌘N</kbd>
</template></UiMenuItem>
</UiMenu>
</template>import { UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
open
aria-label="File actions"
>
<UiMenuItem
value="templates"
href="#templates"
>Browse templates</UiMenuItem>
<UiMenuItem
value="new"
slots={{
shortcut: <kbd>⌘N</kbd>,
}}
>New file </UiMenuItem>
</UiMenu>;
}/** @jsxImportSource preact */
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
open
aria-label="File actions"
>
<UiMenuItem
value="templates"
href="#templates"
>Browse templates</UiMenuItem>
<UiMenuItem
value="new"
slots={{
shortcut: <kbd>⌘N</kbd>,
}}
>New file </UiMenuItem>
</UiMenu>;
}<script>
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>
{#snippet shortcut()}
<kbd>⌘N</kbd>
{/snippet}
<UiMenu
open
aria-label="File actions"
>
<UiMenuItem
value="templates"
href="#templates"
>Browse templates</UiMenuItem>
<UiMenuItem
value="new"
slots={{ shortcut }}
>New file </UiMenuItem>
</UiMenu>import { Component } from "@angular/core";
import { UiMenu, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiMenu, UiMenuItem],
template: `
<div
ui-menu
[open]="true"
aria-label="File actions"
>
<ng-template
ui-menu-item
[value]="'templates'"
[href]="'#templates'"
>Browse templates</ng-template>
<ng-template #shortcut>
<kbd>⌘N</kbd>
</ng-template>
<ng-template
ui-menu-item
[value]="'new'"
[slots]="{ shortcut: shortcut }"
>New file </ng-template>
</div>
`,
})
export default class Example {}Checkable items
Checked state is announced through menuitemcheckbox or menuitemradio. A checked item emits change and the menu stays open.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-menu-group/ui-menu-group.html"
>
<ui-menu
open
aria-label="View options"
>
<ui-menu-item
type="checkbox"
value="grid"
checked
>Show grid</ui-menu-item>
<ui-menu-group
label="Sort by"
>
<ui-menu-item
type="radio"
value="name"
checked
>Name</ui-menu-item>
<ui-menu-item
type="radio"
value="date"
>Date</ui-menu-item>
</ui-menu-group>
</ui-menu><script setup>
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
</script>
<template>
<UiMenu
:open="true"
aria-label="View options"
>
<UiMenuItem
type="checkbox"
value="grid"
:checked="true"
>Show grid</UiMenuItem>
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
:checked="true"
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>
</template>import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
open
aria-label="View options"
>
<UiMenuItem
type="checkbox"
value="grid"
checked
>Show grid</UiMenuItem>
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
checked
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>;
}/** @jsxImportSource preact */
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
export default function Example() {
return <UiMenu
open
aria-label="View options"
>
<UiMenuItem
type="checkbox"
value="grid"
checked
>Show grid</UiMenuItem>
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
checked
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>;
}<script>
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
</script>
<UiMenu
open
aria-label="View options"
>
<UiMenuItem
type="checkbox"
value="grid"
checked
>Show grid</UiMenuItem>
<UiMenuGroup
label="Sort by"
>
<UiMenuItem
type="radio"
value="name"
checked
>Name</UiMenuItem>
<UiMenuItem
type="radio"
value="date"
>Date</UiMenuItem>
</UiMenuGroup>
</UiMenu>import { Component } from "@angular/core";
import { UiMenu, UiMenuGroup, UiMenuItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiMenu, UiMenuItem, UiMenuGroup],
template: `
<div
ui-menu
[open]="true"
aria-label="View options"
>
<ng-template
ui-menu-item
[type]="'checkbox'"
[value]="'grid'"
[checked]="true"
>Show grid</ng-template>
<div
ui-menu-group
[label]="'Sort by'"
>
<ng-template
ui-menu-item
[type]="'radio'"
[value]="'name'"
[checked]="true"
>Name</ng-template>
<ng-template
ui-menu-item
[type]="'radio'"
[value]="'date'"
>Date</ng-template>
</div>
</div>
`,
})
export default class Example {}API
Package: @threadlabs/looma · Native root: div
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
density | Compact trades padding and type size for rows that fit more on screen. | density | keyword | comfortable | comfortable, compact |
for | ID of the control that toggles and anchors the menu. | for | string | | — |
icons | Reserves a fixed icon column in every item, including items without an icon. Supply item icons through their icon slot. | icons | boolean | false | — |
inline | Displays the menu in the page flow without an overlay or trigger, for an embedded list of actions. | inline | boolean | false | — |
open | Whether the menu is open initially or when set externally. | open | boolean | false | — |
placement | Positions an associated menu relative to its trigger. | placement | string | bottom-start | — |
size | sm sizes the menu to a small trigger, such as an xs or sm button: smallest text, short rows, a small check, and a narrower minimum width. md is the standard size. | size | keyword | md | sm, md |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
density | "comfortable" | "compact" | comfortable | comfortable, compact |
for | string | | — |
icons | boolean | false | — |
inline | boolean | false | — |
open | boolean | false | — |
placement | string | bottom-start | — |
size | "sm" | "md" | md | sm, md |
Events
| Name | Detail | Description |
|---|---|---|
close | object
| Emitted when the component transitions to a closed state. |
open | object
| Emitted when the component transitions to an open state. |
select | object
| Emitted when a selectable option becomes active. |
Slots
| Name | Purpose |
|---|---|
default | Default child content. |
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
| Token | Declared values | Fallback |
|---|---|---|
--ui-menu-border | — | var(--ui-overlay-border, var(--ui-border-strong)) | color-mix(in oklab, var(--ui-overlay-border, var(--ui-border-strong)) calc((1 - var(--_ui-material-active)) * 100%), transparent) |
--ui-menu-radius | — | var(--ui-overlay-radius, var(--ui-radius-lg)) |
--ui-menu-shadow | — | var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))) | var(
--ui-overlay-shadow,
0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 0.28) * 8px) calc(
var(--_ui-material-separation) * var(--_ui-material-scale, 0.28) * 24px
) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
var(--ui-material-glow, 0 0 transparent),
inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(--ui-material-edge, var(--ui-border-strong)),
var(--ui-elevation-overlay, var(--ui-shadow-lg))
) |
--ui-menu-surface | — | var(--ui-overlay-surface, var(--ui-surface-elevated)) | var(--_ui-material-underpaint, var(--ui-material-content, var(--ui-overlay-surface, var(--ui-surface-elevated)))) |
--ui-menu-viewport-gap | — | var(--ui-space-2) |
Shared tokens
| Token | Fallback |
|---|---|
--ui-border-divider | — |
--ui-border-strong | — |
--ui-border-width | — |
--ui-elevation-overlay | var(--ui-shadow-lg) |
--ui-font-size-sm | — |
--ui-font-size-xs | — |
--ui-glassiness | 0 |
--ui-material-content | var(--ui-overlay-surface, var(--ui-surface-elevated)) |
--ui-material-edge | var(--ui-border-strong) |
--ui-material-glass | var(--ui-surface-elevated) |
--ui-material-glow | 0 0 transparent |
--ui-material-highlight | var(--ui-surface-highlight, linear-gradient(transparent, transparent)) |
--ui-motion-ease | — |
--ui-motion-fast | — |
--ui-overlay-border | var(--ui-border-strong) |
--ui-overlay-radius | var(--ui-radius-lg) |
--ui-overlay-shadow | var(--ui-elevation-overlay, var(--ui-shadow-lg)) | 0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 0.28) * 8px) calc(
var(--_ui-material-separation) * var(--_ui-material-scale, 0.28) * 24px
) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
var(--ui-material-glow, 0 0 transparent),
inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(--ui-material-edge, var(--ui-border-strong)),
var(--ui-elevation-overlay, var(--ui-shadow-lg)) |
--ui-overlay-surface | var(--ui-surface-elevated) |
--ui-radius-lg | — |
--ui-radius-sm | — |
--ui-shadow-lg | — |
--ui-space-0-5 | — |
--ui-space-1 | — |
--ui-space-2 | — |
--ui-surface-elevated | — |
--ui-surface-highlight | linear-gradient(transparent, transparent) |
--ui-surface-separation | 0 |
--ui-text-primary | — |
Menu Group API
Use a named group to organize related commands or choices within a Menu or Context Menu.
Package: @threadlabs/looma · Native root: div
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
label | Visible heading and accessible name for this group of menu items. | label | string | | — |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
label | string | | — |
Events
No events.
Slots
| Name | Purpose |
|---|---|
default | Default child content. |
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
No component tokens.
Shared tokens
| Token | Fallback |
|---|---|
--ui-font-semibold | — |
--ui-font-size-sm | — |
--ui-space-1 | — |
--ui-space-2 | — |
--ui-space-3 | — |
--ui-text-muted | — |
Menu Item API
Use an item inside a Menu or Context Menu.
Package: @threadlabs/looma · Native root: div
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
checked | Initial or externally controlled checked state of a checkbox or radio item. | checked | boolean | false | — |
disabled | Makes the item unavailable: it is faded, skipped by the arrow keys, and choosing it does nothing. It stays visible and is announced as disabled. | disabled | boolean | false | — |
href | A destination makes an action item a real link, with native navigation and link context actions. | href | string | | — |
tone | Danger identifies a destructive action without changing its behavior. | tone | keyword | neutral | neutral, danger |
type | An action closes its menu; a checkbox toggles, and a radio chooses one item within its nearest Menu Group. Checkable items keep the menu open. | type | keyword | action | action, checkbox, radio |
value | The value the enclosing menu reports in its select event when this item is chosen. Give each item a distinct value so one handler can tell them apart. | value | string | | — |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
checked | boolean | false | — |
disabled | boolean | false | — |
href | string | | — |
tone | "neutral" | "danger" | neutral | neutral, danger |
type | "action" | "checkbox" | "radio" | action | action, checkbox, radio |
value | string | | — |
Events
| Name | Detail | Description |
|---|---|---|
change | object
| Emitted when a toggleable control changes checked state. |
Slots
| Name | Purpose |
|---|---|
icon | Named content for the icon region. |
default | Default child content. |
shortcut | Named content for the shortcut region. |
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
| Token | Declared values | Fallback |
|---|---|---|
--ui-menu-item-font-size | — | var(--ui-font-size-md) |
--ui-menu-item-hover-surface | — | var(--ui-accent-subtle) |
--ui-menu-item-min-block-size | — | var(--ui-control-size-md, 2.5rem) |
--ui-menu-item-padding-block | — | var(--ui-space-2) |
--ui-menu-item-padding-inline | — | var(--ui-space-3) |
--ui-menu-item-radius | — | var(--ui-selection-radius) |
Shared tokens
| Token | Fallback |
|---|---|
--ui-accent | — |
--ui-accent-subtle | — |
--ui-border-width | — |
--ui-control-border | — |
--ui-control-size-md | 2.5rem |
--ui-danger-subtle-text | — |
--ui-disabled-border | — |
--ui-disabled-surface | — |
--ui-disabled-text | — |
--ui-focus-ring | — |
--ui-focus-width | — |
--ui-font-medium | — |
--ui-font-normal | — |
--ui-font-size-md | — |
--ui-font-size-sm | — |
--ui-icon-size-sm | — |
--ui-motion-ease | — |
--ui-motion-fast | — |
--ui-radius-sm | — |
--ui-selection-radius | — |
--ui-selection-surface | — |
--ui-selection-text | — |
--ui-space-2 | — |
--ui-space-3 | — |
--ui-surface | — |
--ui-text-primary | — |
--ui-text-secondary | — |