Skip to content

Commit 10fa11a

Browse files
feat(Templates): Grid Page — template met drierijige responsive grid-layout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9f467ff commit 10fa11a

3 files changed

Lines changed: 356 additions & 0 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Grid Page
2+
3+
Paginatemplate met een drierijige grid-layout voor content met responsieve kolomverdeling.
4+
5+
## Doel
6+
7+
Het Grid Page template bouwt voort op de Base Page-structuur en voegt een `Grid` met drie rijen toe in de `<main>`. Het template laat zien hoe je met `Grid` en `GridItem` een responsive layout opbouwt waarbij kolommen op kleine viewports de volle breedte innemen en vanaf het medium-breakpoint naast elkaar komen te staan.
8+
9+
Templates zijn Storybook-only composities van bestaande componenten. Ze bevatten geen eigen CSS of React component.
10+
11+
<!-- VOORBEELD -->
12+
13+
## Grid-structuur
14+
15+
```html
16+
<div class="dsn-grid dsn-grid--contained">
17+
<!-- Rij 1: volle breedte -->
18+
<div class="dsn-col-12">...</div>
19+
20+
<!-- Rij 2: 2 kolommen vanaf md -->
21+
<div class="dsn-col-12 dsn-col-md-6">...</div>
22+
<div class="dsn-col-12 dsn-col-md-6">...</div>
23+
24+
<!-- Rij 3: 3 kolommen vanaf md -->
25+
<div class="dsn-col-12 dsn-col-md-4">...</div>
26+
<div class="dsn-col-12 dsn-col-md-4">...</div>
27+
<div class="dsn-col-12 dsn-col-md-4">...</div>
28+
</div>
29+
```
30+
31+
```tsx
32+
<Grid contained>
33+
{/* Rij 1 */}
34+
<GridItem colSpan={12}>...</GridItem>
35+
36+
{/* Rij 2 */}
37+
<GridItem colSpan={12} colSpanMd={6}>
38+
...
39+
</GridItem>
40+
<GridItem colSpan={12} colSpanMd={6}>
41+
...
42+
</GridItem>
43+
44+
{/* Rij 3 */}
45+
<GridItem colSpan={12} colSpanMd={4}>
46+
...
47+
</GridItem>
48+
<GridItem colSpan={12} colSpanMd={4}>
49+
...
50+
</GridItem>
51+
<GridItem colSpan={12} colSpanMd={4}>
52+
...
53+
</GridItem>
54+
</Grid>
55+
```
56+
57+
| Rij | Klein (< md) | Medium en groter (>= md) |
58+
| --- | ------------ | ------------------------ |
59+
| 1 | 12/12 | 12/12 |
60+
| 2 | 12/12 elk | 6/12 elk |
61+
| 3 | 12/12 elk | 4/12 elk |
62+
63+
## Use when
64+
65+
- Je een pagina indeelt in een kop-sectie gevolgd door twee- of driekoloms content.
66+
- Je een responsive layout nodig hebt die op mobiel stapelt en op grotere schermen naast elkaar toont.
67+
68+
## Don't use when
69+
70+
- Je geen grid-layout nodig hebt: gebruik dan het Base Page template.
71+
- Je meer controle nodig hebt over breakpoints per rij: definieer dan per `GridItem` de juiste `colSpanSm`, `colSpanMd` en `colSpanLg` props.
72+
73+
## Accessibility
74+
75+
De grid zelf voegt geen ARIA-semantiek toe. Zorg dat de inhoud van elke gridcel een logische leesvolgorde heeft in de DOM (van links naar rechts, van boven naar beneden op grote schermen).
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 GridPageStories from './GridPage.stories';
3+
import docs from './GridPage.docs.md?raw';
4+
import { PreviewFrame } from '../components';
5+
6+
export const [intro, rest] = docs.split('<!-- VOORBEELD -->');
7+
8+
<Meta of={GridPageStories} />
9+
10+
<Markdown>{intro}</Markdown>
11+
12+
## Voorbeeld
13+
14+
<PreviewFrame>
15+
<Story of={GridPageStories.Default} />
16+
</PreviewFrame>
17+
18+
<Markdown>{rest}</Markdown>
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
import React from 'react';
2+
import type { Meta, StoryObj } from '@storybook/react';
3+
import {
4+
Body,
5+
Button,
6+
Container,
7+
Grid,
8+
GridItem,
9+
Link,
10+
Logo,
11+
Menu,
12+
MenuLink,
13+
PageBody,
14+
PageFooter,
15+
PageHeader,
16+
PageLayout,
17+
Paragraph,
18+
SearchInput,
19+
SkipLink,
20+
Stack,
21+
UnorderedList,
22+
} from '@dsn/components-react';
23+
24+
// =============================================================================
25+
// GEDEELDE CONTENT (identiek aan BasePage stories)
26+
// =============================================================================
27+
28+
const logoSlot = (
29+
<a href="/">
30+
<Logo aria-hidden={true} />
31+
<span className="dsn-visually-hidden">
32+
Starter Kit — terug naar homepage
33+
</span>
34+
</a>
35+
);
36+
37+
function PrimaryNavigation() {
38+
const [exp1b, setExp1b] = React.useState(false);
39+
40+
return (
41+
<Menu orientation="vertical">
42+
<MenuLink href="/level-1a" level={1} current>
43+
Level 1a
44+
</MenuLink>
45+
<MenuLink
46+
href="/level-1b"
47+
level={1}
48+
subItems
49+
expanded={exp1b}
50+
onExpandToggle={() => setExp1b((v) => !v)}
51+
>
52+
Level 1b
53+
</MenuLink>
54+
{exp1b && (
55+
<>
56+
<MenuLink href="/level-2a" level={2}>
57+
Level 2a
58+
</MenuLink>
59+
<MenuLink href="/level-2b" level={2}>
60+
Level 2b
61+
</MenuLink>
62+
</>
63+
)}
64+
<MenuLink href="/level-1c" level={1}>
65+
Level 1c
66+
</MenuLink>
67+
<MenuLink href="/level-1d" level={1}>
68+
Level 1d
69+
</MenuLink>
70+
</Menu>
71+
);
72+
}
73+
74+
const primaryNavigationLarge = (
75+
<Menu orientation="horizontal">
76+
<MenuLink href="/level-1a" level={1} current>
77+
Level 1a
78+
</MenuLink>
79+
<MenuLink href="/level-1b" level={1}>
80+
Level 1b
81+
</MenuLink>
82+
<MenuLink href="/level-1c" level={1}>
83+
Level 1c
84+
</MenuLink>
85+
<MenuLink href="/level-1d" level={1}>
86+
Level 1d
87+
</MenuLink>
88+
</Menu>
89+
);
90+
91+
const secondaryNavigation = (
92+
<Menu orientation="vertical">
93+
<MenuLink href="/english" level={1}>
94+
English
95+
</MenuLink>
96+
<MenuLink href="/mijn-omgeving" level={1}>
97+
Mijn omgeving
98+
</MenuLink>
99+
</Menu>
100+
);
101+
102+
const secondaryNavigationLarge = (
103+
<Menu orientation="horizontal">
104+
<MenuLink href="/english" level={1}>
105+
English
106+
</MenuLink>
107+
<MenuLink href="/mijn-omgeving" level={1}>
108+
Mijn omgeving
109+
</MenuLink>
110+
</Menu>
111+
);
112+
113+
const searchSlot = (
114+
<>
115+
<SearchInput placeholder="Zoeken…" aria-label="Zoekopdracht" />
116+
<Button variant="strong">Zoeken</Button>
117+
</>
118+
);
119+
120+
const footerSlot1 = (
121+
<a href="/">
122+
<Logo aria-hidden={true} />
123+
<span className="dsn-visually-hidden">
124+
Starter Kit — terug naar homepage
125+
</span>
126+
</a>
127+
);
128+
129+
const footerSlot2 = (
130+
<Paragraph>
131+
Dit is een voorbeeldorganisatie. <Link href="/about">Meer informatie</Link>.
132+
</Paragraph>
133+
);
134+
135+
const footerSlot3 = (
136+
<UnorderedList>
137+
<li>
138+
<Link href="/nieuws">Nieuws</Link>
139+
</li>
140+
<li>
141+
<Link href="/over-ons">Over ons</Link>
142+
</li>
143+
<li>
144+
<Link href="/werken-bij">Werken bij</Link>
145+
</li>
146+
<li>
147+
<Link href="/klachten">Klachten</Link>
148+
</li>
149+
</UnorderedList>
150+
);
151+
152+
const footerSlot4 = (
153+
<UnorderedList>
154+
<li>
155+
<Link href="/privacy">Privacyverklaring</Link>
156+
</li>
157+
<li>
158+
<Link href="/accessibility">Toegankelijkheid</Link>
159+
</li>
160+
<li>
161+
<Link href="/cookies">Cookies</Link>
162+
</li>
163+
<li>
164+
<Link href="/contact">Contact</Link>
165+
</li>
166+
</UnorderedList>
167+
);
168+
169+
const mainStyle: React.CSSProperties = {
170+
paddingBlock: 'var(--dsn-space-block-6xl)',
171+
paddingInline: 'var(--dsn-space-inline-xl)',
172+
};
173+
174+
// =============================================================================
175+
// META
176+
// =============================================================================
177+
178+
const meta: Meta = {
179+
title: 'Templates/GridPage',
180+
parameters: {
181+
layout: 'fullscreen',
182+
},
183+
};
184+
185+
export default meta;
186+
187+
type Story = StoryObj;
188+
189+
// =============================================================================
190+
// STORIES
191+
// =============================================================================
192+
193+
export const Default: Story = {
194+
name: 'Grid Page',
195+
render: () => (
196+
<Body>
197+
<SkipLink href="#main-content" />
198+
<PageLayout>
199+
<PageHeader
200+
logoSlot={logoSlot}
201+
primaryNavigation={<PrimaryNavigation />}
202+
primaryNavigationLarge={primaryNavigationLarge}
203+
secondaryNavigation={secondaryNavigation}
204+
secondaryNavigationLarge={secondaryNavigationLarge}
205+
searchSlot={searchSlot}
206+
/>
207+
<PageBody>
208+
<main id="main-content" tabIndex={-1} style={mainStyle}>
209+
<Stack space="2xl">
210+
{/* Rij 1: volle breedte */}
211+
<Grid style={{ '--dsn-grid-margin': '0' } as React.CSSProperties}>
212+
<GridItem colSpan={12}>
213+
<Container>
214+
<Paragraph>Rij 1 — volle breedte (12 kolommen)</Paragraph>
215+
</Container>
216+
</GridItem>
217+
</Grid>
218+
219+
{/* Rij 2: 2 kolommen vanaf md */}
220+
<Grid style={{ '--dsn-grid-margin': '0' } as React.CSSProperties}>
221+
<GridItem colSpan={12} colSpanMd={6}>
222+
<Container>
223+
<Paragraph>Rij 2 — kolom 1 van 2</Paragraph>
224+
</Container>
225+
</GridItem>
226+
<GridItem colSpan={12} colSpanMd={6}>
227+
<Container>
228+
<Paragraph>Rij 2 — kolom 2 van 2</Paragraph>
229+
</Container>
230+
</GridItem>
231+
</Grid>
232+
233+
{/* Rij 3: 3 kolommen vanaf md */}
234+
<Grid style={{ '--dsn-grid-margin': '0' } as React.CSSProperties}>
235+
<GridItem colSpan={12} colSpanMd={4}>
236+
<Container>
237+
<Paragraph>Rij 3 — kolom 1 van 3</Paragraph>
238+
</Container>
239+
</GridItem>
240+
<GridItem colSpan={12} colSpanMd={4}>
241+
<Container>
242+
<Paragraph>Rij 3 — kolom 2 van 3</Paragraph>
243+
</Container>
244+
</GridItem>
245+
<GridItem colSpan={12} colSpanMd={4}>
246+
<Container>
247+
<Paragraph>Rij 3 — kolom 3 van 3</Paragraph>
248+
</Container>
249+
</GridItem>
250+
</Grid>
251+
</Stack>
252+
</main>
253+
</PageBody>
254+
<PageFooter
255+
slot1={footerSlot1}
256+
slot2={footerSlot2}
257+
slot3={footerSlot3}
258+
slot4={footerSlot4}
259+
/>
260+
</PageLayout>
261+
</Body>
262+
),
263+
};

0 commit comments

Comments
 (0)