Navigation
Glass Navbar
Refined glass navigation with spring physics
detent-glass-navbar
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 2
- Wave
- phase-2-navigation
- Group
- Navigation
7.5 KB gzip for the class module
Variants
The bar in the preview above is the flat floating pill (default) with a slotted brand and CTA; the first bar here is
variant="full-width" with active-id="Docs"; the second is flat and non-sticky; the third
opts into the glass surface with glass. Flat is the default so the catalog has one surface language;
glass stays available where a nav sits over media. Under 768px every bar collapses to a named drawer behind an
aria-expanded toggle that Escape closes.
Recorded states
hoverrecorded state; no setterfocus-visiblerecorded state; no setterclosedrecorded state; no setterexpandedrecorded state; no setteractiverecorded state; no setter
Usage
Import
import '/components/glass-navbar/define.js';
// or register it yourself
import { DetentGlassNavbar } from '/components/glass-navbar/glass-navbar.js';
Attributes
active-idbrand-hrefglassstickyvariant
Properties
itemsvariantstickyglassbrandHrefactiveIddrawerOpencurrentIdonItemSelect
Slots
brandcta
Events
detent-item-select— { id: string; label: string; href: string; badge?: string; external?: boolean }
Example
Runnable example: site/examples/glass-navbar.ts. The snippet below is generated from the catalogued attributes.
<detent-glass-navbar
active-id
brand-href="/"
glass
sticky
variant="floating"
></detent-glass-navbar>
Property-only values
const element = document.querySelector('detent-glass-navbar');
if (element !== null) {
// items: NavItem[]
element.items = [];
// onItemSelect: (item: NavItem) => void
element.onItemSelect = () => {};
}
Source
The pinned upstream source for this row:
src/components/ui/GlassNavbar.tsxsha256 89b6c065c20f…
Native files
The native module and its artifacts:
src/components/glass-navbar/glass-navbar.tssrc/components/glass-navbar/styles.csssrc/components/glass-navbar/define.tsdocs/components/glass-navbar.mdsite/examples/glass-navbar.tstests/browser/glass-navbar.spec.tsevidence/components/glass-navbar.md
API
Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.
Attributes
| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
active-id | activeId | string | undefined | Kebab-case attribute; camelCase property. |
brand-href | brandHref | string | '/' | Kebab-case attribute; camelCase property. |
glass | glass | boolean | true | Presence semantics: present means true, absent means false. |
sticky | sticky | boolean | true | Presence semantics: present means true, absent means false. |
variant | variant | 'floating' | 'full-width' | 'floating' | Kebab-case attribute; camelCase property. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
items | NavItem[] | Default items array | Property-only: assign from script; not accepted as an attribute. |
variant | 'floating' | 'full-width' | 'floating' | Property-only: assign from script; not accepted as an attribute. |
sticky | boolean | true | Property-only: assign from script; not accepted as an attribute. |
glass | boolean | true | Property-only: assign from script; not accepted as an attribute. |
brandHref | string | '/' | Property-only: assign from script; not accepted as an attribute. |
activeId | string | undefined | Property-only: assign from script; not accepted as an attribute. |
drawerOpen | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
currentId | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
onItemSelect | (item: NavItem) => void | undefined | Property-only: assign from script; not accepted as an attribute. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-item-select | onItemSelect | (item: NavItem) => void | Composed, bubbles; fires only for a user action. |
Slots
| Slot | Notes |
|---|---|
brand | Consumer content; rendered through a slot, never re-created. |
cta | Consumer content; rendered through a slot, never re-created. |
CSS customisation
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- The drawer does not repeat the CTA slot (a slotted node cannot render twice).
- `NavItem.icon` is a text glyph with `aria-hidden`; `NavItem.onClick` has no native form (native click plus `detent-item-select`) and `className` maps to parts and custom properties.
- `sticky` defaults true (property opt-out); `glass` defaults false and the attribute/property opts in, so the default surface is flat (PC-3.2).
- The glass surface is a token `color-mix` at 92% plus `backdrop-filter` rather than `#0E0E0E/90`; sticky positioning lives on the host.
- The layoutId pills glide on the shared helper with the pinned springSnappy (hover, 380/30/0.5) and springGentle (active, 170/26/0.9); one progress spring moves position and size together, and a retarget restarts at zero velocity.
What it does
A translucent navigation bar with real links, a hover spotlight, an active pill and a mobile drawer with a labelled toggle.
Upstream behaviour
- Subtle glassmorphic blur backdrop (bg-[#050505]/85 backdrop-blur-md)
- Dual layout modes: Floating pill or full-width sticky bar
- Shared layout spring animations for hover spotlight & active indicators
- Responsive mobile menu drawer with smooth Framer Motion spring transition
- Customizable brand logo, navigation items, badges, and CTA action slot
- Full keyboard navigation, Escape key dismiss, and aria-expanded accessibility
Differences from upstream
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- The drawer does not repeat the CTA slot (a slotted node cannot render twice).
- `NavItem.icon` is a text glyph with `aria-hidden`; `NavItem.onClick` has no native form (native click plus `detent-item-select`) and `className` maps to parts and custom properties.
- `sticky` defaults true (property opt-out); `glass` defaults false and the attribute/property opts in, so the default surface is flat (PC-3.2).
- The glass surface is a token `color-mix` at 92% plus `backdrop-filter` rather than `#0E0E0E/90`; sticky positioning lives on the host.
- The layoutId pills glide on the shared helper with the pinned springSnappy (hover, 380/30/0.5) and springGentle (active, 170/26/0.9); one progress spring moves position and size together, and a retarget restarts at zero velocity.