{
  "name": "Header",
  "source": "src/components/Header.tsx",
  "propsType": "HeaderProps",
  "description": "Header renders Figma's four Device variants — Desktop, Tablet,\nMobile-Closed, Mobile-Open — from one DOM tree.\n\nTWO AXES, AND THEY ARE NOT THE SAME KIND OF THING\n\nFigma spells Device as a single four-way variant, but it is really two: a\n*breakpoint* (Desktop / Tablet / Mobile) and a *state* (Closed / Open). They\nare modelled differently here because they are known by different people.\n\n  Breakpoint is a media query. It is the one variant axis in the system the\n  browser already knows the answer to — a header is Mobile because the\n  viewport is narrow — and having React duplicate that judgement is how the\n  two drift apart. Breakpoints match the Breakpoint collection's container\n  widths: Tablet below 1216, Mobile below 896.\n\n  Open/Closed is a prop, because only the caller knows. It follows the\n  controlled/uncontrolled pair the rest of the system uses.\n\nONE TREE, TWO LAYOUTS\n\n`center` and `end` sit inline in the bar on Desktop and Tablet, and inside\nthe dropped Menu-Container on Mobile-Open. They are rendered once, in one\nwrapper, which is `display: contents` above the mobile breakpoint and an\nabsolutely positioned panel below it. Rendering them twice — or moving them\nwith JavaScript — would mean the same nav link exists twice in the\naccessibility tree, and would reset any state a caller put in a slot every\ntime the viewport crossed 896px.\n\nThe centre slot is no longer hidden on Tablet. Figma's Tablet variant ships\nit populated; the previous release collapsed it, which was correct for the\nprevious design and is not for this one.\n\nA `<header>` element with no explicit role: it is a landmark already when it\nis a direct child of body, which is where a page header sits.",
  "import": "import { Header } from 'ionbase-ui';",
  "status": "stable",
  "summary": "The page header bar. Renders Figma's Desktop, Tablet, Mobile-Closed and Mobile-Open from one DOM tree.",
  "useWhen": [
    "the app needs a top bar with a brand, navigation and actions",
    "that bar must collapse to a mobile menu below 896px"
  ],
  "useInstead": [
    {
      "when": "it is a sidebar rather than a top bar",
      "use": "NavItem",
      "why": "Header owns a horizontal bar and its own breakpoint behaviour"
    }
  ],
  "slots": {
    "brand": {
      "accepts": "LogoMark or Logo",
      "note": "rendered at the start; never shrinks"
    },
    "center": {
      "accepts": "NavItem",
      "note": "inline on Desktop and Tablet; moves into the mobile menu below 896px. Rendered once, so slot state survives crossing the breakpoint"
    },
    "end": {
      "accepts": "Button, Avatar",
      "note": "actions; same placement rules as `center`"
    },
    "children": {
      "accepts": "any",
      "note": "escape hatch — rendered in the bar after the menu wrapper and before the mobile toggle. Prefer the named slots"
    }
  },
  "a11y": {
    "role": "banner, from the <header> element when it is a direct child of body",
    "guarantees": [
      "the mobile toggle carries `aria-expanded` and `aria-controls` pointing at the menu container",
      "Escape closes the open mobile menu — and the listener sits on the header, so a header that never opens never listens",
      "the slots are rendered once, so no nav link exists twice in the accessibility tree",
      "with `menuType=\"dialog\"` the toggle carries `aria-haspopup=\"dialog\"` instead of `aria-controls` — the Drawer it opens is not in the DOM while closed — and comes first in DOM order as well as on screen"
    ],
    "requires": [
      "`menuLabel` — the mobile toggle is icon-only and has no other name"
    ],
    "notes": [
      "This is a disclosure, not a modal: no focus trap and no scroll lock, so Escape is the whole dismissal contract."
    ]
  },
  "antiPatterns": [
    {
      "dont": "rendering a second copy of the nav for mobile",
      "why": "Header already moves the one copy; two copies duplicate every link in the accessibility tree"
    },
    {
      "dont": "driving the mobile layout from JS state",
      "why": "the breakpoint is a media query — the browser already knows the answer, and duplicating that judgement is how the two drift"
    },
    {
      "dont": "passing `open` without `onOpenChange`",
      "why": "controlled with no handler means the toggle can never change it"
    },
    {
      "dont": "a second menu button in `brand` to open a navigation Drawer",
      "do": "<Header menuType=\"dialog\" open={navOpen} onOpenChange={setNavOpen} />",
      "why": "the built-in toggle still renders, so a phone shows two menu buttons that open different things"
    },
    {
      "dont": "NavItems in `center` with `menuType=\"dialog\"`",
      "why": "nothing collapses them any more — they overflow a phone, and they duplicate the Sidebar the dialog opens"
    }
  ],
  "variants": {
    "menuType": {
      "panel": {
        "use": "the default, Figma's Mobile-Open — `center` and `end` drop into a panel below the bar"
      },
      "dialog": {
        "use": "the app's navigation is a Sidebar. The toggle moves to the start of the bar and only reports `onOpenChange`; open a Drawer holding the same Sidebar with it. `center` and `end` stay in the bar at every width"
      }
    }
  },
  "stylesheet": "src/styles/header.css",
  "tokens": [
    "--border-default",
    "--border-strong",
    "--border-width-default",
    "--border-width-thick",
    "--font-family-sans",
    "--icon-default",
    "--icon-size-sm",
    "--ion-header-center-gap",
    "--ion-header-end-gap",
    "--ion-header-height",
    "--ion-header-padding-x",
    "--ion-header-padding-y",
    "--ion-shadow-raised-lifted-xs",
    "--radius-md",
    "--radius-sm",
    "--ring-focus",
    "--spacing-12",
    "--spacing-16",
    "--spacing-2",
    "--spacing-24",
    "--spacing-32",
    "--spacing-4",
    "--spacing-48",
    "--spacing-64",
    "--spacing-8",
    "--surface-default"
  ],
  "props": {
    "brand": {
      "type": "React.ReactNode",
      "required": false,
      "origin": "own",
      "description": "Figma's Logo slot. Rendered at the start, never shrinks."
    },
    "center": {
      "type": "React.ReactNode",
      "required": false,
      "origin": "own",
      "description": "Figma's `Center Slot` — nav items. Inline on Desktop and Tablet; moves\ninto the Mobile-Open menu below the mobile breakpoint."
    },
    "end": {
      "type": "React.ReactNode",
      "required": false,
      "origin": "own",
      "description": "Figma's `End Slot` — buttons and actions. Same placement rules as `center`."
    },
    "open": {
      "type": "boolean | undefined",
      "required": false,
      "origin": "own",
      "description": "Controlled Mobile-Open state. Pair with `onOpenChange`."
    },
    "defaultOpen": {
      "type": "boolean | undefined",
      "required": false,
      "origin": "own",
      "description": "Initial Mobile-Open state when uncontrolled."
    },
    "onOpenChange": {
      "type": "((open: boolean) => void) | undefined",
      "required": false,
      "origin": "own",
      "description": "Fires whenever the menu toggle changes the state, controlled or not."
    },
    "menuLabel": {
      "type": "string | undefined",
      "required": false,
      "origin": "own",
      "description": "Accessible name for the mobile menu toggle."
    },
    "menuType": {
      "type": "\"dialog\" | \"panel\" | undefined",
      "required": false,
      "origin": "own",
      "description": "What the mobile toggle opens.\n\n`panel` (default) is Figma's Mobile-Open: `center` and `end` drop below the\nbar. `dialog` is for an app whose navigation lives in a Sidebar: the toggle\nsits at the start of the bar and only reports `onOpenChange`, the caller\nopens a Drawer with it, and `center` and `end` stay in the bar at every\nwidth — so keep `center` empty and `end` short.",
      "values": [
        "dialog",
        "panel"
      ]
    },
    "children": {
      "type": "React.ReactNode",
      "required": false,
      "origin": "own"
    }
  },
  "propCounts": {
    "own": 9,
    "aria": 0,
    "dom": 276,
    "other": 0
  }
}
