Six line icons that morph into a second state: menu to close, plus to close, copy to check, play to pause, sun to moon, and a check that draws itself.
Nav Fullscreen
A bar with the name or the mark and one Menu button, and behind the button the whole site in type as large as the screen allows: the menu covers the page, its items arrive one after another, and each label rolls on hover. The menu is StaggerMenu, a native modal <dialog> opened by the invoker command, so the focus stays inside it, Escape closes it and the focus returns to the button; the current page is marked with aria-current. The bar sticks on the page's ground, or scrolls away with the button kept in the corner. Zero JavaScript of its own.
Live demo
a sample page under the bar
Open the menu: the site in type as large as the screen.
Live, from the library itself, at full size: scroll, hover, navigate away and back. The demo is never gated.
Variants · each row writes the attribute the prop writes; the section's own CSS answers
Measured
JavaScript of its own
0 B
CSS 19.0 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.
Browser support
Chrome 135 · Firefox 144 · Safari 26.2; needs command, dialog, starting-style, allow-discrete. Elsewhere: polyfill (on by default) opens the menu where invoker commands are missing (Safari before 26.2, Firefox before 144); without @starting-style the menu opens without the stagger; Safari closes it on Escape and the close button, not on a backdrop click.
Install Pro
The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:
npx shadcn@latest add @moonarc-pro/nav-fullscreen---
import NavFullscreen from '@/components/moonarc/pro/NavFullscreen.astro';
---
<NavFullscreen
brand={{ href: '/', label: 'Northfold' }}
links={[
{ href: '/work/', label: 'Work' },
{ href: '/about/', label: 'Studio' },
{ href: '/journal/', label: 'Journal' },
{ href: '/contact/', label: 'Contact' },
]}
current="/work/"
themeToggle
>
<p slot="foot"><a href="mailto:hello@studio.example">hello@studio.example</a></p>
</NavFullscreen>Without a key the registry answers 401 with an upgrade message. Everything this section composes is free: stagger-menu, theme-toggle.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
brand | { href: string; label: string } | none | The link home. Its text is label; the brand slot puts a mark in the link instead (give the mark alt="" and keep the name as text, or name it). |
links | { href: string; label: string }[] | none | The menu's links, numbered in order in mono. |
current | string | none | href of the link to mark with aria-current="page": the page's own, or the section it sits in (/work/ on a case study). |
id | string | 'ma-navfs-menu' | Id of the menu's dialog; unique per page. |
label | string | 'Menu' | The button's word and the menu's accessible name. |
closeLabel | string | 'Close menu' | Accessible name of the close button. |
showLabel | boolean | true | Print label beside the burger; false leaves the round button, named by label. |
themeToggle | boolean | false | Core ThemeToggle beside the menu button; hidden without JavaScript. |
polyfill | boolean | true | Inline the invoker handler (403 B raw) for browsers without command / commandfor. Off, the menu does not open in Safari before 26.2 and Firefox before 144. |
mode | 'sticky' | 'float' | 'static' | 'sticky' | sticky: the bar stays at the top on the page's ground. float: the bar scrolls away and the menu button stays in the corner. static: the bar scrolls away whole. |
tone | 'surface' | 'muted' | 'ink' | 'brand' | unset (the page's ground) | The bar's ground. The menu is the ink pair on every tone but ink, where it takes the page's own ground and text. |
density | 'compact' | 'cozy' | 'roomy' | 'cozy' | Bar padding ×0.8 / ×1 / ×1.25 through --ma-density. |
Theming and overrides
The section reads the theme contract (shadcn's variable names with the base tokens as fallback), so a pasted theme or one --ma-brand recolours it. Its class names are the override surface and stay stable; a named slot replaces the matching prop with your own markup.
Classes
.ma-navfs.ma-navfs__actions.ma-navfs__bar.ma-navfs__brand.ma-navfs__foot.ma-navfs__menu.ma-navfs__theme.ma-navfs__tools
Slots
slot="brand"slot="actions"slot="foot"
Reduced motion
The menu and its items cross-fade together in 150 ms with no travel and no stagger (StaggerMenu), and the labels do not roll.
With ClientRouter
Native; nothing to rebind. Following a link closes the menu with the page, and the next page marks its own current link.
Why it is built this way
- A modal <dialog> is the menu: the focus trap, Escape and the focus returning to the button cost nothing and cannot drift from the platform.
- The menu is StaggerMenu set through its hooks (--ma-smenu-font, --ma-smenu-size, --ma-smenu-weight, --ma-smenu-tracking, the trigger's ground and edge), never its rules: the section decides the display face and the size, the primitive keeps its behaviour.
- Items at clamp(2.75rem, 10vw, 8rem) in the display face, tracked tight, each numbered in mono: on a phone the four links of a small site still fill the screen, which is the point of a full-screen menu.
- The button prints its word (Menu) beside the burger: a lone icon is a guess for a first-time visitor, and the word is its accessible name too.
- The panel is the ink pair whatever the page's scheme, so opening the menu is a change of light; on an ink bar it inverts to the page's own ground.
Replaces: StaggerMenu (React Bits) · Fullscreen menus (Framer studio templates) · FlyoutNav (Hover.dev). See the migration table.
Related
Open the menu.
full · top · right: items in, reversed out
A burger that opens a full-screen menu (or a top sheet, or a side panel) whose items arrive one after another and leave in reverse.
Text Roll
TextA label rolls upward into a copy of itself on hover, letter by letter from the left, and rolls back when the pointer leaves.
A sticky navigation bar that condenses over the first 120 px of scroll: the row tightens and a hairline appears, bound to the document's scroll timeline, with zero JavaScript.
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown