Skip to content

Global Header

The global header provides the FDICnet-style masthead, attached mega-menu, mobile drill-down drawer, and composed header-search family for FDIC sites and applications.

Component

Use fd-global-header when the product needs the approved FDICnet header pattern with a top-level search component, reference-aligned grouped navigation, and application-owned information architecture.

When to use

  • The page needs one durable site header contract — The brand area, utility actions, primary navigation, and search belong together and should stay consistent across routes.
  • The experience needs the approved FDICnet header structure — The desktop trigger row, attached three-column mega-menu, mobile drill-down hierarchy, and search behavior are designed to match the approved reference closely.
  • Some top-level items are direct links and others expand into grouped navigation — The component supports both without switching to action-menu semantics.
  • The information architecture already exists in application data — The component expects navigation and search configuration to be passed in as JavaScript data, not fetched by the component.

When not to use

  • Don't use it for local section navigationfd-global-header is for the site or application shell, not a page subsection.
  • Don't use it when the framework shell already owns the masthead semantics — If another shell controls primary navigation, avoid nesting a second site header.
  • Don't use it as an action menu or command palette — The component renders navigation, not command actions. fd-menu remains the action-menu primitive.
  • Don't use it when the component would need to fetch its own navigation or search data — Runtime content stays framework-agnostic and application-provided.

Examples

Reference-aligned desktop story using the exact FDICnet main-menu YAML-derived fixture. Storybook also includes dedicated shy-header, search-open, mobile drawer, and mobile drill-down states. View in Storybook →
  • Pass the navigation tree as a JavaScript property. Assign a new array or object when content changes so the component can re-render.
  • Leave the brand slot empty only when the FDICnet-specific default wordmark is appropriate; provide a real home link in the slot for other agencies, products, or site brands.
  • Use the utility slot for application-specific support links or actions. Keep the set short and high-value.
  • Enable shy only on pages that benefit from reclaiming vertical space while scrolling. Keep it off by default for short or highly task-dense screens.
  • The reference stories and tests use the exact exported fixture from packages/components/src/components/fd-global-header.reference-content.ts and packages/components/src/components/fd-global-header.reference.ts.

Integration contract

  • Treat FdGlobalHeaderNavigationItem[] as the canonical runtime contract.
  • Normalize CMS or API payloads before assigning navigation and search.
  • Keep fetching and source-specific payload handling outside the component.
  • Use @jflamb/fdic-ds-components for generic content helpers and @jflamb/fdic-ds-components/fd-global-header-drupal for Drupal-oriented structural mapping.
ts
import { createFdGlobalHeaderContent } from "@jflamb/fdic-ds-components";
import { createFdGlobalHeaderContentFromDrupal } from "@jflamb/fdic-ds-components/fd-global-header-drupal";

const content = createFdGlobalHeaderContentFromDrupal({
  items: drupalMenuItems,
  search: {
    action: "/search",
    label: "Search FDICnet",
    placeholder: "Search FDICnet",
  },
});

const header = document.querySelector("fd-global-header");

if (header) {
  const resolved = createFdGlobalHeaderContent(content);
  header.navigation = resolved.navigation;
  header.search = resolved.search ?? null;
}
  • The Drupal helper targets a minimal structural menu shape. It does not fetch Drupal data and it does not require one exact backend response format.
  • The header contract supports one top-level row, section groups, section items, and one nested child-link level. Normalize deeper CMS trees before passing them to the helper.

Generic workflow

  1. Fetch or assemble menu data in the application layer.
  2. Normalize the source payload into the design system's content contract.
  3. Assign a fresh navigation array and search object to fd-global-header.
  4. Handle routing or search submit interception at the application layer when needed.

Search integration

fd-global-header provides local header suggestions and one cancelable submit event. It does not fetch remote search results, debounce a remote provider, render grouped results, or own a search-results page.

Use this split for v1:

  • Use search.items for deterministic local suggestions such as known navigation destinations.
  • Use search.action as the explicit fallback results URL.
  • Listen for fd-global-header-search-submit when the application or CMS router needs to handle the query itself.
  • Call event.preventDefault() only when the application will take over navigation, fetching, or routing.
  • Keep loading, empty, error, analytics, and results-page focus behavior outside the header component.
ts
const header = document.querySelector("fd-global-header");

header?.addEventListener("fd-global-header-search-submit", (event) => {
  event.preventDefault();

  const { query, href, firstMatchHref, surface } = event.detail;

  analytics.track("header_search_submit", {
    query,
    surface,
    matchedDestination: firstMatchHref ?? null,
  });

  // Preserve the component-built fallback URL so query-string behavior stays
  // aligned with the public header contract.
  router.navigate(href);
});

If the event is not canceled, the component navigates to the first direct local match when one exists. Otherwise it navigates to the fallback URL built from search.action, paramName, and the submitted query.

For async or CMS-backed search, use the header event as a handoff point:

  1. Cancel the submit event.
  2. Move focus or route to the application-owned results region or results page.
  3. Show loading state in that region, not inside the header suggestion panel.
  4. Render no-results and error recovery where the full context is available.
  5. Preserve the query in the URL so refresh, copy link, and browser history behavior stay trustworthy.
Search handoff — cancel the global-header submit event, preserve the fallback URL, and let the application route to its own results experience. View in Storybook →

Shy header adoption

  • Set shy to opt into the sticky hide/reveal behavior. Leave it unset to preserve the current in-flow header behavior.
  • Prefer enabling shy only on routes that actually overflow vertically. Short pages usually read better with the simpler in-flow header.
  • When shy is enabled the header switches to position: fixed and exposes --fd-global-header-shy-height on the host element. Use the shared .fdic-page[data-page-overflow="true"] shell or reserve equivalent space in the document flow. The component does not insert a spacer or otherwise reserve layout automatically.
  • On desktop, scrolling down past the threshold hides the full header and reveals a compact sticky header with the brand, utility actions, and a menu toggle for accessing the full navigation. Scrolling back up reveals the full header. The transition between states is animated (250ms ease).
  • On mobile, scrolling down hides the header entirely (translateY(-100%)). Scrolling up reveals it. There is no compact mobile variant.
  • Treat the desktop compact sticky state as the v1 reduced-chrome behavior. Do not build a second condensed global-header variant unless adopter evidence shows the compact state cannot support the shell need.
  • Use shyThreshold when the page needs a threshold that differs from the header's own height.
  • By default, the component observes window scrolling. If a route scrolls inside one application-owned element, assign that element to the property-only scrollContainer API. Set scrollContainer = null to return to window scrolling.
  • The component owns explicit scroll tracking, hide/reveal state, transition timing, and overlay awareness.
  • The application owns whether shy mode should be enabled, any shyThreshold override, choosing any explicit scrollContainer, and surrounding page layout (including reserving space for the fixed header).
  • No auto-detection: fd-global-header does not search for scrollable ancestors, infer route shells, or coordinate nested scroll containers. Pass the exact scroll element only when the application owns that shell.
  • Placement requirement: Shy mode uses position: fixed. The header must be a direct child of <body> or an ancestor without transform, filter, or perspective — these CSS properties create a new containing block and break fixed positioning.
html
<div class="fdic-page" data-page-overflow="true">
  <fd-global-header shy></fd-global-header>
  <main class="fdic-page__main">…</main>
</div>
ts
const header = document.querySelector("fd-global-header");
const shell = document.querySelector(".fdic-page");

if (header && shell) {
  const syncShyState = () => {
    const hasPageOverflow = document.documentElement.scrollHeight > window.innerHeight + 1;

    shell.dataset.pageOverflow = String(hasPageOverflow);
    header.shy = hasPageOverflow;
    header.shyThreshold = hasPageOverflow ? 64 : undefined;
  };

  header.navigation = resolved.navigation;
  header.search = resolved.search ?? null;
  syncShyState();
  window.addEventListener("resize", syncShyState, { passive: true });
}

When a route scrolls inside an explicit shell element, assign that element as a property after both elements exist:

ts
const header = document.querySelector("fd-global-header");
const scrollShell = document.querySelector<HTMLElement>("[data-route-scroll]");

if (header) {
  header.scrollContainer = scrollShell ?? null;
}

Use the generic helpers when the source is already close to the header contract:

ts
import {
  createFdGlobalHeaderContent,
  createFdGlobalHeaderSearchConfig,
  createHeaderSearchItemsFromNavigation,
} from "@jflamb/fdic-ds-components";

const navigation = [
  {
    kind: "panel",
    id: "services",
    label: "Services",
    sections: [
      {
        label: "Services Overview",
        href: "/services",
        items: [],
      },
      {
        label: "Programs",
        href: "/services/programs",
        items: [
          {
            label: "Bank Data",
            href: "/services/programs/bank-data",
          },
        ],
      },
    ],
  },
];

const content = createFdGlobalHeaderContent({
  navigation,
  search: createFdGlobalHeaderSearchConfig({
    action: "/search",
    label: "Search",
    items: createHeaderSearchItemsFromNavigation(navigation),
  }),
});

Drupal integration guide

The Drupal helper is designed for the common case where Drupal already exposes a menu tree through preprocess output, JSON, JSON:API, GraphQL, or a custom controller. The helper expects a small structural shape rather than one specific Drupal response contract.

  • Query or build the Drupal menu tree outside the Web Component.
  • Map the Drupal payload into the helper's structural items shape.
  • Let createFdGlobalHeaderContentFromDrupal() convert that shape into navigation and default search items.
  • Override search labels or the fallback search URL in the search option when the product shell needs different copy.

Minimal Drupal-shaped input

ts
import { createFdGlobalHeaderContentFromDrupal } from "@jflamb/fdic-ds-components/fd-global-header-drupal";

const content = createFdGlobalHeaderContentFromDrupal({
  items: [
    {
      title: "News & Events",
      url: "/news-events",
      description: "Stay current with FDIC announcements and events.",
      below: [
        {
          title: "News",
          url: "/news-events/news",
          below: [
            {
              title: "FDICNews",
              url: "/news-events/news/fdicnews",
            },
            {
              title: "Global Messages",
              url: "/news-events/news/global-messages",
              below: [
                {
                  title: "Global Digest FAQ",
                  url: "/news-events/news/global-messages/global-digest-faq",
                },
              ],
            },
          ],
        },
      ],
    },
    {
      title: "Benefits",
      url: "/benefits",
      current: true,
    },
  ],
  search: {
    action: "/search",
    label: "Search FDICnet",
    placeholder: "Search FDICnet",
  },
});

What the Drupal helper does

  • Top-level items with no below array become direct header links.
  • Top-level items with children become mega-menu panels.
  • Second-level items become panel sections.
  • Third-level items become section items.
  • Fourth-level items become the nested child-link column.
  • Search items are derived automatically from the normalized navigation tree unless you provide your own search.items.
  • A top-level item marked current or containing a current descendant marks the corresponding header item as current.

What the Drupal helper does not do

  • It does not fetch Drupal menu data.
  • It does not understand every possible Drupal response field automatically.
  • It does not preserve arbitrary tree depth beyond the header's supported information architecture.
  • It does not replace application-owned routing, caching, or access-control decisions.

Normalizing a raw Drupal payload

If your Drupal response uses different field names, map it into the structural helper input before calling the adapter:

ts
import { createFdGlobalHeaderContentFromDrupal } from "@jflamb/fdic-ds-components/fd-global-header-drupal";

function normalizeDrupalMenuItem(item) {
  return {
    id: item.id,
    title: item.title,
    url: item.url?.path || item.url || undefined,
    description: item.description || item.summary || undefined,
    current: Boolean(item.in_active_trail),
    below: (item.children || item.below || []).map(normalizeDrupalMenuItem),
  };
}

const content = createFdGlobalHeaderContentFromDrupal({
  items: drupalMenu.items.map(normalizeDrupalMenuItem),
  search: {
    action: "/search",
    label: "Search FDICnet",
  },
});

Drupal implementation notes

  • Prefer producing the structural items shape in the Drupal integration layer rather than passing raw backend payloads throughout the frontend.
  • If the Drupal tree is deeper than four levels, choose intentionally which levels belong in the header and flatten or reroute the rest.
  • Use stable URLs and durable labels. Search suggestions and mobile drill-down behavior become less trustworthy when menu entries rely on placeholders or temporary campaign language.
  • If Drupal owns active-trail state, map it into current during normalization so the header reflects the current destination consistently.
  • If the site needs custom search suggestions, pass search.items explicitly instead of relying on derived items.

Properties

NameTypeDefaultDescription
navigationFdGlobalHeaderNavigationItem[][]Consumer-provided navigation tree. Set this as a JavaScript property; it is not reflected to an HTML attribute.
searchFdGlobalHeaderSearchConfig | nullnullOptional header-search configuration. When present, the component renders desktop and mobile search surfaces and derives suggestions from navigation.
shybooleanfalseOpt-in sticky hide/reveal behavior. When true, the header uses position: fixed and exposes --fd-global-header-shy-height. On desktop, scrolling down transitions to a compact header. Reveals the full header on upward scroll or immediate interaction.
shyThresholdnumber | undefinedundefinedOptional downward-scroll threshold in pixels before shy behavior engages. When omitted, fd-global-header uses its own rendered height.
scrollContainerHTMLElement | null | undefinednullProperty-only explicit scroll target for shy-header mode. When unset or null, the header observes window scrolling. Set this to one application-owned scroll container when a route scrolls inside an element; the component does not automatically detect scroll ancestors or reserve layout space.
  • fd-global-header owns desktop menu preview state, mobile drill-down state, the shared query string coordinated with fd-header-search, shy-header scroll tracking, and compact desktop state when shy is enabled.
  • The application owns navigation data, current-link flags, routing, any custom submit handling, and page-layout decisions related to sticky shy mode.
  • scrollContainer is a property-only prototype for one explicit app-owned scroll element; set it to null to return shy tracking to window.
  • Assign a new array or object when updating navigation or search so Lit can detect the change.

Slots

NameDescription
brandOptional brand or home-link content for the masthead. When omitted, the component renders the FDICnet-specific default wordmark link.
utilityOptional utility links or actions rendered in the masthead
  • The default brand is the FDICnet wordmark linked to / with aria-label="FDICnet home"; replace the brand slot for other agencies, products, or site brands.
  • Author brand and utility content as real links or controls when an application needs to replace the default brand.

Events

NameDetailDescription
fd-global-header-search-submit{ query: string, href: string, firstMatchHref?: string, surface: \"desktop\" | \"mobile\" }Cancelable event fired when the user submits header search. If not canceled, the component navigates to firstMatchHref when a direct match exists or to href as the configured fallback results URL.

CSS custom properties

NameDefaultDescription
--fd-global-header-shy-transition-duration0.3sHide-transition duration used when shy is enabled. Reveal timing is derived internally as a proportionally faster transition.
--fd-global-header-shy-heightRead-only. Set on the host when shy is enabled. Contains the header's full (non-compact) rendered height as a pixel value. Use .fdic-page[data-page-overflow="true"] or an equivalent parent wrapper to reserve space for the fixed header. Removed when shy is disabled.
--fd-global-header-wordmark-height35pxBlock size of the default FDICnet wordmark.
--fd-global-header-wordmark-width8.75remInline size of the default FDICnet wordmark.
--fd-global-header-wordmark-colorcurrentColorColor used by the default FDICnet wordmark.
  • --fd-global-header-shy-transition-duration is ignored when the user requests reduced motion because the component suppresses shy-header transitions entirely.
  • --fd-global-header-shy-height updates automatically when the header resizes (e.g., viewport changes).

Shadow parts

NameDescription
baseRoot header wrapper
mastheadTop masthead row containing the brand, utilities, and search
primary-navDesktop primary navigation region
panelDesktop attached mega-menu panel
panel-columnIndividual desktop panel columns
mobile-drawerMobile drawer surface
  • fd-global-header intentionally does not expose every internal row or cell as a styling hook. Keep theming focused on the documented surfaces.

Best practices

Do

Author real navigation destinations

Use actual URLs for top-level links, panel overviews, section links, and child links so search suggestions and mobile drill-down links stay useful.

Don't

Feed placeholder-only links into production layouts

Empty links, fake hrefs, or CMS-specific placeholders make the header harder to test and less trustworthy for users.

Do

Keep the public family small

Use one fd-global-header instance with slots and data. Publish follow-on utilities only when the design system has a clear semantic reason.

Don't

Force navigation into fd-menu

The header family needs navigation semantics. fd-menu is the action-menu primitive and is intentionally not reused for the mega-menu contract.

Content guidelines

Keep top-level labels short and durable.

Top-level labels should describe stable navigation areas, not campaign language or temporary slogans.

Use section and item descriptions for orientation, not marketing copy.

The desktop panel and mobile drill-down surfaces use descriptions to help people decide where to go next. Keep the text brief and task-oriented.

Document the fallback search destination.

If no direct destination matches the query, the component falls back to the configured search results URL. Make sure that destination exists and is understandable to users.

Keep remote search state out of the header.

Loading, no-results, and error copy belongs with the application-owned results page or region, where people can review the full query context and recover without losing their place.

Accessibility

  • Semantics stay native — Top-level direct destinations render as links. Grouped destinations render as disclosure buttons. Desktop panel and mobile drill-down content render as navigation structures, not action menus.
  • Keyboard model follows the approved header interaction pattern without giving up semantics — Top-level desktop items support Left, Right, Home, End, and ArrowDown convenience. Mega-menu columns support directional movement between rows and columns. Mobile drill-down and search surfaces stay link/button based.
  • Focus restoration is component-owned only for ephemeral surfaces — Closing the desktop panel, mobile drawer, or mobile search surface returns focus to the invoking control when it still exists. Broader page-level focus after navigation remains application-owned.
  • Shy mode never hides the header from focused users — When shy is enabled, focus moving into the header reveals it immediately and keeps it visible while the desktop panel, mobile drawer, or mobile search surface is open. In the compact desktop state, the full navigation remains accessible via the compact menu toggle button.
  • Shy mode keeps landmarks stable — The translated mobile shy state does not add aria-hidden or inert to the header. Toggling landmark availability on scroll would make focus behavior less predictable; use concrete assistive-technology evidence before changing that contract.
  • Compact-mode transitions respect reduced motion — The animated transition between full and compact header states (padding, scale, opacity) is suppressed when users request reduced motion.
  • Mobile overlays behave like true modal surfaces only while open — The menu drawer and mobile search shell trap focus while open, restore focus to their invoking control on close, and do not keep dialog semantics attached when hidden.
  • Closed content stays out of the tab order — The component hides closed desktop and mobile surfaces so keyboard users do not tab into unavailable content.
  • Reduced motion is honored across the full component — Non-essential transitions and animations are suppressed when users request reduced motion, including overlay, mega-menu, and shy-header state changes.
  • Search uses a compact, labeled pattern — The header search field shows the Search FDICnet placeholder as its visible cue while keeping a hidden programmatic label, and ends with an always-visible magnifying-glass submit button (Submit search). There is no leading icon and no / shortcut hint competing with the submit affordance.
  • Search is local and deterministic in v1 — Suggestions are derived only from the supplied navigation tree and search configuration. The fallback submission path is explicit and testable.
  • Async results are application-owned — If a site cancels the submit event to fetch or route, the application must manage loading announcements, no-results copy, error recovery, analytics, URL state, and focus on the results page or region.
  • Multiple instances are supported — The component does not rely on global IDs or singleton state. If multiple headers render in Storybook or docs, their controls do not collide.

Known limitations

  • The component expects navigation and search data to be supplied by the application. It does not fetch CMS data or search results.
  • Header search does not provide an async provider contract, debounce lifecycle, remote grouped suggestions, or an in-header loading/error state in v1.
  • Utility-slot content does not get a dedicated alternate mobile placement contract in v1.
  • Shy mode uses position: fixed, which positions the header relative to the viewport. It requires the header to be a direct child of <body> or an ancestor without transform, filter, perspective, or will-change set — any of these on an ancestor creates a new containing block and breaks fixed positioning. Do not enable shy when the header lives inside a constrained shell, embedded app frame, or transformed container.
  • Shy mode supports either window scrolling or one explicit scrollContainer property supplied by the application. Automatic scroll-container detection, nested-scroll choreography, and automatic layout reservation are intentionally out of scope for v1.
  • Shy mode does not change assistive-technology exposure based on scroll position. If product testing shows that translated-but-exposed header content is confusing in a specific shell, document the evidence before adding route-specific semantics.
  • fd-global-header does not coordinate stacked sticky regions such as local navigation bars, alert rails, or app-owned toolbars. Keep that choreography in the page shell until a specific adopter case proves a shared contract is needed.