Skip to content

Commit 72e7ce2

Browse files
feat(Templates): Base Page — eerste paginatemplate (#168)
* feat(Templates): Basic Page — eerste paginatemplate Voegt de nieuwe Storybook-categorie 'Templates' toe met het Basic Page template: een complete, toegankelijke paginastructuur van Body + SkipLink + PageLayout + PageHeader + PageBody + PageFooter. Twee stories: Default en Inverse (colorScheme="inverse" op header en footer). Documentatie beschrijft de verantwoordelijkheidsverdeling per laag, waarom <main> in het template zit (niet in PageBody), en de skip-link vereiste (WCAG 2.4.1). Sluit #165. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(Templates): hernoem BasicPage naar BasePage, vervang Container door inline padding - BasicPage → BasePage (bestanden, titels, Storybook-route) - Container verwijderd: padding nu direct op <main> als inline style via tokens: paddingBlock var(--dsn-space-block-6xl) (64px), paddingInline var(--dsn-space-inline-xl) (16px) - Documentatie uitgebreid met uitleg waarom inline style en niet een herbruikbare klasse Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(PageFooter): reset --dsn-grid-margin naar 0 binnen footer context De Grid inside PageFooter had een dubbele padding-inline: de eigen --dsn-grid-margin (24px) bovenop de --dsn-page-footer-padding-inline (16px) van de __inner wrapper. Via een scoped custom property override op .dsn-page-footer valt de Grid-margin weg en lijnt het logo uit met de paginainhoud. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0615f49 commit 72e7ce2

5 files changed

Lines changed: 453 additions & 1 deletion

File tree

packages/components-html/src/page-footer/page-footer.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
============================================================================= */
2626

2727
.dsn-page-footer {
28+
--dsn-grid-margin: 0;
2829
background-color: var(--dsn-page-footer-background-color);
2930
border-block-start: var(--dsn-page-footer-border-block-start-width) solid
3031
var(--dsn-page-footer-border-block-start-color);

packages/storybook/src/Introduction.mdx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ function App() {
124124
- **Form Fields**: FormFieldLabel, FormFieldLegend, FormFieldDescription, FormFieldErrorMessage, FormFieldStatus
125125
- **Form Containers**: FormField (enkelvoudige inputs) en FormFieldset (groepen met legend)
126126

127+
### Templates (1)
128+
129+
- **BasePage**: Volledige paginastructuur met `Body`, `SkipLink`, `PageLayout`, `PageHeader`, `PageBody` en `PageFooter`: fundament voor alle verdere paginatemplates
130+
127131
## Design Tokens
128132

129133
Alle visuele eigenschappen worden gedefinieerd via design tokens. Bekijk de **Foundations / Design Tokens** pagina voor een volledig overzicht van beschikbare tokens.
@@ -173,4 +177,4 @@ MIT License: zie LICENSE bestand voor details.
173177

174178
---
175179

176-
**Versie:** 5.26.0 | **Laatste update:** 17 april 2026 | **Auteur:** Jeffrey Lauwers
180+
**Versie:** 5.27.0 | **Laatste update:** 17 april 2026 | **Auteur:** Jeffrey Lauwers
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Base Page
2+
3+
Eerste paginatemplate: de basisstructuur voor elke pagina in de applicatie.
4+
5+
## Doel
6+
7+
Het Base Page template combineert `Body`, `SkipLink`, `PageLayout`, `PageHeader`, `PageBody` en `PageFooter` tot een complete, toegankelijke paginastructuur. Het template dient als fundament voor alle volgende templates (Homepage, Detailpagina, Formulierpagina, etc.).
8+
9+
Templates zijn Storybook-only composities van bestaande componenten. Ze bevatten geen eigen CSS of React component.
10+
11+
<!-- VOORBEELD -->
12+
13+
## Verantwoordelijkheidsverdeling
14+
15+
Elk onderdeel van het template heeft een afgebakende taak:
16+
17+
| Onderdeel | Element | Verantwoordelijkheid |
18+
| ------------ | ------------------------------- | ----------------------------------------------------------------------- |
19+
| `Body` | `<div class="dsn-body">` | font-family, achtergrondkleur, basistypografie via CSS-overerving |
20+
| `SkipLink` | `<a href="#main-content">` | WCAG 2.4.1 — eerste focusbaar element, verborgen totdat het gefocust is |
21+
| `PageLayout` | `<div class="dsn-page-layout">` | `display: flex; flex-direction: column; min-block-size: 100dvh` |
22+
| `PageHeader` | `<header>` | logo, navigatie, zoeken (impliciet `role="banner"`) |
23+
| `PageBody` | `<div class="dsn-page-body">` | `flex: 1` — vult ruimte tussen header en footer |
24+
| `<main>` | `<main id="main-content">` | Primaire pagina-inhoud (impliciet `role="main"`) |
25+
| `PageFooter` | `<footer>` | links, logo, colofon (impliciet `role="contentinfo"`) |
26+
27+
## Waarom `<main>` in het template en niet in `PageBody`?
28+
29+
`PageBody` is een flexibele container die in complexere templates ook `SideNavigation` en `Breadcrumbs` kan bevatten — elementen die buiten `<main>` vallen. De `<main>` is daarom een expliciete child in het template, niet ingebakken in `PageBody`:
30+
31+
```html
32+
<!-- Base Page: alleen main -->
33+
<div class="dsn-page-body">
34+
<main id="main-content" tabindex="-1">...</main>
35+
</div>
36+
37+
<!-- Later: Detailpagina met side navigation -->
38+
<div class="dsn-page-body">
39+
<nav class="dsn-breadcrumbs" aria-label="Kruimelpad">...</nav>
40+
<div class="dsn-page-body__content">
41+
<nav class="dsn-side-nav" aria-label="Sectienavigatie">...</nav>
42+
<main id="main-content" tabindex="-1">...</main>
43+
</div>
44+
</div>
45+
```
46+
47+
## Padding op `<main>`
48+
49+
Dit template plaatst padding direct als inline style op `<main>`, zonder gebruik van de `Container`-component:
50+
51+
```tsx
52+
<main
53+
id="main-content"
54+
tabIndex={-1}
55+
style={{
56+
paddingBlock: 'var(--dsn-space-block-6xl)', // 64px boven en onder
57+
paddingInline: 'var(--dsn-space-inline-xl)', // 16px links en rechts
58+
}}
59+
>
60+
```
61+
62+
**Waarom geen `Container`?** De `Container`-component voegt een `max-inline-size` toe en centreert de content. Voor templates die een andere breedtebeperking nodig hebben (of geen), is directe padding op `<main>` flexibeler. Andere templates kiezen hun eigen spacing-strategie.
63+
64+
**Waarom inline style en niet een CSS-klasse?** De padding is template-specifiek. Een herbruikbare klasse zou suggereren dat dit het standaard patroon is voor alle templates — dat is niet het geval.
65+
66+
## Use when
67+
68+
- Je een volledige pagina opbouwt met header, inhoud en footer.
69+
- Je een startpunt nodig hebt voor een nieuw paginatype.
70+
71+
## Don't use when
72+
73+
- Je een gedeelte van een pagina opmaakt: gebruik dan `Stack`, `Grid` of `Container`.
74+
- Je geen `PageHeader` of `PageFooter` nodig hebt.
75+
76+
## Best practices
77+
78+
### Skip-link in het template
79+
80+
De skip-link hoort **niet** in `PageLayout` of `PageHeader` — die components weten niets van de `<main>` verderop in de DOM. Het template is de juiste plek omdat het de volledige paginastructuur overziet. De `SkipLink` moet het **eerste focusbare element** in de DOM zijn:
81+
82+
```tsx
83+
<Body>
84+
<SkipLink href="#main-content" /> {/* altijd als eerste */}
85+
<PageLayout>
86+
<PageHeader ... />
87+
...
88+
</PageLayout>
89+
</Body>
90+
```
91+
92+
### `<main>` met `tabIndex={-1}`
93+
94+
Geef de `<main>` altijd `tabIndex={-1}` zodat de skip-link er programmatisch naartoe kan springen. Zonder dit werkt de focus-sprong niet in alle browsers.
95+
96+
### Zichtbare `<h1>` verplicht
97+
98+
Elke pagina moet een zichtbare `<h1>` bevatten (WCAG 2.4.6). Gebruik de `Heading`-component met `level={1}` als eerste heading in `<main>`.
99+
100+
## Accessibility
101+
102+
### Landmarks
103+
104+
De template biedt alle vereiste ARIA-landmarks automatisch via de semantische HTML-elementen:
105+
106+
- `<header>` → impliciet `role="banner"`
107+
- `<main>` → impliciet `role="main"`
108+
- `<footer>` → impliciet `role="contentinfo"`
109+
110+
Screenreadergebruikers kunnen via landmark-navigatie direct naar elk onderdeel springen.
111+
112+
### Skip-link (WCAG 2.4.1)
113+
114+
De skip-link is verborgen totdat de gebruiker er met Tab op focust, waarna hij zichtbaar wordt. Bij activeren springt de focus naar `<main id="main-content">`. Dit voldoet aan WCAG 2.1 succescriterium 2.4.1 (Bypass Blocks, Level A).
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Meta, Story, Markdown } from '@storybook/blocks';
2+
import * as BasePageStories from './BasePage.stories';
3+
import docs from './BasePage.docs.md?raw';
4+
import { PreviewFrame } from '../components';
5+
6+
export const [intro, rest] = docs.split('<!-- VOORBEELD -->');
7+
8+
<Meta of={BasePageStories} />
9+
10+
<Markdown>{intro}</Markdown>
11+
12+
## Voorbeeld
13+
14+
<PreviewFrame>
15+
<Story of={BasePageStories.Default} />
16+
</PreviewFrame>
17+
18+
<Markdown>{rest}</Markdown>

0 commit comments

Comments
 (0)