Navigationstable.page-header
Page header
The top of every page: optional back link, the h1, one line of context and the page's actions.
Every page starts with a page header (UX guide §3.2): an h1 with the page title, an optional one-line meta ("3 warten auf Freigabe · Formular online") and at most one primary action plus up to two secondary ones. Further actions go into an overflow menu composed by the app.
Detail pages get a back link to their parent list, named after it ("Mitglieder"), not "Zurück" and not browser history.
The title is the target of focus after a route change: render gives the h1 the id {id} (default page-title) and tabindex="-1", so the router can call document.getElementById("page-title").focus() and screen readers announce the new page.
On narrow screens the actions wrap below the title. render rejects more than one primary action.
Examples
List page with action
Anmeldungen
Data
render({
"title": "Anmeldungen",
"meta": "3 warten auf Freigabe · Formular online",
"actions": [
{
"label": "Formular ansehen",
"href": "/public/registration",
"icon": "external-link"
}
]
})Markup
<header class="page-header">
<div class="page-header__text">
<h1 class="page-header__title" id="page-title" tabindex="-1">Anmeldungen</h1>
<p class="page-header__meta">3 warten auf Freigabe · Formular online</p>
</div>
<div class="button-row"><a class="button" href="/public/registration"><svg class="icon" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#external-link"/></svg>Formular ansehen</a></div>
</header>Detail page with back link
Lea Müller
Data
render({
"title": "Lea Müller",
"meta": "Kinder 7–10 · 6. Kup · seit 2024",
"back": {
"label": "Mitglieder",
"href": "/admin/members"
},
"actions": [
{
"icon": "more",
"iconOnly": true,
"variant": "ghost",
"ariaLabel": "Weitere Aktionen für Lea Müller"
},
{
"label": "Bearbeiten",
"variant": "primary",
"icon": "edit"
}
]
})Markup
<header class="page-header">
<div class="page-header__text">
<a class="back-link" href="/admin/members"><svg class="icon" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#arrow-left"/></svg>Mitglieder</a>
<h1 class="page-header__title" id="page-title" tabindex="-1">Lea Müller</h1>
<p class="page-header__meta">Kinder 7–10 · 6. Kup · seit 2024</p>
</div>
<div class="button-row"><button class="button" type="button" data-variant="ghost" data-icon-only aria-label="Weitere Aktionen für Lea Müller"><svg class="icon" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#more"/></svg></button><button class="button" type="button" data-variant="primary"><svg class="icon" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#edit"/></svg>Bearbeiten</button></div>
</header>Title only
Einstellungen
Data
render({
"title": "Einstellungen"
})Markup
<header class="page-header">
<div class="page-header__text">
<h1 class="page-header__title" id="page-title" tabindex="-1">Einstellungen</h1>
</div>
</header>Data contract
What render(data) accepts — validated against this JSON Schema in development and tests. Unknown fields are rejected. The same schema is in components.json.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | optional | "page-title" | Id of the h1 (focus target after navigation).pattern ^[A-Za-z][\w-]*$ |
title | string | required | — | Page title — the same words as the nav item or the object name ("Anmeldungen", "Lea Müller"). min 1 chars |
meta | string | optional | — | One line of context or summary ("3 warten auf Freigabe · Formular online"). |
back | object | optional | — | Link to the parent page, shown above the title. |
back.label | string | required | — | Parent page name ("Mitglieder"). min 1 chars |
back.href | string | required | — | URL of the parent page. |
actions | array of objects | optional | — | Page actions (Button data), primary last. At most one primary.max 3 items |
actions[].label | string | optional | — | Visible text. Required unless iconOnly.min 1 chars |
actions[].variant | "secondary" | "primary" | "ghost" | "danger" | "danger-ghost" | optional | "secondary" | Visual weight / meaning. |
actions[].size | "sm" | "md" | "lg" | optional | "md" | sm (36 px) only in dense desktop tables; lg (52 px) for the single main action of a screen. |
actions[].icon | string | optional | — | Icon id from the sprite, shown before the label. |
actions[].iconOnly | boolean | optional | false | Square button showing only icon. Requires ariaLabel. |
actions[].ariaLabel | string | optional | — | Accessible name when the visible label is missing or ambiguous ("Weitere Aktionen für Lea Müller"). |
actions[].href | string | optional | — | Render an <a> that looks like a button (navigation, not actions). |
actions[].type | "button" | "submit" | "reset" | optional | "button" | Button type inside forms. |
actions[].value | string | optional | — | Form value, e.g. cancel / confirm inside <form method="dialog">. |
actions[].name | string | optional | — | Form field name when the button submits a value. |
actions[].disabled | boolean | optional | false | Not available now. Prefer explaining why nearby over silently disabling. |
actions[].busy | boolean | optional | false | Action in progress (aria-busy). |
actions[].formNoValidate | boolean | optional | false | Submit without native validation — use on "Abbrechen" inside <form method="dialog"> so required fields do not block closing. |
actions[].block | "always" | "mobile" | optional | — | Full width always, or only below 768 px. |
actions[].behavior | string | optional | — | Behaviour hook (data-behavior), e.g. open-dialog, theme-toggle. |
actions[].action | string | optional | — | Screen action name (data-action). Declared in the actions of the screen with a handler and a risk class; the engine adds undo or confirm accordingly.pattern ^[a-z][a-zA-Z0-9-]*$ |
actions[].actionId | string | optional | — | Id of the thing the action applies to (data-id), when the button is not inside an element that has one.pattern ^[A-Za-z0-9_-]+$ |
actions[].target | string | optional | — | Id of the element a behaviour acts on (data-target), e.g. the dialog to open. |
actions[].popoverTarget | string | optional | — | Id of a popover menu this button toggles (popovertarget). |
JSON Schema
{
"type": "object",
"additionalProperties": false,
"required": [
"title"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z][\\w-]*$",
"default": "page-title",
"description": "Id of the `h1` (focus target after navigation)."
},
"title": {
"type": "string",
"minLength": 1,
"description": "Page title — the same words as the nav item or the object name (\"Anmeldungen\", \"Lea Müller\")."
},
"meta": {
"type": "string",
"description": "One line of context or summary (\"3 warten auf Freigabe · Formular online\")."
},
"back": {
"type": "object",
"additionalProperties": false,
"required": [
"label",
"href"
],
"description": "Link to the parent page, shown above the title.",
"properties": {
"label": {
"type": "string",
"minLength": 1,
"description": "Parent page name (\"Mitglieder\")."
},
"href": {
"type": "string",
"description": "URL of the parent page."
}
}
},
"actions": {
"type": "array",
"maxItems": 3,
"description": "Page actions (Button data), primary last. At most one `primary`.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [],
"properties": {
"label": {
"type": "string",
"minLength": 1,
"description": "Visible text. Required unless `iconOnly`."
},
"variant": {
"type": "string",
"enum": [
"secondary",
"primary",
"ghost",
"danger",
"danger-ghost"
],
"default": "secondary",
"description": "Visual weight / meaning."
},
"size": {
"type": "string",
"enum": [
"sm",
"md",
"lg"
],
"default": "md",
"description": "`sm` (36 px) only in dense desktop tables; `lg` (52 px) for the single main action of a screen."
},
"icon": {
"type": "string",
"description": "Icon id from the sprite, shown before the label."
},
"iconOnly": {
"type": "boolean",
"default": false,
"description": "Square button showing only `icon`. Requires `ariaLabel`."
},
"ariaLabel": {
"type": "string",
"description": "Accessible name when the visible label is missing or ambiguous (\"Weitere Aktionen für Lea Müller\")."
},
"href": {
"type": "string",
"description": "Render an `<a>` that looks like a button (navigation, not actions)."
},
"type": {
"type": "string",
"enum": [
"button",
"submit",
"reset"
],
"default": "button",
"description": "Button type inside forms."
},
"value": {
"type": "string",
"description": "Form value, e.g. `cancel` / `confirm` inside `<form method=\"dialog\">`."
},
"name": {
"type": "string",
"description": "Form field name when the button submits a value."
},
"disabled": {
"type": "boolean",
"default": false,
"description": "Not available now. Prefer explaining why nearby over silently disabling."
},
"busy": {
"type": "boolean",
"default": false,
"description": "Action in progress (`aria-busy`)."
},
"formNoValidate": {
"type": "boolean",
"default": false,
"description": "Submit without native validation — use on \"Abbrechen\" inside `<form method=\"dialog\">` so required fields do not block closing."
},
"block": {
"type": "string",
"enum": [
"always",
"mobile"
],
"description": "Full width always, or only below 768 px."
},
"behavior": {
"type": "string",
"description": "Behaviour hook (`data-behavior`), e.g. `open-dialog`, `theme-toggle`."
},
"action": {
"type": "string",
"pattern": "^[a-z][a-zA-Z0-9-]*$",
"description": "Screen action name (`data-action`). Declared in the `actions` of the screen with a handler and a risk class; the engine adds undo or confirm accordingly."
},
"actionId": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]+$",
"description": "Id of the thing the action applies to (`data-id`), when the button is not inside an element that has one."
},
"target": {
"type": "string",
"description": "Id of the element a behaviour acts on (`data-target`), e.g. the dialog to open."
},
"popoverTarget": {
"type": "string",
"description": "Id of a popover menu this button toggles (`popovertarget`)."
}
},
"description": "One button."
}
}
}
}Markup & states
Root: header.page-header — usable without render() by writing the markup directly.
Parts
| Part | Description |
|---|---|
.page-header__text | Column with back link, title and meta. |
.back-link | Back to the parent page (arrow-left, flips in RTL). |
.page-header__title | The h1 (tabindex="-1" as focus target). |
.page-header__meta | Muted context line. |
.button-row | Actions at the end side; wrap below on narrow screens. |
Accessibility
- Exactly one
h1per page — the page header's title. - The
h1is focusable by script (tabindex="-1") so focus can move to it after a route change. - The back link names its destination; its arrow icon is decorative.