The top of one page or one object: the breadcrumbs, the page’s one main heading, a short introduction, its status, and the few actions for the whole page.
Projects
Every project the studio runs, with who leads it and what it waits for.
Options
| Choice | Values | Default | What it does |
|---|---|---|---|
compositionsmbk-page-header--composition-<value> | simpleobjecttabbed | simple | Simple is a title, an introduction and actions. Object adds a status beside the title and a line of facts, for the page of one thing. Tabbed ends with tabs that switch the page’s views. The wrapper sets it from what it is given. |
loadingsmbk-page-header--loading | On or off | Off | The page is still loading: the words are skeleton lines of their own height, the actions are drawn disabled, and the tabs are skeletons that cannot be pressed. |
Examples
Usage
The breadcrumbs, the page’s title, a line on what the page is for, and its actions: the main one first, the rest behind More actions. On its own page the title is the one main heading, level 1; here, under this page’s own, it is level 2.
Projects
Every project the studio runs, with who leads it and what it waits for.
About one thing
A page about one thing adds its status beside the title and a quiet line of facts. A destructive action stays in the menu.
Quarterly report
PublishedRevenue, costs and the work behind them, from July to September.
More than three actions
Three actions show as buttons; a fourth and any after it go behind More actions, after the ones always kept there. Keep to three, and put the rest in the menu yourself.
Projects
Every project the studio runs, with who leads it and what it waits for.
Main action elsewhere
When the page’s main action sits elsewhere, such as at the end of the app’s toolbar, every action is an outline button and none is brand. In a narrow space they all go behind More actions.
Overview
What waits on you, and what changed since this morning.
An action with a note
An action can be drawn by the system’s own button, here a button note saying why it cannot be pressed yet; the header still places it, and folds it into More actions where it would fold a button.
Release 0.4
In reviewTwelve changes since the last release, with their notes.
With tabs
Tabs that switch the page’s views come last; their view follows straight under the header.
Projects
Every project the studio runs, with who leads it and what it waits for.
Guidelines
Purpose
The top of one page or one thing: where the reader is, the page's one main heading, a short introduction, the status of the thing the page is about, and the few actions that act on the whole page. It is not the site's header or the app bar, which stay the same on every page; the page header changes with each page.
When to use
- At the top of every page of an application or a site's inner pages: a list, a record, a settings page, a report.
- At the top of the page of one thing, with its status and the facts about it.
When not to use
- For the site's own header or the app bar, which hold the actions for the whole site.
- For the heading of a section inside the page. Use the section heading.
- For a marketing page's opening, with a large picture and a call to action. Build that as the page's own hero.
Usage rules
Do
- Keep one main action, the one most people come to the page for, or none when it already sits in the app's toolbar.
- Keep the introduction to a sentence or two.
- Put a destructive action behind "More actions".
Don't
- Put the actions for the whole site in it.
- Make a destructive action the main one.
- Use more than one page header on a page.
Website redesign
Keep a destructive action behind More actions, where it is not pressed by habit.
Website redesign
A destructive action as the main button invites the press that cannot be undone.
Anatomy
- The block, a column that draws no surface.
- The breadcrumbs part, optional: the small breadcrumbs, drawn from a list of links (each a label and an address, the page itself last with none), or the page's own link to the page above.
- The main part, a row: the body at its start and the actions at its end.
- The body part, holding the title row, the description and the meta line.
- The title-row part, holding the title and the status side by side.
- The title part, the page's one main heading, in the largest heading style, a step smaller in a narrow layout.
- The status part, optional: a badge beside the title, for the state of the thing the page is about.
- The description part, optional: a short introduction in a large body style and the subtle text color.
- The meta part, optional: a quiet line of facts in a small body style, such as when it changed and who owns it.
- The actions part, optional: the action parts, the main one a brand button and the others outline buttons, then the more part, a "More actions" button opening a menu. When the page's main action sits elsewhere, every action part is an outline button. An action part can be the system's own button or button note, which the header gives its action classes.
- The tabs part, optional: tabs that switch the page's views, last; the tabs-cue part shows at their end while more tabs lie past it.
- While loading, the tabs-skeleton part takes the tabs' place, and the hidden part holds what a screen reader hears as the heading.
The composition follows from what the header is given: simple, object with a status or a meta line, tabbed with tabs.
Behavior
In a wide layout the actions sit at the row's end, centred on the title's first line. In a narrow layout they move under the words: the main action fills the row, and the other actions move behind "More actions" with the actions that are always there. Up to three actions show as buttons; give the rest as moreActions. An action given past the third is never dropped: it goes behind "More actions", after the ones always kept there.
When the page's main action sits somewhere else, such as at the end of the app's toolbar, set mainAction={false}: every action is then an outline button, and in a narrow layout all of them move behind "More actions". In markup written by hand, mark each action smbk-page-header__action--secondary.
An action can be drawn by the system's own button instead of the header's, given as its button: a Button that shows a state, such as one disabled while its job runs, or a button note that says what pressing it does or why it cannot be pressed now (with the note under the button, lined up with the header's end). Give it the header's look, the medium size, brand for the main action and outline for the rest. The header still places it and folds it behind "More actions" in a narrow layout, where its menu item is drawn from the action's label, icon and press, without the note. With a note, the buttons in the row line up by their tops, still centered on the title's first line, and the note hangs under its button. While the header is loading, it draws its own disabled button in that place.
Tabs come last and are as wide as they need, up to the row; when there are more than fit, the row scrolls sideways inside itself and a cue at its end shows there are more. Give the tabs the id of the one panel under the header that shows each view, so the view follows straight under them. The header ends with the space a heading block leaves before what follows, except when it ends with tabs.
Loading, the words are skeleton lines in their own type, so nothing moves when the page arrives; the actions are disabled, and the tabs are skeletons that cannot be pressed.
Accessibility
What it does for you
- The title is the page's one main heading, level 1 by default. There is no second main heading and no second banner landmark: the header is a plain block, not a banner.
- Reading and Tab order: the breadcrumbs, the title, the description, the actions, then the tabs. It has no keys of its own.
- "More actions" is a button named "More actions" that opens a menu; the menu's keys are the dropdown's.
- Loading, the page says it is busy, and the heading reads "Loading" (
loadingLabel) until the title arrives. No tab can be pressed. - A status badge says its state in words; its color only backs them up.
What you must do
- Set
headingLevelonly when the header sits inside something that has its own main heading. - Give a header that sticks to the top of the window a top scroll padding on the page as tall as the sticking header, so it never covers the control that has focus.
How to test
- Tab from the top of the page: focus goes through the breadcrumbs, the actions, then the tabs, in that order, and the focus ring shows on each.
- Check that the page has one level 1 heading, and that the header adds no banner landmark.
- With a screen reader, the title is read as the page's heading, and "More actions" as a button that opens a menu. While loading, the heading reads "Loading" and the page is busy.
- With text at 200% and the page 320 pixels wide, the actions move under the words and the tabs row scrolls sideways inside itself.
- In a forced-color mode, the focus rings on the actions and tabs stay visible.
Source
The files the page header is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.
Stylesheet
/* smbk-page-header — Page header. Figma "Page header" 7115:869 (Composition × Layout × State). The top of one page:
* small breadcrumbs, then the title (Heading XL, Heading LG in a narrow layout) with an optional status badge beside
* it, the introduction in Body LG and a line of facts in Body SM, the actions at the row's end centred on the title's
* line, and the tabs last. A narrow layout puts the actions under the words, the main one filling the row and the rest
* behind "More actions". The block ends with the space a heading block leaves before what follows, except when it
* ends with tabs, whose views follow straight under them. It draws no surface. */
.smbk-page-header {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: var(--smbk-space-stack-lg);
padding-block-end: var(--smbk-space-layout-heading);
}
/* The block frames the words it hides for screen readers, so they stay inside it wherever it is placed: without a
frame they hang from the page itself and stretch it, past the end of an area that scrolls. No weight, so a parent
that places the block keeps its say. */
:where(.smbk-page-header) {
position: relative;
}
.smbk-page-header--composition-tabbed {
padding-block-end: 0;
}
.smbk-page-header__breadcrumbs {
min-width: 0;
}
.smbk-page-header__main {
display: flex;
align-items: flex-start;
gap: var(--smbk-space-inline-lg);
}
.smbk-page-header__body {
flex: 1 1 auto;
min-width: 0;
display: flex;
flex-direction: column;
gap: var(--smbk-space-text-caption);
}
.smbk-page-header__title-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--smbk-space-inline-md);
}
.smbk-page-header__title {
margin: 0;
min-width: 0;
color: var(--smbk-color-text-default);
font-family: var(--smbk-typography-heading-xl-font-family);
font-size: var(--smbk-typography-heading-xl-font-size);
font-weight: var(--smbk-typography-heading-xl-font-weight);
line-height: var(--smbk-typography-heading-xl-line-height);
letter-spacing: var(--smbk-typography-heading-xl-letter-spacing);
text-transform: var(--smbk-typography-heading-xl-text-transform);
}
.smbk-page-header__status {
flex: none;
}
.smbk-page-header__description {
margin: 0;
max-width: var(--smbk-size-content-lg);
color: var(--smbk-color-text-subtle);
font-family: var(--smbk-typography-body-lg-font-family);
font-size: var(--smbk-typography-body-lg-font-size);
font-weight: var(--smbk-typography-body-lg-font-weight);
line-height: var(--smbk-typography-body-lg-line-height);
letter-spacing: var(--smbk-typography-body-lg-letter-spacing);
}
.smbk-page-header__meta {
margin: 0;
color: var(--smbk-color-text-subtle);
font-family: var(--smbk-typography-body-sm-font-family);
font-size: var(--smbk-typography-body-sm-font-size);
font-weight: var(--smbk-typography-body-sm-font-weight);
line-height: var(--smbk-typography-body-sm-line-height);
letter-spacing: var(--smbk-typography-body-sm-letter-spacing);
}
/* The actions sit in a box one title line tall, in the title's type, so they are centred on the title's first line. */
.smbk-page-header__actions {
flex: none;
display: flex;
align-items: center;
gap: var(--smbk-space-inline-sm);
min-height: 1lh;
font-family: var(--smbk-typography-heading-xl-font-family);
font-size: var(--smbk-typography-heading-xl-font-size);
line-height: var(--smbk-typography-heading-xl-line-height);
}
/* An action drawn by a button note: its note hangs under the buttons. The row then lines its items up by their tops,
each button a medium control, a step in from the title's line so it stays centred on it, as without a note. */
.smbk-page-header__actions:has(> .smbk-button-note) {
align-items: flex-start;
padding-block-start: calc((1lh - var(--smbk-size-control-md)) / 2);
}
/* Each "More actions" opener shows in one layout only: the narrow one also holds the actions the narrow row hides. */
.smbk-page-header__more {
display: flex;
}
.smbk-page-header__more--narrow {
display: none;
}
/* ---- tabs: last, as wide as they need up to the row; a cue at the end while more tabs lie past it ---- */
.smbk-page-header__tabs {
position: relative;
display: flex;
max-width: 100%;
min-width: 0;
}
.smbk-page-header__tabs > .smbk-tabs {
min-width: 0;
max-width: 100%;
}
.smbk-page-header__tabs-cue {
display: none;
position: absolute;
inset-block: 0;
inset-inline-end: 0;
align-items: center;
justify-content: center;
width: var(--smbk-size-control-xs);
background: var(--smbk-color-surface-default);
color: var(--smbk-color-icon-subtle);
pointer-events: none;
}
.smbk-page-header__tabs-cue .smbk-icon {
font-size: var(--smbk-size-icon-sm);
}
.smbk-page-header__tabs[data-more] .smbk-page-header__tabs-cue {
display: flex;
}
.smbk-page-header__tabs-skeleton {
display: flex;
align-items: center;
gap: var(--smbk-space-inline-2xl);
height: var(--smbk-size-control-md);
}
/* ---- loading: skeleton lines in the type of the words they stand for, so nothing moves when the words come ---- */
.smbk-page-header .smbk-page-header__title > .smbk-skeleton,
.smbk-page-header .smbk-page-header__description > .smbk-skeleton,
.smbk-page-header .smbk-page-header__meta > .smbk-skeleton {
font: inherit;
line-height: inherit;
}
.smbk-page-header .smbk-page-header__title > .smbk-skeleton {
width: var(--smbk-size-width-320);
max-width: 100%;
}
.smbk-page-header .smbk-page-header__description > .smbk-skeleton {
width: var(--smbk-size-width-480);
max-width: 100%;
}
.smbk-page-header .smbk-page-header__meta > .smbk-skeleton {
width: var(--smbk-size-width-240);
max-width: 100%;
}
.smbk-page-header .smbk-page-header__description > .smbk-skeleton > .smbk-skeleton__line:nth-child(n + 2) {
display: none;
}
.smbk-page-header__tabs-skeleton > .smbk-skeleton {
width: var(--smbk-size-72);
}
.smbk-page-header__tabs-skeleton > .smbk-skeleton:nth-child(2n) {
width: var(--smbk-size-88);
}
/* .smbk-page-header__hidden: hidden from view, kept for screen readers and the keyboard, by the shared rule in visually-hidden/visually-hidden.css. */
/* ---- narrow: the title a step smaller, the actions under the words, the main one filling the row ---- */
@container style(--smbk-layout: narrow) {
.smbk-page-header__main {
flex-direction: column;
align-items: stretch;
gap: var(--smbk-space-stack-lg);
}
.smbk-page-header__title {
font-family: var(--smbk-typography-heading-lg-font-family);
font-size: var(--smbk-typography-heading-lg-font-size);
font-weight: var(--smbk-typography-heading-lg-font-weight);
line-height: var(--smbk-typography-heading-lg-line-height);
letter-spacing: var(--smbk-typography-heading-lg-letter-spacing);
text-transform: var(--smbk-typography-heading-lg-text-transform);
}
.smbk-page-header__actions {
min-height: 0;
}
.smbk-page-header__actions:has(> .smbk-button-note) {
padding-block-start: 0;
}
/* A main action with a note fills the row too, its note under it at the start. */
.smbk-page-header__action--primary.smbk-button-note {
align-items: stretch;
text-align: start;
}
.smbk-page-header__action--primary {
flex: 1 1 auto;
}
.smbk-page-header__action--secondary,
.smbk-page-header__more--wide {
display: none;
}
.smbk-page-header__more--narrow {
display: flex;
}
.smbk-page-header .smbk-page-header__title > .smbk-skeleton,
.smbk-page-header .smbk-page-header__description > .smbk-skeleton {
width: var(--smbk-size-width-240);
}
.smbk-page-header .smbk-page-header__description > .smbk-skeleton > .smbk-skeleton__line:nth-child(n + 2) {
display: block;
}
}Tokens it reads
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| space.stack.lg--smbk-space-stack-lg | comfortable: 16pxcompact: 12pxchanges with density | Spacing | |
| space.layout.heading--smbk-space-layout-heading | phone: comfortable: 40px · compact: 32pxdesktop: comfortable: 56px · compact: 48pxchanges with layout | Spacing | |
| space.inline.lg--smbk-space-inline-lg | comfortable: 12pxcompact: 8pxchanges with density | Spacing | |
| space.text.caption--smbk-space-text-caption | phone: 4pxdesktop: 8pxchanges with layout | Spacing | |
| space.inline.md--smbk-space-inline-md | comfortable: 8pxcompact: 6pxchanges with density | Spacing | |
| color.text.default--smbk-color-text-default | light: #232527dark: #e7ebf0changes with theme | Color | |
| typography.heading-xl--smbk-typography-heading-xl-font-family--smbk-typography-heading-xl-font-size--smbk-typography-heading-xl-font-weight--smbk-typography-heading-xl-line-height--smbk-typography-heading-xl-letter-spacing--smbk-typography-heading-xl-text-transform | fontFamily: Funnel Display, Funnel Display Fallback, system-ui, sans-seriffontSize: min: 40px · max: 56pxfontWeight: 400lineHeight: 1.2letterSpacing: 0px | The quick brown fox | Typography |
| size.content.lg--smbk-size-content-lg | 960px | Size | |
| color.text.subtle--smbk-color-text-subtle | light: #4e555edark: #9ba5b1changes with theme | Color | |
| typography.body-lg--smbk-typography-body-lg-font-family--smbk-typography-body-lg-font-size--smbk-typography-body-lg-font-weight--smbk-typography-body-lg-line-height--smbk-typography-body-lg-letter-spacing--smbk-typography-body-lg-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 20pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| typography.body-sm--smbk-typography-body-sm-font-family--smbk-typography-body-sm-font-size--smbk-typography-body-sm-font-weight--smbk-typography-body-sm-line-height--smbk-typography-body-sm-letter-spacing--smbk-typography-body-sm-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| space.inline.sm--smbk-space-inline-sm | comfortable: 6pxcompact: 4pxchanges with density | Spacing | |
| size.control.md--smbk-size-control-md | comfortable: 40pxcompact: 32pxchanges with density | Size | |
| size.control.xs--smbk-size-control-xs | 24px | Size | |
| color.surface.default--smbk-color-surface-default | light: #f3f5f8dark: #181a1cchanges with theme | Color | |
| color.icon.subtle--smbk-color-icon-subtle | light: #4e555edark: #c4cdd8changes with theme | Color | |
| size.icon.sm--smbk-size-icon-sm | 16px | Size | |
| space.inline.2xl--smbk-space-inline-2xl | comfortable: 24pxcompact: 16pxchanges with density | Spacing | |
| size.width.320--smbk-size-width-320 | 320px | Size | |
| size.width.480--smbk-size-width-480 | 480px | Size | |
| size.width.240--smbk-size-width-240 | 240px | Size | |
| size.72--smbk-size-72 | 72px | Size | |
| size.88--smbk-size-88 | 88px | Size | |
| typography.heading-lg--smbk-typography-heading-lg-font-family--smbk-typography-heading-lg-font-size--smbk-typography-heading-lg-font-weight--smbk-typography-heading-lg-line-height--smbk-typography-heading-lg-letter-spacing--smbk-typography-heading-lg-text-transform | fontFamily: Funnel Display, Funnel Display Fallback, system-ui, sans-seriffontSize: min: 32px · max: 48pxfontWeight: 400lineHeight: 1.2letterSpacing: 0px | The quick brown fox | Typography |
Manifest
{
"name": "page-header",
"block": "smbk-page-header",
"summary": "The top of one page or one object: the breadcrumbs, the page’s one main heading, a short introduction, its status, and the few actions for the whole page.",
"adapt": "css",
"axes": {
"composition": {
"values": [
"simple",
"object",
"tabbed"
],
"default": "simple",
"description": "Simple is a title, an introduction and actions. Object adds a status beside the title and a line of facts, for the page of one thing. Tabbed ends with tabs that switch the page’s views. The wrapper sets it from what it is given."
}
},
"flags": {
"loading": {
"description": "The page is still loading: the words are skeleton lines of their own height, the actions are drawn disabled, and the tabs are skeletons that cannot be pressed."
}
},
"parts": [
"breadcrumbs",
"main",
"body",
"title-row",
"title",
"status",
"description",
"meta",
"actions",
"action",
"more",
"tabs",
"tabs-cue",
"tabs-skeleton",
"hidden"
],
"figmaNodeId": "7115:869"
}