# Page
Page provides a consistent layout shell for application pages, including navigation aids, a title, page-level actions, and a content area.
Section: components
Docs: https://nebula.kaluza.com/components/page
---
## When to use
- As the outermost layout wrapper for every application page, including dashboards, task flows, and informational views
- Whenever a page needs a consistent title, back navigation, breadcrumbs, or page-level actions
## When not to use
- **For marketing or landing pages** - page is designed for authenticated application views with a standard structure. Use a custom layout instead.
- **Inside a modal** - content inside a [Modal](/components/modal) should not use the Page wrapper.
## Anatomy
1. **Waymarkers** (navigation aids placed above the title; contains the back link and breadcrumbs)
2. **Back link** (an icon link that navigates to the previous page)
3. **Breadcrumbs** (a text trail showing the current page's position in the hierarchy)
4. **Header section** (a flex row containing the title and page actions; stacks vertically on narrow viewports)
5. **Title** (the page's h1 heading)
6. **Actions** (page-level buttons displayed to the right of the title on wider viewports)
7. **Content** (the main body of the page)
## Basic usage
Compose the sub-components inside `Page` in the order that matches the anatomy above.
```tsx
Transactions historyAdd transaction
Page content
```
## Variants and features
### Max width
By default, `Page` centres its content and constrains it to a maximum width of 160rem. Set `maxWidth="none"` to allow the content to fill the available width. Use this for pages that intentionally use full-width layouts, such as dashboards with wide data tables.
```tsx
Transactions history
This content fills the available width instead of being constrained to 160rem.
```
### Breadcrumbs
Use `Page.Breadcrumbs` to show the current page's position within a hierarchy. Pass an array of strings as the `breadcrumbs` prop. The current page is not included. Breadcrumbs show the path to the current page, not the current page itself. The slash separator between items is added automatically and hidden from screen readers.
Include breadcrumbs whenever the page has a parent page in the information architecture.
```tsx
Transactions history
Page content
```
### Back link
Use `Page.BackLink` to provide a single icon link that navigates to the previous page. It renders an arrow-left icon with a visually hidden "Back" label for screen readers.
Use the back link when the user is in a non-sequential flow and needs a quick way to return to a parent page. Avoid back links in multi-step forms, where back navigation should be handled by the form's own step controls, except on the first step where a back link to the previous page outside the form is appropriate.
```tsx
// Example only - always use within the Page component.
```
### Page title
`Page.Title` renders the page's `h1`. It must be the only `h1` on the page. The title must not end with a full stop or exclamation mark.
### Page actions
Use `Page.Actions` to place page-level controls to the right of the page title. Actions are spaced using the `betweenButtons` token and wrap using the Cluster layout.
A page must not have more than one primary action. One secondary action may accompany it. Do not combine a primary page action with another primary button in the page content, as this creates competing calls to action.
```tsx
Transactions historyAdd transaction
Page content
```
### Combining waymarkers
Back link and breadcrumbs can be used together in `Page.Waymarkers` when both are needed.
```tsx
// Example only - always use within the Page component.
```
## Rules and guidance
### Usage
- **Page** SHOULD be the outermost layout wrapper for every authenticated application page. Rationale: Consistent use of Page across all views gives users a predictable layout and reduces bespoke layout code. [id: page.use-as-outermost-wrapper; testability: context]
- **Page.BackLink** SHOULD NOT be used on steps within a multi-step form. Rationale: Back navigation in a multi-step form should be controlled by the form's own step controls to preserve form state and sequence. Exception: the back link is on the first step, where it may be appropriate to navigate away. [id: page.back-link.not-in-multistep-form; testability: context]
- **Page** SHOULD NOT be used for marketing or landing pages. Rationale: Page enforces application layout conventions that are not appropriate for promotional content. [id: page.no-marketing-pages; testability: context]
### Placement
- **Page.Waymarkers** MUST be placed inside Page.Header and above Page.HeaderSection. Rationale: Navigation aids must come before the title in source order so screen reader users encounter them in the correct sequence. [id: page.waymarkers-before-header-section; testability: auto]
- **Page.HeaderSection** SHOULD contain Page.Title before Page.Actions. Rationale: Title first gives screen readers and keyboard users the page name before reaching the actions. [id: page.header-section-order; testability: auto]
### Content
- **Page.Title** MUST clearly describe the purpose of the page. [id: page.title.describes-page; testability: manual]
- **Page.Title** MUST NOT end with a full stop or exclamation mark. [id: page.title.no-punctuation; testability: auto]
- **Page.Breadcrumbs** MUST NOT include the current page in the breadcrumbs array. Rationale: The current page is represented by the Page.Title. Repeating it in the breadcrumb trail creates redundancy. [id: page.breadcrumbs.excludes-current; testability: manual]
- **Page.Title** SHOULD use sentence case. Rationale: Title case is inconsistent with the Kaluza content style and harder to read. [id: page.title.sentence-case; testability: manual]
- If the page displays information rather than prompting an action: **Page.Title** SHOULD use a noun or noun phrase for informational pages. Rationale: 'Payment history' or 'Account summary' describes what the user can see. [id: page.title.informational-noun; testability: manual]
- If the page prompts the user to complete a task: **Page.Title** SHOULD use a verb-noun phrase for task pages. Rationale: 'Make a payment' or 'Add meter reading' makes the user's goal explicit. [id: page.title.task-verb-noun; testability: manual]
### Composition
- **Page** WILL render as an HTML main element by default. The as prop overrides this when a different element is required. [id: page.renders-as-main; testability: auto]
- **Page** WILL wrap its children in a Stack with betweenCards spacing automatically. Direct children do not need their own vertical spacing applied. [id: page.stack-wraps-children; testability: auto]
- **Page.Title** MUST be included on every page. Rationale: Every page must have a visible h1 for screen readers to announce and for users to orient themselves. An absent h1 is an accessibility failure. [id: page.title-required; testability: auto]
- **Page.Title** MUST be the only h1 on the page. Rationale: Multiple h1 elements break the document outline and confuse screen reader navigation. [id: page.title-is-h1; testability: auto]
- **Page.Actions** MUST NOT contain more than one primary action. Rationale: Multiple primary actions dilute emphasis and leave users uncertain about the main task. [id: page.actions.max-one-primary; testability: manual]
- **Page.Breadcrumbs** SHOULD be included when the page has a parent page in the information architecture. Rationale: Breadcrumbs orient users and provide a secondary navigation path back through the hierarchy. [id: page.breadcrumbs.if-has-parent; testability: context]
- **Page.Content** SHOULD be used to wrap the main body of the page. Rationale: Page.Content marks the boundary of the page body for styling and future extension. Placing content directly inside Page bypasses this. [id: page.content-in-page-content; testability: context]
- **Page.Actions primary button** SHOULD NOT appear on a page that already contains a primary CTA Button in the content area. Rationale: Competing primary actions create visual and cognitive ambiguity about what the user should do next. [id: page.actions.no-competing-primary; testability: context]
## Accessibility
### In brief
Page is the `main` landmark of a screen. A screen reader announces the page title as a level 1 heading, and the back link as a link named "Back".
### What Nebula provides
- `Page` renders a `main` landmark and `Page.Title` renders an `h1`.
- `Page.BackLink` is a native link with a visible focus ring. Its arrow icon is hidden from screen readers and the link is named "Back".
- `Page.Breadcrumbs` is an ordered list, and its slash separators are hidden from screen readers.
### Keyboard behaviour
The page itself is not a tab stop. The back link is a native link reached with `Tab`, and the page actions and content are the controls you place in them.
#### Interactions
- **Tab** - Moves focus to the back link, then to the controls in the page actions and content
### Accessible design
#### Write a page title that says what the page is for
The title is the level 1 heading, and often the first thing a screen reader user hears. Make it specific, so two pages never share one.
WCAG: [2.4.6 Headings and Labels](https://www.w3.org/WAI/WCAG22/Understanding/headings-and-labels.html)
#### Don't rely on breadcrumbs to move around
The breadcrumbs are plain text, not links, so they say where the page is without taking the user anywhere. Keep the back link, or the main navigation, as the way back.
### Accessible development
#### Always pass an `href` to `Page.BackLink`
Without one, the link points at the current page and reloads it. Give it the page it goes back to, so "Back" has a destination.
WCAG: [2.4.4 Link Purpose (In Context)](https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html)
#### Never put a `Page` inside another `main`
`Page` is the `main` landmark, and a page has one. If it sits inside the layout of an app that already has one, render it as another element, for example `as="section"`.
WCAG: [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html)
#### Avoid a second `h1` in the page content
`Page.Title` already renders the level 1 heading, so start the headings in the content at level 2.
WCAG: [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html)
### Accessibility testing status
Reviewed on 9 October 2026 against v17.0.1.
| Test | Status | Tested with |
| --- | --- | --- |
| Default state | Tested | axe-core via the Storybook test runner (CI) |
| Advanced states | Partially tested | axe-core via the Storybook test runner (CI) |
| Keyboard navigation | Manually tested | keyboard (reported by the owner) |
| Screen reader | Partially tested | VoiceOver with Chrome (reported by the owner) |
What each test covers:
- Default state: The component as it first appears, before anything is clicked or changed.
- Advanced states: Every other state and variant: error, open, selected, each size and style, and hovering.
- Keyboard navigation: Whether every function can be reached and used with the keyboard alone, with a visible focus ring.
- Screen reader: Whether a screen reader announces what is on screen correctly: the name, the role and any state.
What these statuses mean:
- Tested: Every check in this row passed in an automated run that repeats on every pull request.
- Manually tested: Every check in this row was run once, by a person or with a tool they started, and all of them passed. It is not re-run on every change.
- Partially tested: Some checks in this row were run once and passed; the rest are still to do.
- Issue found: A check in this row failed. The ticket is shown beside the status.
- Not tested: No check in this row has been run yet.
- Not applicable: This row does not apply to the component, for the reason given beside it.
The table records which checks were run on this component on its own. It is not a statement of WCAG conformance, and it does not cover how the component is used.
## Properties
### Page
Also accepts all props from `HTMLAttributes`.
- `maxWidth` (type `'none' | 'standard'`, optional, default `'standard'`). `'standard'` caps the page width at `160rem` and centres it; `'none'` lets it fill its container.
- `as` (type `ElementType`, optional). Renders the page as a different element, e.g. `as="section"`.
- `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped.
### BackLink
### Breadcrumbs
Also accepts all props from `HTMLAttributes`.
- `breadcrumbs` (type `string[]`, required). Labels of the breadcrumbs in order, shown as plain text separated by slashes.
## Styles
Reference only; you do not need this to use the component. This is the CSS its styled elements use, with theme tokens shown by name. `${...}` is an interpolation (a theme token, a mixin or another value from the code). A `&[prop]` or `@when ` block holds CSS that applies only under that condition; these are notation, not valid CSS.
### back-link
```css
text-decoration: none;
font-weight: inherit;
display: inline-flex;
align-items: center;
justify-content: center;
width: ${space[6]};
height: ${space[6]};
color: ${colors.body};
border-radius: ${radii.standard};
&:focus-visible {
${focusRing}
}
&:hover {
background: ${colors.primaryMuted};
}
```
### breadcrumbs
```css
display: flex;
gap: ${space[2]};
align-items: center;
```
### breadcrumb
```css
font-family: ${fonts.body};
line-height: ${lineHeights.small};
font-weight: ${fontWeights.normal};
color: ${colors.body};
font-size: ${responsiveFontSizes.small[0]};
white-space: nowrap;
${mediaQueries.smallAndUp} {
font-size: ${responsiveFontSizes.small[1]};
}
&:not(:first-child):before {
content: '/' / '';
margin-right: ${space[2]};
}
```
### page
```css
width: ${maxWidth === 'standard' ? '160rem' : 'initial'};
max-width: 100%;
margin: 0 auto;
```
### wrapper
```css
flex-shrink: 0;
```
### page-waymarkers
```css
display: flex;
gap: ${space[3]};
```
### page-header-section
```css
display: flex;
gap: ${space[2]};
justify-content: space-between;
align-content: flex-start;
flex-direction: column;
${mediaQueries.smallAndUp} {
flex-direction: row;
}
```
### page-title
```css
margin: 0;
```