Skip to content
llms.txt
llms.txt

Menu

Open the live menu demo

Live demo

Menu

Source for this preview

MenuExample.astro
---
import {MENU_LABELS} from '../../example-data';
---
<article id="menu-demo" class="demo-card menu-card">
<div class="card-top">
<span class="number">C</span><span class="chip">roving focus</span>
</div>
<h3>Command menu</h3>
<p>
Arrow keys, looping, and typeahead share one list registry. Try typing
“signal”.
</p>
<div class="card-action">
<floating-root
id="menu-root"
interactions="click dismiss"
floating-role="menu"
placement="bottom-start"
>
<floating-reference>
<button class="dark-button">
Open navigator <span aria-hidden="true">⌄</span>
</button>
</floating-reference>
<template slot="content">
<div class="menu-panel">
<div class="menu-heading">
<span>Jump to a field</span>
<span class="menu-shortcuts" aria-label="Arrow keys to navigate, Enter to select, Escape to close">
<kbd>↑</kbd><kbd>↓</kbd><kbd>↵</kbd><kbd>Esc</kbd>
</span>
</div>
<floating-list
navigation
typeahead
loop
item-selector=".menu-item"
>
{
MENU_LABELS.map((label) => (
<button
class="menu-item"
role="menuitem"
data-fup-close
aria-label={label}
>
<span>{label}</span><kbd>{label.slice(0, 1).toUpperCase()}</kbd>
</button>
))
}
</floating-list>
</div>
</template>
</floating-root>
</div>
<code>&lt;floating-list navigation typeahead item-selector="…" /&gt;</code>
</article>
<script>
import {
FloatingRootElement,
flip,
offset,
size,
shift,
transformOrigin,
} from '@floating-ui-plus/web-components';
import {initializeExample} from './initialize-example';
initializeExample('menu', (scope) => {
const floating = FloatingRootElement.query(scope, '#menu-root');
floating.configure({
middleware: [
offset(8),
flip({padding: 18}),
shift({padding: 18}),
size({
padding: 18,
rootBoundary: 'viewport',
apply({availableHeight, elements}) {
elements.floating.style.setProperty(
'--menu-panel-max-height',
`${Math.max(0, availableHeight)}px`,
);
},
}),
transformOrigin({padding: 12}),
],
});
});
</script>

Open the full demo ↗

The menu’s registry owns active index, roving focus, typeahead labels, and wrapping:

<floating-list
navigation
typeahead
loop
item-selector=".menu-item"
>
<button class="menu-item" role="menuitem" data-fup-close aria-label="Signal">
Signal
</button>
</floating-list>

Open the demo and type “signal” to exercise typeahead. Arrow keys move the active item, Enter selects it, and Escape dismisses the surface. In Vue, use FloatingList navigation typeahead loop with FloatingListItem and bind the same active-index model when the application needs it.

The menu root uses click(), dismiss(), role({role: 'menu'}), and offset(8) → flip() → shift({padding: 18}). data-fup-close sends a normal close request, so before-close guards still apply.