# Header

## Overview

The header component converts the old BYU-style site header into a semantic contract built around the top-level page `<header>` or an internal `<header class="header">` testing hook.

Instead of the original `.byu-*` selector tree, the component styles the header through direct child structure:

- first child home link for the brand mark or logo
- an optional short, non-interactive supporting `<p>` line when a subtitle provides useful context
- an optional heading for the site or application title
- an optional breadcrumb `<nav aria-label="breadcrumb">`
- an optional `[data-header-actions]` action group
- an optional icon button with `popovertarget` and matching `nav[popover]` for the mobile menu

For exact styles, use [header.css](/Users/stuft2/Projects/design-tokens/src/css/header.css) as the source of truth.

## When to use

Use the header once per page to identify the parent organization and current site, with optional global utility actions and navigation.

## When not to use

Do not use the header for alerts, status messages, marketing hero content, or primary navigation placed in the header action group.

## Usage

```html
<header>
  <a href="/" aria-label="Brigham Young University home">Y</a>
  <h1>Design Tokens</h1>
  <div data-header-actions aria-label="Header actions">
    <span class="icon" aria-hidden="true">account_circle</span>
    <a href="/signin">Sign In</a>
  </div>
  <nav aria-label="breadcrumb">
    <ol>
      <li><a href="https://it.byu.edu/">Office of IT</a></li>
      <li><span aria-current="page">Application Engineering</span></li>
    </ol>
  </nav>
  <button class="icon" type="button" popovertarget="site-navigation" aria-label="Open navigation menu">menu</button>
  <nav id="site-navigation" aria-label="Site" popover>
    <ul>
      <li><a href="/" aria-current="page">Home</a></li>
      <li><a href="/tokens/">Design Tokens</a></li>
      <li><a href="/components/">Components</a></li>
    </ul>
  </nav>
</header>

<nav aria-label="Site">
  <ul>
    <li><a href="/" aria-current="page">Home</a></li>
    <li><a href="/tokens/">Design Tokens</a></li>
    <li><a href="/components/">Components</a></li>
  </ul>
</nav>
```

Use this component when a page needs a strong branded header that identifies the parent organization and current site.

Use the application or site name as the header title and author it as the page's `<h1>`, including on landing pages. A labeled brand mark identifies the institution but does not replace the application title or the page's primary heading. Do not use "Brigham Young University" as the title or as a fallback subtitle; the brand mark already establishes the institution. Subtitles and breadcrumbs are optional and should appear only when they give users meaningful navigation context. Omit the subtitle when there is no obvious contextual label. Keep title and subtitle text non-interactive, and put useful linked ancestors in the header breadcrumb trail.

Use `[data-header-actions]` for page-level account or utility actions that belong in the branded header but are not primary site navigation. The action area is centered vertically to the right of the title stack. Use a normal anchor when sign-in navigates to another route. Use a native `<button>` only when sign-in opens an in-page flow, modal, or menu:

```html
<div data-header-actions aria-label="Header actions">
  <span class="icon" aria-hidden="true">account_circle</span>
  <a href="/signin">Sign In</a>
</div>
```

Use the existing [nav](/Users/stuft2/Projects/design-system/src/css/nav.md) component for the desktop primary menu below the header. When mobile navigation is needed, duplicate those primary links inside the header as a `popover` navigation menu and connect it to an icon button with `popovertarget`.

## Behavior

Covered by [Playwright behavior tests](../../tests/e2e/data-branding-tokens.spec.js).

- HDR-001: The component styles the first top-level `<header>` in `body` and any internal `<header class="header">` instance.
- HDR-002: The first child link is treated as the visual mark area and can contain text, an image, or an SVG.
- HDR-003: The top-level page header uses the available full-bleed width so long titles can wrap with balanced lines instead of forcing horizontal overflow.
- HDR-004: The optional supporting paragraph is a non-interactive caption-style contextual label, not a second heading, hierarchical link, or fallback repetition of the BYU brand.
- HDR-005: The main title should be authored as a real heading so the page keeps a meaningful outline, and it remains left aligned with authored casing within the branded header.
- HDR-006: Breadcrumbs remain optional and inherit the existing classless breadcrumb navigation presentation.
- HDR-007: Header breadcrumb parent links and separators use brand inverse text color, while hover and current items use default inverse text color.
- HDR-008: Banner breadcrumbs scroll horizontally when the trail is wider than the available space instead of wrapping onto multiple lines.
- HDR-009: Header actions are optional, stay visually grouped, and align to the inline end of the title area.
- HDR-010: Header action links keep link semantics and presentation while staying legible on the branded header background.
- HDR-011: Header action buttons use an inverse outline treatment by default so real button actions remain visibly distinct from links.
- HDR-012: When both header actions and the mobile menu button are present, the action group sits before the menu button.
- HDR-013: The optional menu button is visible below `48rem` and opens the matching primary navigation popover through the native Popover API.
- HDR-014: A standalone `body > nav:not([popover])` remains the desktop primary navigation and is hidden below `48rem` when paired with the mobile header menu.
- HDR-015: The branded header has no outer margin, while keeping its own internal padding.
- HDR-016: When the header is a direct child of `body`, it fills the body's flush inline width to read like page chrome instead of centered content.
- HDR-017: A one-line header title keeps the header compact, while a title that wraps onto multiple lines expands the header to fit its content.
- HDR-018: Landing pages retain the application title as the branded header's `<h1>` even when a redundant title is omitted from the main content.
- HDR-019: The header and its mobile navigation popover use their existing boundaries without elevation shadows.

## Accessibility

- Use a native top-level `<header>` for real page-level header content and do not add a redundant `role="banner"`.
- Use the internal `.header` hook only for demo or test pages that need header styling outside the document-level page header.
- Keep the first child link pointed at a stable home or parent destination.
- Use real `<h1>` text for the current site or application name in the branded header instead of relying on a logo image alone.
- Use the application or site name for the title, never "Brigham Young University" as a title or fallback subtitle.
- Omit subtitles and breadcrumbs unless they provide meaningful user context; keep subtitle text non-interactive and use breadcrumbs for linked hierarchical locations.
- Keep breadcrumb links as real anchors and mark the current location with `aria-current`.
- Give the header action group an accessible label when the controls are not self-evident from surrounding context.
- Do not use the header action group for primary site navigation. Use a labeled `nav` when the content is a set of destinations.
- Keep the menu button label explicit with `aria-label`, and make sure its `popovertarget` value matches the primary popover navigation `id`.
- Use real links inside the primary navigation popover and mirror `aria-current` with the desktop primary navigation.
- Do not use this component for alerts, status messages, or marketing hero blocks.

## Migration Notes

Compared with the BYU header web component, this header currently supports the branded shell, optional subtitle line, optional breadcrumb trail, a mobile primary navigation popover, and composition with primary navigation below the header.

The main documented gaps are:

- Constrained header layouts are intentionally out of scope for now.
- Search and complete authenticated account states are not covered by this header contract.
- Nested links within the site title are not documented as a supported authored pattern.
- Search and richer account states remain separate work beyond the dedicated header action area.

When migrating from the BYU header component, use this header for the branded page shell, pair it with the existing navigation component below it on desktop, and add the native popover menu for mobile primary navigation. Treat search and richer account controls as separate work rather than assuming parity today.

## Variables

| Variable | Type | Description |
| --- | --- | --- |
| `--header-background` | `<color>` | Sets the header surface color and active menu contrast target; change it when the branded header needs a different primary surface. |
| `--header-caption-size` | `<length>` | Sets the uppercase caption or subtitle size in the header; change it when the secondary page label needs more or less hierarchy. |
| `--header-color-accent` | `<color>` | Sets --header-color-accent so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-color-border-subtle` | `<color>` | Sets --header-color-border-subtle so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-color-focus-ring` | `<color>` | Sets --header-color-focus-ring so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-color-text` | `<color>` | Sets --header-color-text so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-color-text-accent-inverse` | `<color>` | Sets --header-color-text-accent-inverse so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-control-border-width` | `<length>` | Sets --header-control-border-width so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-control-gap` | `<length>` | Sets --header-control-gap so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-control-min-height` | `<length>` | Sets --header-control-min-height so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-control-padding-block` | `<length>` | Sets --header-control-padding-block so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-control-padding-inline` | `<length>` | Sets --header-control-padding-inline so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-control-radius` | `<length>` | Sets --header-control-radius so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-focus-ring-size` | `<length>` | Sets --header-focus-ring-size so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-font-family-sans` | `Any` | Sets --header-font-family-sans so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-font-size` | `<length>` | Sets --header-font-size so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-font-weight-body-bold` | `<number>` | Sets --header-font-weight-body-bold so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-font-weight-heading-2` | `<number>` | Sets --header-font-weight-heading-2 so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-landmark-padding-block` | `<length>` | Sets --header-landmark-padding-block so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-line-height` | `<number>` | Sets --header-line-height so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-mark-size` | `<length>` | Sets the brand mark column width and mobile menu vertical offset; change it when the logo area should be larger or smaller. |
| `--header-menu-popover-width` | `<length>` | Sets the width of the header menu popover; change it when mobile or compact navigation needs a wider or narrower menu. |
| `--header-rule-color` | `<color>` | Sets the header divider color, mark divider, and active menu button border; change it when separators need different contrast. |
| `--header-rule-width` | `<length>` | Sets the thickness of header dividers; change it when the bottom rule and mark divider should read heavier or lighter. |
| `--header-safe-area-top` | `<length>` | Sets --header-safe-area-top so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-space-inline` | `<length>` | Sets --header-space-inline so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--header-text-color` | `<color>` | Sets header text and action button foreground colors; change it when the header surface requires a different accessible foreground. |
| `--header-title-size` | `<length>` | Sets the main header title size; change it when page titles should be more compact or more prominent in the header. |
| `--header-title-weight` | `<number>` | Sets the main header title font weight; change it to make page titles feel lighter, stronger, or closer to body heading weight. |

## Depends On

- `--color-accent`
- `--color-border-inverse`
- `--color-border-subtle`
- `--color-focus-ring`
- `--color-primary-strong`
- `--color-text`
- `--color-text-accent-inverse`
- `--color-text-inverse`
- `--font-family-sans`
- `--font-line-height-body`
- `--font-size`
- `--font-size-caption`
- `--font-size-title`
- `--font-weight-body-bold`
- `--font-weight-heading-2`
- `--layout-control-border-width`
- `--layout-control-gap`
- `--layout-control-icon-size`
- `--layout-control-min-height`
- `--layout-control-padding-block`
- `--layout-control-padding-inline`
- `--layout-control-radius`
- `--layout-focus-ring-size`
- `--layout-safe-area-top`
- `--layout-space-inline`
- `--layout-space-stack`
- `nav[aria-label="breadcrumb"]`
- `body > nav:not([popover])`
- `nav[popover]`
