Detent web components
By Shane Logsdon

Navigation

Glass Navbar

Implemented

Refined glass navigation with spring physics

detent-glass-navbar

Port status
Catalog status
implemented
Origin
upstream
Phase
2
Wave
phase-2-navigation
Group
Navigation
Nebula Get started

7.5 KB gzip for the class module

Variants

Nebula Docs Pricing Nebula Glass

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

  • hover recorded state; no setter
  • focus-visible recorded state; no setter
  • closed recorded state; no setter
  • expanded recorded state; no setter
  • active recorded 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-id
  • brand-href
  • glass
  • sticky
  • variant

Properties

  • items
  • variant
  • sticky
  • glass
  • brandHref
  • activeId
  • drawerOpen
  • currentId
  • onItemSelect

Slots

  • brand
  • cta

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.tsx sha256 89b6c065c20f…

Native files

The native module and its artifacts:

  • src/components/glass-navbar/glass-navbar.ts
  • src/components/glass-navbar/styles.css
  • src/components/glass-navbar/define.ts
  • docs/components/glass-navbar.md
  • site/examples/glass-navbar.ts
  • tests/browser/glass-navbar.spec.ts
  • evidence/components/glass-navbar.md

API

Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.

Attributes

Attributes
AttributePropertyTypeDefaultNotes
active-idactiveIdstringundefinedKebab-case attribute; camelCase property.
brand-hrefbrandHrefstring'/'Kebab-case attribute; camelCase property.
glassglassbooleantruePresence semantics: present means true, absent means false.
stickystickybooleantruePresence semantics: present means true, absent means false.
variantvariant'floating' | 'full-width''floating'Kebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
itemsNavItem[]Default items arrayProperty-only: assign from script; not accepted as an attribute.
variant'floating' | 'full-width''floating'Property-only: assign from script; not accepted as an attribute.
stickybooleantrueProperty-only: assign from script; not accepted as an attribute.
glassbooleantrueProperty-only: assign from script; not accepted as an attribute.
brandHrefstring'/'Property-only: assign from script; not accepted as an attribute.
activeIdstringundefinedProperty-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) => voidundefinedProperty-only: assign from script; not accepted as an attribute.

Events

Events
EventUpstream propPayload typeNotes
detent-item-selectonItemSelect(item: NavItem) => voidComposed, bubbles; fires only for a user action.

Slots

Slots
SlotNotes
brandConsumer content; rendered through a slot, never re-created.
ctaConsumer 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.
Previous: Funnel Chart Next: Glitch Text

Docs

Components

Project