Header
<cw-header> | CwHeader
Page header region composing branding, primary navigation, icon-triggered action panels, and secondary navigation into one piece of chrome.
<cw-header> composes everything a site’s persistent page chrome typically needs, via
named slots: logo, primary-navigation, five icon-triggered action slots
(utility, search, notification, user,
cart), and secondary-navigation. Any slot you don’t populate renders nothing — no
reserved empty space.
Below, mobile-breakpoint is set low enough that this preview renders in its desktop layout —
horizontal primary navigation alongside the logo — even inside the doc site’s fairly narrow preview frame.
Shrink the frame (drag its bottom-right corner) below the breakpoint to see it collapse to the mobile,
hamburger layout instead. See Mobile Breakpoint below for a preview forced
into that layout instead.
<cw-header mobile-breakpoint="320"> <cw-logo slot="logo" image="/assets/images/Cordwainer-logo-light-mode-vector.svg" dark-image="/assets/images/Cordwainer-logo-dark-mode-vector.svg" href="/" label="Cordwainer home" ></cw-logo> <cw-primary-navigation slot="primary-navigation" mobile-breakpoint="320"> <cw-primary-navigation-item href="/" current>Home</cw-primary-navigation-item> <cw-primary-navigation-item href="/">Products</cw-primary-navigation-item> </cw-primary-navigation> <div slot="search" style="padding: 1rem;"> <cw-input placeholder="Search..." clearable></cw-input> </div> <div slot="cart" style="padding: 1rem;">Your cart is empty.</div> <cw-secondary-navigation slot="secondary-navigation"> <cw-secondary-navigation-item href="/" current>Overview</cw-secondary-navigation-item> <cw-secondary-navigation-item href="/">Installation</cw-secondary-navigation-item> </cw-secondary-navigation> </cw-header>
import { CwHeader, CwInput, CwLogo, CwPrimaryNavigation, CwPrimaryNavigationItem, CwSecondaryNavigation, CwSecondaryNavigationItem } from '@cordwainer/cw-elements/dist/react'; const App = () => ( <CwHeader mobileBreakpoint={320}> <CwLogo slot="logo" image="/assets/images/Cordwainer-logo-light-mode-vector.svg" darkImage="/assets/images/Cordwainer-logo-dark-mode-vector.svg" href="/" label="Cordwainer home" /> <CwPrimaryNavigation slot="primary-navigation" mobileBreakpoint={320}> <CwPrimaryNavigationItem href="/" current> Home </CwPrimaryNavigationItem> <CwPrimaryNavigationItem href="/">Products</CwPrimaryNavigationItem> </CwPrimaryNavigation> <div slot="search" style={{ padding: '1rem' }}> <CwInput placeholder="Search..." clearable /> </div> <div slot="cart" style={{ padding: '1rem' }}> Your cart is empty. </div> <CwSecondaryNavigation slot="secondary-navigation"> <CwSecondaryNavigationItem href="/" current> Overview </CwSecondaryNavigationItem> <CwSecondaryNavigationItem href="/">Installation</CwSecondaryNavigationItem> </CwSecondaryNavigation> </CwHeader> );
Examples
Action Panels
Each of the five action slots — utility (gear), search (magnifying glass),
notification (bell), user (person), cart (basket) — gets a fixed icon
trigger automatically when populated. Clicking a trigger opens a panel below the header showing that slot’s
content; only one panel is open at a time, and clicking a different trigger while one is already open swaps
the content directly. Panels close on Escape or on clicking outside, and the trigger receives focus back on
close.
<cw-header> <div slot="utility" style="padding: 1rem;">Utility settings go here.</div> <div slot="search" style="padding: 1rem;"> <cw-input placeholder="Search..." clearable></cw-input> </div> <div slot="notification" style="padding: 1rem;">You're all caught up.</div> <div slot="user" style="padding: 1rem;">Signed in as jane@example.com</div> <div slot="cart" style="padding: 1rem;">Your cart is empty.</div> </cw-header>
import { CwHeader, CwInput } from '@cordwainer/cw-elements/dist/react'; const App = () => ( <CwHeader> <div slot="utility" style={{ padding: '1rem' }}> Utility settings go here. </div> <div slot="search" style={{ padding: '1rem' }}> <CwInput placeholder="Search..." clearable /> </div> <div slot="notification" style={{ padding: '1rem' }}> You're all caught up. </div> <div slot="user" style={{ padding: '1rem' }}> Signed in as jane@example.com </div> <div slot="cart" style={{ padding: '1rem' }}> Your cart is empty. </div> </CwHeader> );
Panel Width
Above mobile-breakpoint, an open panel is flush right at 50% of the header’s width by default.
Override it with the --panel-width custom property. Below the breakpoint, panels are always
full width.
<cw-header style="--panel-width: 320px;"> <div slot="search" style="padding: 1rem;"> <cw-input placeholder="Search..."></cw-input> </div> </cw-header>
import { CwHeader, CwInput } from '@cordwainer/cw-elements/dist/react'; const App = () => ( <CwHeader style={{ '--panel-width': '320px' }}> <div slot="search" style={{ padding: '1rem' }}> <CwInput placeholder="Search..." /> </div> </CwHeader> );
Mobile Breakpoint
Set mobile-breakpoint (in pixels) to control when the header switches to its stacked mobile
layout — logo and primary navigation on the first row (primary navigation’s own hamburger takes over),
action triggers on the second, secondary navigation on the third. Defaults to 768, matching
<cw-primary-navigation>’s own default so the two switch together out of the box.
<cw-header mobile-breakpoint="900" class="header-demo-narrow"> <cw-logo slot="logo" image="/assets/images/Cordwainer-logo-light-mode-vector.svg" dark-image="/assets/images/Cordwainer-logo-dark-mode-vector.svg" href="/" label="Cordwainer home" ></cw-logo> <cw-primary-navigation slot="primary-navigation"> <cw-primary-navigation-item href="/">Home</cw-primary-navigation-item> <cw-primary-navigation-item href="/">About</cw-primary-navigation-item> </cw-primary-navigation> <div slot="search" style="padding: 1rem;"> <cw-input placeholder="Search..."></cw-input> </div> </cw-header> <style> .header-demo-narrow { max-width: 400px; } </style>
import { CwHeader, CwInput, CwLogo, CwPrimaryNavigation, CwPrimaryNavigationItem } from '@cordwainer/cw-elements/dist/react'; const css = ` .header-demo-narrow { max-width: 400px; } `; const App = () => ( <> <CwHeader className="header-demo-narrow" mobileBreakpoint={900}> <CwLogo slot="logo" image="/assets/images/Cordwainer-logo-light-mode-vector.svg" darkImage="/assets/images/Cordwainer-logo-dark-mode-vector.svg" href="/" label="Cordwainer home" /> <CwPrimaryNavigation slot="primary-navigation"> <CwPrimaryNavigationItem href="/">Home</CwPrimaryNavigationItem> <CwPrimaryNavigationItem href="/">About</CwPrimaryNavigationItem> </CwPrimaryNavigation> <div slot="search" style={{ padding: '1rem' }}> <CwInput placeholder="Search..." /> </div> </CwHeader> <style>{css}</style> </> );
Submenus on Mobile
Slot a <cw-primary-navigation-menu> into a
<cw-primary-navigation-item>’s menu slot to give it a dropdown of sub-links
— see Primary Navigation Item for details. Above
mobile-breakpoint, that dropdown floats below its trigger like any other. Below it, forced here
with a narrow wrapper, it instead expands inline and full width, right where the trigger sits, matching the
rest of the mobile menu: tap “Products” or “Company” below to open the hamburger menu, then tap either one
to expand its sub-links in place.
<cw-header mobile-breakpoint="900" class="header-demo-narrow"> <cw-logo slot="logo" image="/assets/images/Cordwainer-logo-light-mode-vector.svg" dark-image="/assets/images/Cordwainer-logo-dark-mode-vector.svg" href="/" label="Cordwainer home" ></cw-logo> <cw-primary-navigation slot="primary-navigation"> <cw-primary-navigation-item href="/" current>Home</cw-primary-navigation-item> <cw-primary-navigation-item> Products <cw-primary-navigation-menu slot="menu"> <a href="/">Shoes</a> <a href="/">Boots</a> <a href="/">Sandals</a> </cw-primary-navigation-menu> </cw-primary-navigation-item> <cw-primary-navigation-item> Company <cw-primary-navigation-menu slot="menu"> <a href="/">About</a> <a href="/">Careers</a> </cw-primary-navigation-menu> </cw-primary-navigation-item> </cw-primary-navigation> </cw-header> <style> .header-demo-narrow { max-width: 400px; } </style>
import { CwHeader, CwLogo, CwPrimaryNavigation, CwPrimaryNavigationItem, CwPrimaryNavigationMenu } from '@cordwainer/cw-elements/dist/react'; const css = ` .header-demo-narrow { max-width: 400px; } `; const App = () => ( <> <CwHeader className="header-demo-narrow" mobileBreakpoint={900}> <CwLogo slot="logo" image="/assets/images/Cordwainer-logo-light-mode-vector.svg" darkImage="/assets/images/Cordwainer-logo-dark-mode-vector.svg" href="/" label="Cordwainer home" /> <CwPrimaryNavigation slot="primary-navigation"> <CwPrimaryNavigationItem href="/" current> Home </CwPrimaryNavigationItem> <CwPrimaryNavigationItem> Products <CwPrimaryNavigationMenu slot="menu"> <a href="/">Shoes</a> <a href="/">Boots</a> <a href="/">Sandals</a> </CwPrimaryNavigationMenu> </CwPrimaryNavigationItem> <CwPrimaryNavigationItem> Company <CwPrimaryNavigationMenu slot="menu"> <a href="/">About</a> <a href="/">Careers</a> </CwPrimaryNavigationMenu> </CwPrimaryNavigationItem> </CwPrimaryNavigation> </CwHeader> <style>{css}</style> </> );
Roadmap
Badges on action triggers (e.g. a notification count on the bell, an item count on the
cart) are not yet supported — deferred out of this component’s initial scope. When picked up, the intended
approach is the same corner-overlay technique <cw-button> already uses for slotted
<cw-badge> content
(src/components/button/button.styles.ts):
.button ::slotted(cw-badge) { position: absolute; top: 0; right: 0; translate: 50% -50%; pointer-events: none; }
The equivalent for <cw-header> would let a <cw-badge> be slotted
alongside (or into) an action slot’s content and absolutely positioned against that action’s trigger.
Importing
If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.
To import this component from the CDN using a script tag:
<script type="module" src="https://cdn.jsdelivr.net/npm/@cordwainer/cw-elements@1.5.2/cdn/components/header/header.js"></script>
To import this component from the CDN using a JavaScript import:
import 'https://cdn.jsdelivr.net/npm/@cordwainer/cw-elements@1.5.2/cdn/components/header/header.js';
To import this component using a bundler:
import '@cordwainer/cw-elements/dist/components/header/header.js';
To import this component as a React component:
import CwHeader from '@cordwainer/cw-elements/dist/react/header';
Slots
| Name | Description |
|---|---|
logo
|
A <cw-logo>. |
primary-navigation
|
A <cw-primary-navigation>. |
utility
|
Content for the utility action panel, opened via a gear icon trigger. |
search
|
Content for the search action panel, opened via a search icon trigger. |
notification
|
Content for the notification action panel, opened via a bell icon trigger. |
user
|
Content for the user/account action panel, opened via a person icon trigger. |
cart
|
Content for the cart action panel, opened via a cart icon trigger. |
secondary-navigation
|
A <cw-secondary-navigation>, rendered on its own row below everything else.
|
Learn more about using slots.
Properties
| Name | Description | Reflects | Type | Default |
|---|---|---|---|---|
activePanel
|
The name of the currently open action panel, or null if none is open. Not reflected as
an attribute — null is a legitimate, frequently-reached value here (no panel open),
which doesn’t play well with CwElement’s reflected-attribute “morph fix”
(src/internal/cw-element.ts), designed around properties whose first non-null value is
a permanent default rather than a value that legitimately toggles back to null at
runtime.
|
HeaderAction['name'] | null
|
null
|
|
mobileBreakpoint
mobile-breakpoint
|
The viewport width, in pixels, below which the header switches to its stacked mobile layout. |
number
|
768
|
|
updateComplete |
A read-only promise that resolves when the component has finished updating. |
Learn more about attributes and properties.
Events
| Name | React Event | Description | Event Detail |
|---|---|---|---|
cw-panel-show |
onCwPanelShow |
Emitted when an action panel opens. |
{ name: String }
|
cw-panel-after-show |
onCwPanelAfterShow |
Emitted after an action panel opens and all animations are complete. |
{ name: String }
|
cw-panel-hide |
onCwPanelHide |
Emitted when an action panel closes. |
{ name: String }
|
cw-panel-after-hide |
onCwPanelAfterHide |
Emitted after an action panel closes and all animations are complete. |
{ name: String }
|
Learn more about events.
Methods
| Name | Description | Arguments |
|---|---|---|
showPanel() |
Opens the named action panel. No-op if that slot has no content. |
name: HeaderAction['name']
|
hidePanel() |
Closes the currently open action panel, if any. | - |
Learn more about methods.
Custom Properties
| Name | Description | Default |
|---|---|---|
--panel-width |
The width of an open action panel above mobile-breakpoint. Below it, panels are always
full width. Defaults to 50%.
|
Learn more about customizing CSS custom properties.
Parts
| Name | Description |
|---|---|
base |
The component’s base wrapper, a <header> landmark element. |
logo |
The container that wraps the slotted logo. |
primary-navigation |
The container that wraps the slotted primary navigation. |
actions |
The container that wraps the action triggers. |
trigger |
Each action trigger, a <cw-icon-button>. |
trigger--utility |
The utility action trigger. |
trigger--search |
The search action trigger. |
trigger--notification |
The notification action trigger. |
trigger--user |
The user action trigger. |
trigger--cart |
The cart action trigger. |
trigger__base |
A trigger’s exported base part. |
indicator |
The line that highlights the currently open action trigger. |
panel |
The panel shown below the header when an action trigger is open. |
secondary-navigation |
The container that wraps the slotted secondary navigation. |
Learn more about customizing CSS parts.
Animations
| Name | Description |
|---|---|
header.show |
The animation to use when an action panel opens. |
header.hide |
The animation to use when an action panel closes. |
Learn more about customizing animations.
Dependencies
This component automatically imports the following dependencies.
<cw-icon><cw-icon-button>