Skip to content

PageHeader

<caos-page-header>

the eyebrow, title, description, and count at the head of a surface

May be placed on an app’s home page and a page inside an app.

Name Attribute Description Type Default Required
eyebrow eyebrow, markup only The small mono label above the title, saying which app or area this surface belongs to. Hidden entirely when it is empty. text none declared Optional
heading heading, markup only The page title. Drawn as the surface’s

, so it is also its accessible name.

text none declared Required
count count, markup only A record count set beside the title in the muted ink. Text rather than a number, so a host can write “248” or “1,204” or “248 of 3,000” without this piece deciding the wording. Hidden when empty. text none declared Optional
description description, markup only One line under the title saying what the surface holds. A shortcut: it is written into the same element that carries the default slot, so it REPLACES slotted description content rather than adding to it. Set one or the other. text none declared Optional

This piece raises no events.

Name What goes in it
the default slot The description body, when it is richer than one line of text. Overwritten if the description attribute is set as well.
breadcrumb A Breadcrumb, above the eyebrow. The row collapses when nothing is in it.
actions The surface-level controls, pinned to the right of the title row.
Design token What it controls
--caos-color-border The rule drawn under the whole header.
--caos-font-display The typeface of the title.
--caos-text-h2 The size of the title.
--caos-font-mono The typeface of the eyebrow.
--caos-color-text-muted The ink of the eyebrow, the count and the description.
--caos-space-3 The gap between the breadcrumb, the title row and the description.

Nothing else drives this piece by calling it.

Nothing on this piece can be read back.

Part Which piece of it When it is there
::part(header) the header block Always
::part(breadcrumb) the area the trail is slotted into Always
::part(eyebrow) the small line above the title Always
::part(title) the heading Always
::part(count) the count beside the heading Always
::part(description) the line under the heading Always
::part(actions) the action group at the right Always

The top of a surface that is ABOUT a set of things rather than about one record: a list view, a report, a Setup page, an overview.

  • RecordHeader — The surface is ONE RECORD. That one leads with the object type, sets the record name in the accent beneath it, and carries the object mark, the status and the key fields a record needs. This one has nowhere to put any of that, and its title is a page name rather than a record name.
  • AppHome — The surface is an app’s landing page. That is a whole assembled surface with its own hero, not a header you build a page underneath.
  • DetailSection — You want a heading for one block WITHIN a page rather than for the page itself.
  • Breadcrumb — All you need is the trail back up. It goes INSIDE this piece’s breadcrumb slot rather than instead of it.

The title is a real

, so the surface has a document heading and a screen reader announces it as the page title without the author doing anything. The breadcrumb, eyebrow, count, description and actions rows each disappear from the layout entirely when nothing is in them, so an empty slot leaves no announced-but-blank region behind. What is left to the author: only ONE

belongs on a surface, so do not place two of these on one page; and controls put in the actions slot need labels that still make sense read on their own, away from the title.

All four text inputs set as attributes — the shape a list view uses, with the record count beside the title.

{
"id": "example",
"section": "The head of a list",
"columns": 1,
"items": [
{
"id": "page_header_1",
"type": "component",
"key": "page_header",
"inputs": {
"eyebrow": "Sales Atlas",
"heading": "Orders",
"count": "248",
"description": "Every order raised across all accounts."
}
}
]
}
<caos-page-header eyebrow="Sales Atlas" heading="Orders" count="248" description="Every order raised across all accounts."></caos-page-header>

Every slot filled, and the description supplied as slotted content instead of as the attribute — the two paths are exclusive, and this is the other one.

{
"id": "example",
"section": "With a trail and actions",
"columns": 1,
"items": [
{
"id": "page_header_1",
"type": "component",
"key": "page_header",
"inputs": {
"eyebrow": "Sales Atlas",
"heading": "Orders",
"count": "248"
},
"children": [
{
"id": "breadcrumb_2",
"type": "component",
"key": "breadcrumb",
"inputs": {
"items": [
{
"label": "Sales Atlas",
"href": "/sales-atlas"
},
{
"label": "Orders"
}
]
},
"slot": "breadcrumb"
},
"Every order raised across all accounts, newest first.",
{
"id": "button_3",
"type": "component",
"key": "button",
"inputs": {
"variant": "secondary"
},
"slot": "actions",
"children": [
"Export"
]
},
{
"id": "button_4",
"type": "component",
"key": "button",
"inputs": {
"variant": "primary"
},
"slot": "actions",
"children": [
"New order"
]
}
]
}
]
}
<caos-page-header eyebrow="Sales Atlas" heading="Orders" count="248">
<caos-breadcrumb id="pageHeader1breadcrumb1" slot="breadcrumb"></caos-breadcrumb>
Every order raised across all accounts, newest first.
<caos-button slot="actions" variant="secondary">Export</caos-button>
<caos-button slot="actions" variant="primary">New order</caos-button>
</caos-page-header>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const pageHeader1breadcrumb1 = document.getElementById('pageHeader1breadcrumb1');
pageHeader1breadcrumb1.items = [
{
"label": "Sales Atlas",
"href": "/sales-atlas"
},
{
"label": "Orders"
}
];