Navigation
Hamburger Menu
Architectural line morphing & symmetrical spring motion
detent-hamburger-menu
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 2
- Wave
- phase-2-navigation
- Group
- Navigation
2.5 KB gzip for the class module
Variants
Closed (the preview above), customised (size="40", color="#8fb0ff"), already open
(is-open) and disabled. Activation by click, Space or Enter emits one detent-change with
{ isOpen }; Escape closes an open menu. The lines are aria-hidden decoration and the
button's label switches between Open <label> and Close <label>.
Recorded states
focus-visiblerecorded state; no setterdisabledrecorded state; no setteropenrecorded state; no setterexpandedrecorded state; no setter
Usage
Import
import '/components/hamburger-menu/define.js';
// or register it yourself
import { DetentHamburgerMenu } from '/components/hamburger-menu/hamburger-menu.js';
Attributes
colordisabledis-openlabelsize
Properties
isOpensizecolorlabeldisabledonChange
Slots
No slots are recorded for this component.
Events
detent-change— { isOpen: boolean }
Example
Runnable example: site/examples/hamburger-menu.ts. The snippet below is generated from the catalogued attributes.
<detent-hamburger-menu
color="currentColor"
disabled
is-open
label="Menu"
size="24"
></detent-hamburger-menu>
Property-only values
const element = document.querySelector('detent-hamburger-menu');
if (element !== null) {
// onChange: (isOpen: boolean) => void
element.onChange = () => {};
}
Source
The pinned upstream source for this row:
src/components/ui/HamburgerMenu.tsxsha256 cf8c257f7551…
Native files
The native module and its artifacts:
src/components/hamburger-menu/hamburger-menu.tssrc/components/hamburger-menu/styles.csssrc/components/hamburger-menu/define.tsdocs/components/hamburger-menu.mdsite/examples/hamburger-menu.tstests/browser/hamburger-menu.spec.tsevidence/components/hamburger-menu.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 |
|---|---|---|---|---|
color | color | string | 'currentColor' | Kebab-case attribute; camelCase property. |
disabled | disabled | boolean | false | Presence semantics: present means true, absent means false. |
is-open | isOpen | boolean | — | Presence semantics: present means true, absent means false. |
label | label | string | 'Menu' | Kebab-case attribute; camelCase property. |
size | size | number | 24 | Kebab-case attribute; camelCase property. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
isOpen | boolean | — | Property-only: assign from script; not accepted as an attribute. |
size | number | 24 | Property-only: assign from script; not accepted as an attribute. |
color | string | 'currentColor' | Property-only: assign from script; not accepted as an attribute. |
label | string | 'Menu' | Property-only: assign from script; not accepted as an attribute. |
disabled | boolean | false | Property-only: assign from script; not accepted as an attribute. |
onChange | (isOpen: boolean) => void | — | Property-only: assign from script; not accepted as an attribute. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-change | onChange | (isOpen: boolean) => void | Composed, bubbles; fires only for a user action. |
Slots
No slots are recorded for this component.
CSS customisation
- 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.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- Upstream is controlled (`isOpen` + `onChange` required, no default); the element owns the state, defaults `isOpen` to false and reflects `is-open` (the catalog `null` means required upstream, with no default).
- `onChange` becomes the `detent-change` event; Escape-to-close implements the recorded catalog feature the pinned source lacks (only while focused and open).
- `disabled:opacity-40` becomes a full-opacity icon with `cursor: not-allowed` (PC-12.6); the springSnappy morph becomes a CSS transform/opacity transition; `className` maps to parts and properties.
What it does
A three-line menu toggle that morphs into a symmetric close mark with zero layout shift.
Upstream behaviour
- Symmetric line rotation and middle line dissolution with zero layout shift
- Customizable size, stroke thickness, and color tokens
- Keyboard accessible with Space and Enter triggers and Escape close support
- Integrated aria-expanded and dynamic aria-label status updates
Differences from upstream
- 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.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- Upstream is controlled (`isOpen` + `onChange` required, no default); the element owns the state, defaults `isOpen` to false and reflects `is-open` (the catalog `null` means required upstream, with no default).
- `onChange` becomes the `detent-change` event; Escape-to-close implements the recorded catalog feature the pinned source lacks (only while focused and open).
- `disabled:opacity-40` becomes a full-opacity icon with `cursor: not-allowed` (PC-12.6); the springSnappy morph becomes a CSS transform/opacity transition; `className` maps to parts and properties.