Skip to content

Commit 411c21f

Browse files
Merge pull request #146 from jeffreylauwers/feature/page-header-large-viewport
fix(PageHeader): searchbox button padding, navbar hoogte en Storybook controls
2 parents a7e1891 + 96b4832 commit 411c21f

8 files changed

Lines changed: 348 additions & 15 deletions

File tree

CLAUDE.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,26 @@ Kernregels:
9494
- Geen geneste element-namen: `dsn-alert__content__text`
9595
- HTML-toestanden via pseudo-klassen: `.dsn-button:disabled`
9696

97-
### 4. TypeScript moet volledig schoon zijn
97+
### 4. Token-hiërarchie — altijd op de juiste laag aanpassen
98+
99+
Tokens zijn gelaagd: `base.json` (gedeelde primitieven) → component-token JSON → CSS custom property → component CSS. Pas altijd aan op de **hoogste laag die de waarde definieert**, zodat de delegatieketen intact blijft.
100+
101+
```json
102+
// ❌ Omzeilen van de delegatieketen in text-input.json
103+
"padding-block-start": { "value": "{dsn.space.block.md}" }
104+
105+
// ✅ Aanpassen op de juiste laag — in base.json onder form-control
106+
"padding-block-start": { "value": "{dsn.space.block.md}" }
107+
// text-input.json blijft delegeren naar {dsn.form-control.padding-block-start}
108+
```
109+
110+
**Werkwijze bij een token-wijziging:**
111+
112+
1. Zoek via `Grep` welk token de waarde _uiteindelijk_ definieert (vaak in `base.json` of een theme-bestand)
113+
2. Pas dáár de waarde aan
114+
3. Controleer of de delegatieketen in component-JSONs ongewijzigd blijft
115+
116+
### 5. TypeScript moet volledig schoon zijn
98117

99118
`pnpm --filter storybook exec tsc --noEmit` geeft **0 fouten en 0 warnings**. Nieuwe code mag geen nieuwe fouten of warnings introduceren.
100119

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pnpm --filter @dsn/design-tokens watch
6565
# Start Storybook in development mode
6666
pnpm dev
6767

68-
# Run tests (1234 tests across 61 test suites)
68+
# Run tests (1282 tests across 63 test suites)
6969
pnpm test
7070

7171
# Run tests in watch mode
@@ -201,14 +201,15 @@ All components are fully typed with TypeScript and include comprehensive JSDoc d
201201
| **StatusBadge** | Yes | Yes ||
202202
| **Table** | Yes | Yes ||
203203

204-
**Navigation Components (4)**
204+
**Navigation Components (5)**
205205

206206
| Component | HTML/CSS | React | Web Component |
207207
| ------------------------ | -------- | ----- | ------------- |
208208
| **BreadcrumbNavigation** | Yes | Yes ||
209209
| **Menu** | Yes | Yes ||
210210
| **MenuButton** | Yes | Yes ||
211211
| **MenuLink** | Yes | Yes ||
212+
| **PageHeader** | Yes | Yes ||
212213

213214
**Form Components (25)**
214215

@@ -383,7 +384,7 @@ Comprehensive documentation is available in the `/docs` folder:
383384

384385
- **Pre-commit hooks** via Husky + lint-staged (ESLint + Prettier)
385386
- **Type checking** across all packages (`pnpm type-check`)
386-
- **1248 tests** covering React components, Web Components, and utilities
387+
- **1282 tests** covering React components, Web Components, and utilities
387388
- **CI/CD** via GitHub Actions (lint, type-check, test, build)
388389

389390
## Tech Stack

docs/03-components.md

Lines changed: 223 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Components
22

3-
**Last Updated:** April 5, 2026
3+
**Last Updated:** April 6, 2026
44

55
Complete component specifications and guidelines for the Design System Starter Kit.
66

@@ -1587,7 +1587,7 @@ const [isOpen, setIsOpen] = React.useState(false);
15871587

15881588
## Navigation Components
15891589

1590-
**Status:** Complete (HTML/CSS, React) — 4 components total
1590+
**Status:** Complete (HTML/CSS, React) — 5 components total
15911591

15921592
### Menu
15931593

@@ -1899,6 +1899,223 @@ const [isOpen, setIsOpen] = React.useState(false);
18991899

19001900
---
19011901

1902+
### PageHeader
1903+
1904+
**Status:** Complete (HTML/CSS, React)
1905+
1906+
**Location:** `packages/components-{html|react}/src/PageHeader/`
1907+
1908+
**Tokens:** `tokens/components/page-header.json`
1909+
1910+
**Props:** `logoSlot`, `sticky` (`'none'` | `'sticky'` | `'auto-hide'`), `primaryNavigation`, `primaryNavigationLarge`, `secondaryNavigation`, `secondaryNavigationLarge`, `searchSlot`, `onMenuOpen`, `onMenuClose`, `onSearchOpen`, `onSearchClose`, `className`
1911+
1912+
**Features:**
1913+
1914+
- Mobile-first: hamburgerknop (inline-start) opent een `Drawer`, gecentreerd logo (CSS-grid `1fr auto 1fr`), zoekknop (inline-end) ontvouwt zoekpaneel direct onder de header
1915+
- Boven `64em` (~1024px): tweebandig large viewport layout via `display: none` switch
1916+
- **Masthead** — neutrale achtergrond met logo (inline-start), servicemenu en inline zoekveld (inline-end)
1917+
- **Navigatiebalk** — accent-1 achtergrond met primaire navigatie; MenuLink-items krijgen `min-block-size: 4rem` en `padding-inline: var(--dsn-space-inline-xl)` via token-overschrijving op de container
1918+
- `primaryNavigationLarge` / `secondaryNavigationLarge` — aparte slots voor large viewport; valt terug op de mobile variant wanneer weggelaten
1919+
- `sticky='sticky'`: `position: sticky; inset-block-start: 0`
1920+
- `sticky='auto-hide'`: sticky + verbergt bij scroll-down via JS `scroll`-eventlistener (`data-hidden` attribuut), CSS-transitie animeert de beweging
1921+
- Focus management: openen zoekpaneel → focus naar `<input>`; sluiten → focus terug naar zoekknop
1922+
- Unieke IDs via `useId()` voor `aria-controls` (zoekpaneel) en `aria-labelledby` (nav-elementen)
1923+
- De `Drawer` is altijd in de DOM (niet `hidden`) zodat focus management correct werkt
1924+
1925+
**CSS-klassen:**
1926+
1927+
| Klasse | Element | Beschrijving |
1928+
| --------------------------------- | ---------- | ----------------------------------------------------- |
1929+
| `dsn-page-header` | `<header>` | Basiscomponent |
1930+
| `dsn-page-header--sticky` | `<header>` | Sticky gedrag: `position: sticky` |
1931+
| `dsn-page-header--auto-hide` | `<header>` | Auto-hide sticky: CSS-transitie op `data-hidden` |
1932+
| `dsn-page-header__small-layout` | `<div>` | Zichtbaar op small viewport (`< 64em`) |
1933+
| `dsn-page-header__large-layout` | `<div>` | Zichtbaar op large viewport (`≥ 64em`) |
1934+
| `dsn-page-header__inner` | `<div>` | CSS-grid (`1fr auto 1fr`) voor gecentreerd logo |
1935+
| `dsn-page-header__start` | `<div>` | Inline-start slot (hamburgerknop) |
1936+
| `dsn-page-header__logo` | `<div>` | Logo-slot; `max-block-size` op directe child |
1937+
| `dsn-page-header__end` | `<div>` | Inline-end slot (zoekknop) |
1938+
| `dsn-page-header__search-panel` | `<div>` | Zoekpaneel (small viewport); verborgen via `[hidden]` |
1939+
| `dsn-page-header__search-inner` | `<div>` | Flex-container: zoekveld + zoekknop |
1940+
| `dsn-page-header__masthead` | `<div>` | Bovenste band large viewport (neutrale achtergrond) |
1941+
| `dsn-page-header__masthead-inner` | `<div>` | Flex-container: logo ↔ secondary-nav |
1942+
| `dsn-page-header__secondary-nav` | `<div>` | Servicemenu + zoekveld naast elkaar (inline-end) |
1943+
| `dsn-page-header__searchbox` | `<div>` | Inline zoekveld + zoekknop in masthead |
1944+
| `dsn-page-header__navbar` | `<div>` | Onderste band large viewport (accent-1 achtergrond) |
1945+
1946+
**Design tokens:**
1947+
1948+
| Token | Waarde | Beschrijving |
1949+
| ------------------------------------------------- | ------------------------------------ | ------------------------------------------- |
1950+
| `--dsn-page-header-background-color` | `{dsn.color.neutral.bg-document}` | Achtergrondkleur header |
1951+
| `--dsn-page-header-border-block-end-width` | `{dsn.border.width.thick}` | Breedte onderkantrand (4px, small viewport) |
1952+
| `--dsn-page-header-border-block-end-color` | `{dsn.color.accent-1.color-default}` | Kleur onderkantrand (merkkleur) |
1953+
| `--dsn-page-header-padding-block` | `{dsn.space.block.md}` | Verticale padding mobile binnenbalk |
1954+
| `--dsn-page-header-padding-inline` | `{dsn.space.inline.xl}` | Horizontale padding mobile binnenbalk |
1955+
| `--dsn-page-header-z-index` | `300` | Z-index sticky — onder backdrop (400) |
1956+
| `--dsn-page-header-logo-max-block-size` | `2rem` | Maximale hoogte logo (32px) |
1957+
| `--dsn-page-header-search-panel-background-color` | `{dsn.color.accent-1.bg-default}` | Achtergrond zoekpaneel (small viewport) |
1958+
| `--dsn-page-header-search-panel-padding-block` | `{dsn.space.block.md}` | Verticale padding zoekpaneel |
1959+
| `--dsn-page-header-search-panel-padding-inline` | `{dsn.space.inline.xl}` | Horizontale padding zoekpaneel |
1960+
| `--dsn-page-header-masthead-background-color` | `{dsn.color.neutral.bg-document}` | Masthead achtergrond (large viewport) |
1961+
| `--dsn-page-header-masthead-padding-block` | `{dsn.space.block.xl}` | Verticale padding masthead |
1962+
| `--dsn-page-header-masthead-padding-inline` | `{dsn.space.inline.xl}` | Horizontale padding masthead |
1963+
| `--dsn-page-header-navbar-background-color` | `{dsn.color.accent-1.bg-default}` | Navigatiebalk achtergrond |
1964+
| `--dsn-page-header-navbar-padding-inline` | `{dsn.space.inline.xl}` | Horizontale padding navigatiebalk |
1965+
| `--dsn-page-header-secondary-nav-gap` | `{dsn.space.column.3xl}` | Gap servicemenu ↔ zoekveld in masthead |
1966+
1967+
**Usage:**
1968+
1969+
```html
1970+
<!-- HTML/CSS — small viewport -->
1971+
<header class="dsn-page-header">
1972+
<div class="dsn-page-header__small-layout">
1973+
<div class="dsn-page-header__inner">
1974+
<div class="dsn-page-header__start">
1975+
<button
1976+
type="button"
1977+
class="dsn-button dsn-button--subtle"
1978+
aria-expanded="false"
1979+
aria-controls="nav-drawer"
1980+
>
1981+
<svg class="dsn-icon" aria-hidden="true"><!-- menu --></svg>
1982+
<span class="dsn-button__label">Menu</span>
1983+
</button>
1984+
</div>
1985+
<div class="dsn-page-header__logo">
1986+
<a href="/">
1987+
<svg class="dsn-logo" aria-hidden="true"><!-- logo --></svg>
1988+
<span class="dsn-visually-hidden"
1989+
>Naam organisatie — terug naar homepage</span
1990+
>
1991+
</a>
1992+
</div>
1993+
<div class="dsn-page-header__end">
1994+
<button
1995+
type="button"
1996+
class="dsn-button dsn-button--subtle"
1997+
aria-expanded="false"
1998+
aria-controls="search-panel"
1999+
>
2000+
<svg class="dsn-icon" aria-hidden="true"><!-- search --></svg>
2001+
<span class="dsn-button__label">Zoeken</span>
2002+
</button>
2003+
</div>
2004+
</div>
2005+
<div class="dsn-page-header__search-panel" id="search-panel" hidden>
2006+
<div class="dsn-page-header__search-inner">
2007+
<div class="dsn-search-input-wrapper">
2008+
<input
2009+
type="search"
2010+
class="dsn-text-input"
2011+
placeholder="Zoeken…"
2012+
aria-label="Zoekopdracht"
2013+
/>
2014+
</div>
2015+
<button type="button" class="dsn-button dsn-button--strong">
2016+
<span class="dsn-button__label">Zoeken</span>
2017+
</button>
2018+
</div>
2019+
</div>
2020+
</div>
2021+
2022+
<!-- Large viewport (zichtbaar boven 64em) -->
2023+
<div class="dsn-page-header__large-layout">
2024+
<div class="dsn-page-header__masthead">
2025+
<div class="dsn-page-header__masthead-inner">
2026+
<div class="dsn-page-header__logo">
2027+
<a href="/"
2028+
><svg class="dsn-logo" aria-hidden="true"><!-- logo --></svg></a
2029+
>
2030+
</div>
2031+
<div class="dsn-page-header__secondary-nav">
2032+
<nav aria-labelledby="service-nav-id">
2033+
<h2 id="service-nav-id" class="dsn-visually-hidden">Servicemenu</h2>
2034+
<ul class="dsn-menu dsn-menu--horizontal">
2035+
<!-- MenuLink items -->
2036+
</ul>
2037+
</nav>
2038+
<div class="dsn-page-header__searchbox">
2039+
<div class="dsn-search-input-wrapper">
2040+
<input
2041+
type="search"
2042+
class="dsn-text-input"
2043+
placeholder="Zoeken…"
2044+
aria-label="Zoekopdracht"
2045+
/>
2046+
</div>
2047+
<button type="button" class="dsn-button dsn-button--strong">
2048+
<span class="dsn-button__label">Zoeken</span>
2049+
</button>
2050+
</div>
2051+
</div>
2052+
</div>
2053+
</div>
2054+
<div class="dsn-page-header__navbar">
2055+
<nav aria-labelledby="primary-nav-id">
2056+
<h2 id="primary-nav-id" class="dsn-visually-hidden">Hoofdmenu</h2>
2057+
<ul class="dsn-menu dsn-menu--horizontal">
2058+
<!-- MenuLink items -->
2059+
</ul>
2060+
</nav>
2061+
</div>
2062+
</div>
2063+
</header>
2064+
```
2065+
2066+
```tsx
2067+
// React
2068+
<PageHeader
2069+
logoSlot={
2070+
<a href="/">
2071+
<Logo aria-hidden={true} />
2072+
<span className="dsn-visually-hidden">
2073+
Naam organisatie — terug naar homepage
2074+
</span>
2075+
</a>
2076+
}
2077+
primaryNavigation={
2078+
<Menu orientation="vertical">
2079+
<MenuLink href="/home" level={1}>
2080+
Home
2081+
</MenuLink>
2082+
</Menu>
2083+
}
2084+
primaryNavigationLarge={
2085+
<Menu orientation="horizontal">
2086+
<MenuLink href="/home" level={1} current>
2087+
Home
2088+
</MenuLink>
2089+
</Menu>
2090+
}
2091+
secondaryNavigation={
2092+
<Menu orientation="vertical">
2093+
<MenuLink href="/contact" level={1}>
2094+
Contact
2095+
</MenuLink>
2096+
</Menu>
2097+
}
2098+
secondaryNavigationLarge={
2099+
<Menu orientation="horizontal">
2100+
<MenuLink href="/contact" level={1}>
2101+
Contact
2102+
</MenuLink>
2103+
</Menu>
2104+
}
2105+
searchSlot={
2106+
<>
2107+
<SearchInput placeholder="Zoeken…" aria-label="Zoekopdracht" />
2108+
<Button variant="strong">Zoeken</Button>
2109+
</>
2110+
}
2111+
sticky="auto-hide"
2112+
/>
2113+
```
2114+
2115+
**Tests:** React (38 tests)
2116+
2117+
---
2118+
19022119
## Branding Components
19032120

19042121
**Status:** Complete (HTML/CSS, React) — 1 component total
@@ -2294,15 +2511,15 @@ defineButton('my-custom-button');
22942511

22952512
## Component Statistics
22962513

2297-
**Total Components:** 48
2514+
**Total Components:** 49
22982515

22992516
**Implementations:**
23002517

2301-
- **HTML/CSS:** 48 components
2302-
- **React:** 48 components (1248 tests total, 62 test suites)
2518+
- **HTML/CSS:** 49 components
2519+
- **React:** 49 components (1282 tests total, 63 test suites)
23032520
- **Web Component:** 7 components (Button, Heading, Icon, Link, OrderedList, Paragraph, UnorderedList)
23042521

2305-
**Test Coverage:** 1248 tests across 62 test suites
2522+
**Test Coverage:** 1282 tests across 63 test suites
23062523

23072524
---
23082525

docs/changelog.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,41 @@ All notable changes to this project are documented in this file.
66

77
---
88

9+
## Version 5.21.1 (April 6, 2026)
10+
11+
### Fix: PageHeader large viewport verfijningen (PR #146)
12+
13+
#### Fixed
14+
15+
- **Zoekknop padding** in de masthead (large viewport): `SearchInput` wrapper krijgt `flex: 1` + `max-inline-size: none` zodat de Zoeken-knop zijn natuurlijke breedte en padding behoudt — zelfde patroon als het mobile zoekpaneel
16+
- **Token gecorrigeerd**: `--dsn-page-header-padding-inline` van `{dsn.space.row.md}` naar `{dsn.space.inline.xl}` — nu consistent met masthead en navbar padding
17+
- **Docs gecorrigeerd**: `--dsn-page-header-search-panel-background-color` was gedocumenteerd als `{dsn.color.neutral.bg-subtle}`, is `{dsn.color.accent-1.bg-default}`
18+
19+
#### Changed
20+
21+
- **Navbar MenuLink-hoogte en padding**: `min-block-size: 4rem` en `padding-inline: var(--dsn-space-inline-xl)` via token-overschrijving op `.dsn-page-header__navbar`; de `calc()`-compensatie voor de current-indicator werkt automatisch mee
22+
- **Storybook controls opgeschoond**: circulaire `DocsPage`-import verwijderd (loste crash op), `argTypes` geherstructureerd in drie categorieën: **Gedrag** / **Slots** / **Events**
23+
24+
---
25+
26+
## Version 5.21.0 (April 6, 2026)
27+
28+
### PageHeader large viewport layout (PR #142)
29+
30+
#### Added
31+
32+
- **Large viewport layout** (≥ 64em) voor `PageHeader` — tweebandig ontwerp boven `64em` via `display: none` switch:
33+
- **Masthead** (`dsn-page-header__masthead`) — neutrale achtergrond met logo (inline-start), servicemenu en inline zoekveld (inline-end)
34+
- **Navigatiebalk** (`dsn-page-header__navbar`) — accent-1 achtergrond met primaire navigatie
35+
- `primaryNavigationLarge` prop — aparte navigatie-inhoud voor de navigatiebalk op large viewport; valt terug op `primaryNavigation` wanneer weggelaten
36+
- `secondaryNavigationLarge` prop — aparte servicemenu-inhoud voor de masthead; valt terug op `secondaryNavigation` wanneer weggelaten
37+
- `searchSlot` prop — inline zoekveld (SearchInput + zoekknop) in de masthead rechts van het servicemenu
38+
- CSS-klassen: `dsn-page-header__large-layout`, `dsn-page-header__small-layout`, `dsn-page-header__masthead`, `dsn-page-header__masthead-inner`, `dsn-page-header__secondary-nav`, `dsn-page-header__searchbox`, `dsn-page-header__navbar`
39+
- 6 nieuwe design tokens: `masthead-background-color`, `masthead-padding-block`, `masthead-padding-inline`, `navbar-background-color`, `navbar-padding-inline`, `secondary-nav-gap`
40+
- 38 React tests
41+
42+
---
43+
944
## Version 5.20.0 (April 5, 2026)
1045

1146
### Logo component (issue #126)

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,32 @@
9797
gap: var(--dsn-space-inline-md);
9898
}
9999

100+
/* SearchInput wrapper vult beschikbare ruimte — button behoudt zijn natuurlijke
101+
breedte en padding. Zelfde patroon als dsn-page-header__search-inner. */
102+
.dsn-page-header__searchbox .dsn-search-input-wrapper {
103+
flex: 1;
104+
min-inline-size: 0;
105+
max-inline-size: none;
106+
}
107+
108+
.dsn-page-header__searchbox .dsn-text-input {
109+
max-inline-size: none;
110+
inline-size: 100%;
111+
}
112+
100113
/* =============================================================================
101114
Navbar (large viewport) — accent-1 achtergrond, primaire navigatie
102115
============================================================================= */
103116

104117
.dsn-page-header__navbar {
105118
background-color: var(--dsn-page-header-navbar-background-color);
106119
padding-inline: var(--dsn-page-header-navbar-padding-inline);
120+
121+
/* MenuLink-items in de navbar krijgen een grotere min-block-size en ruimere
122+
padding-inline. Door de tokens te overschrijven op de container werkt de
123+
calc()-compensatie voor de current-indicator automatisch mee. */
124+
--dsn-menu-item-min-block-size: 4rem;
125+
--dsn-menu-item-padding-inline: var(--dsn-space-inline-xl);
107126
}
108127

109128
/* =============================================================================

0 commit comments

Comments
 (0)