Ziteox Forge

HamburgerButton

CSS-driven mobile menu trigger.

HamburgerButton

Domain: navigation
Package path: packages/ui/src/navigation/hamburger-button.tsx
Import: @ziteox/ui or @ziteox/ui/navigation

Mobile menu trigger. Three SVG bars morph into an X via CSS transitions driven by aria-expanded.

Preview

HamburgerButton

Usage

import { useState } from "react";
import { HamburgerButton } from "@ziteox/ui";

export function MobileNavTrigger() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <HamburgerButton
        variant="circle"
        isOpen={isOpen}
        onOpenChange={setIsOpen}
        className="lg:hidden"
      />
      {/* App-owned menu overlay/sheet */}
    </>
  );
}

Props

PropTypeRequiredDescription
isOpenbooleanYesOpen state; sets aria-expanded
onOpenChange(isOpen: boolean) => voidYesCalled on click with toggled value
classNamestringNoAppended to root button classes
variant"default" | "circle"Nocircle is fully rounded (recommended for mobile nav)
hapticboolean | preset nameNoWebHaptics feedback on tap; default selection; false disables

Install peer: pnpm add web-haptics

Accessibility

AttributeValue
aria-expandedMirrors isOpen
aria-label"Open menu" / "Close menu" based on state
Screen readerHidden "Menu" text via ziteox-sr-only

Styling

Default appearance: frosted glass in both themes — backdrop-filter blur, semi-transparent fill, and layered inset/outset shadows. Light mode uses a bright frosted panel with dark bars; dark mode (.dark / [data-theme="dark"] on an ancestor) uses a light frosted panel with white bars.

Scoped classes:

  • ziteox-hamburger-button
  • ziteox-hamburger-button--circle (fully rounded variant)
  • ziteox-hamburger-bar-top|middle|bottom
  • ziteox-hamburger-icon

Responsive visibility (e.g. lg:hidden) is not built in. Pass via className.

Animation

ConcernImplementation
MorphCSS transform + transition on three <rect> elements
Easingcubic-bezier per bar (matches original design)
Reduced motionusePrefersReducedMotion → 150ms vs 300ms duration
HydrationuseSkipInitialAnimation disables transitions on first render

Does not use motion. Uses web-haptics for tap feedback on supported mobile browsers (skipped when prefers-reduced-motion is set).

Expanded state transforms

BarClosedOpen (aria-expanded="true")
Toptranslate(7px, -5px)rotate(315deg)
Middlerotate(45deg)
BottomtranslateY(5px)rotate(135deg)

State ownership

HamburgerButton does not include menu panel, overlay, scroll lock, or context provider. The app provides:

  • Open state (useState, context, or URL)
  • Menu sheet component (future @ziteox/ui MobileMenu TBD)
  • Body scroll lock on open (app concern today)

Architecture notes

DecisionRationale
Controlled APIParent coordinates button + menu; no hidden global state
CSS not motionSimple transform morph; smaller bundle; no animation lib on critical path
No Tailwind in packageFramework-agnostic; scoped injected CSS
No lg:hidden defaultVisibility is layout concern per app