@ndwnu/design-system 13.1.0 → 13.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md ADDED
@@ -0,0 +1,1525 @@
1
+ # @ndwnu/design-system
2
+
3
+ > **Guidelines version**: Written for `@ndwnu/design-system@13.1.1` (2026-05-01). If your installed version is newer, check the [CHANGELOG](./CHANGELOG.md) for additions or breaking changes since this version.
4
+
5
+ Angular component library for NDW (Nationaal Dataportaal Wegverkeer) applications. Install via `npm install @ndwnu/design-system`.
6
+
7
+ ## Rules
8
+
9
+ - **Always use NDW components** instead of native HTML or Material components when an NDW equivalent exists.
10
+ - **Always use NDW CSS custom properties** for colors (`--ndw-color-*`), spacing (`--ndw-spacing-*`), typography (`--ndw-font-*`), borders, and elevation. Never hardcode values.
11
+ - **Always use NDW utility CSS classes** for typography (`.ndw-heading-*`, `.ndw-paragraph-*`), grid layout (`.ndw-grid`, `.ndw-c-grid`), and directives (`[ndwButton]`, `[ndwInput]`, `[ndwLink]`, `[ndwLabel]`, `[ndwDivider]`).
12
+ - **Always use NDW SCSS mixins** for typography in component SCSS files (`@include ndw-heading-xl`, `@include ndw-paragraph-md`, etc.).
13
+ - **Import the global stylesheet** in your `angular.json` styles array to get all base styles, component styles, layout, and utilities.
14
+ - **Themes**: Default theme is NDW (orange primary). NWB theme is available via `data-theme="nwb"` on `<html>`.
15
+ - **Angular signals & OnPush**: All NDW components use `ChangeDetectionStrategy.OnPush`. Follow the same conventions in your own components: use `input()` / `input.required()` instead of `@Input()`, `output()` instead of `@Output()`, `model()` for two-way bindings, `signal()` / `computed()` for local state, `inject()` instead of constructor injection, and `viewChild()` / `contentChildren()` for queries. Never use `@Component({ changeDetection: ChangeDetectionStrategy.Default })` — always set `OnPush`. Avoid manual `ChangeDetectorRef.detectChanges()` / `markForCheck()` calls; signals handle reactivity automatically.
16
+ - **Never use inline styles**: Do not use `style="..."` in HTML templates. If you need sizing, spacing, or colors, first check if an NDW component attribute (e.g., `large` on `[ndwButton]` for 40px height), CSS custom property (`--ndw-spacing-*`), or utility class already covers it. If exact values don't exist but something close does (e.g., you want 42px but `large` gives 40px), prefer the design system value — consistency matters more than pixel-perfection. Only if nothing fits, create a CSS class in the component's SCSS file using NDW tokens.
17
+ - **Accessibility (a11y)**: Always ensure WCAG 2.1 AA compliance:
18
+ - **Color contrast**: Never rely on color alone to convey meaning. Ensure text meets minimum contrast ratios (4.5:1 for normal text, 3:1 for large text) against its background. When combining NDW color tokens, verify contrast — e.g., don't place `grey-400` text on `grey-100` backgrounds.
19
+ - **ARIA labels**: Many NDW components accept `ariaLabel` inputs (e.g., `ndw-badge`, `ndw-map-button`, `ndw-form-field`), but for custom or composed UI you must add ARIA attributes yourself. Always add `aria-label` or `aria-labelledby` to icon-only buttons, interactive elements without visible text, and landmark regions. Use `aria-live` regions for dynamic content updates (toasts, alerts, loaders). Add `aria-describedby` to link form fields to their error/help messages when not using `ndw-form-field`.
20
+ - **Semantic HTML**: Use the correct HTML elements (`<nav>`, `<main>`, `<section>`, `<aside>`, `<h1>`–`<h6>`) for structure. Don't use `<div>` or `<span>` for interactive elements — use `<button>` or `<a>`. Use the `sr-only` utility class to provide screen-reader-only text where visual context is obvious but programmatic context is missing.
21
+ - **Keyboard navigation**: Ensure all interactive elements are reachable and operable via keyboard. Use `tabindex` only when native tab order is insufficient. Never set `tabindex` > 0. Ensure custom dropdowns, modals, and popovers trap focus correctly and support Escape to close.
22
+
23
+ ## Global Styles Setup
24
+
25
+ Component styles (e.g. `ndw-card`, `ndw-alert`) are encapsulated and work automatically after importing the component.
26
+
27
+ The global stylesheet provides CSS custom properties (colors, spacing, typography variables), utility classes (`.ndw-heading-*`, `.ndw-grid`), and directive styles (`[ndwButton]`, `[ndwInput]`, `[ndwLink]`, `[ndwLabel]`, `[ndwDivider]`). Add it to your `angular.json`:
28
+
29
+ ```json
30
+ // angular.json > projects > your-app > architect > build > options > styles
31
+ "styles": [
32
+ "./node_modules/@ndwnu/design-system/styles/index.scss"
33
+ ]
34
+ ```
35
+
36
+ ---
37
+
38
+ ## CSS Custom Properties
39
+
40
+ ### Spacing
41
+
42
+ | Variable | Value |
43
+ | ------------------- | -------------- |
44
+ | `--ndw-spacing-3xs` | 0.125rem (2px) |
45
+ | `--ndw-spacing-2xs` | 0.25rem (4px) |
46
+ | `--ndw-spacing-xs` | 0.5rem (8px) |
47
+ | `--ndw-spacing-sm` | 0.75rem (12px) |
48
+ | `--ndw-spacing-md` | 1rem (16px) |
49
+ | `--ndw-spacing-lg` | 1.5rem (24px) |
50
+ | `--ndw-spacing-xl` | 2rem (32px) |
51
+ | `--ndw-spacing-2xl` | 2.5rem (40px) |
52
+ | `--ndw-spacing-3xl` | 3rem (48px) |
53
+ | `--ndw-spacing-4xl` | 5rem (80px) |
54
+ | `--ndw-spacing-5xl` | 8rem (128px) |
55
+
56
+ ### Colors
57
+
58
+ **Grey scale**: `--ndw-color-grey-50` through `--ndw-color-grey-700`, `--ndw-color-white`
59
+
60
+ **Primary** (NDW: orange, NWB: teal): `--ndw-color-primary-050` through `--ndw-color-primary-800`
61
+
62
+ - Shortcuts: `--ndw-color-primary`, `--ndw-color-primary-hover`, `--ndw-color-primary-active`
63
+
64
+ **Secondary** (NDW: blue, NWB: pink): `--ndw-color-secondary-050` through `--ndw-color-secondary-700`
65
+
66
+ - Shortcuts: `--ndw-color-secondary`, `--ndw-color-secondary-hover`, `--ndw-color-secondary-active`
67
+
68
+ **Tertiary**: `--ndw-color-tertiary-400`, `--ndw-color-tertiary-500`
69
+
70
+ **Links**: `--ndw-color-link-400`, `--ndw-color-link-500`
71
+
72
+ **Feedback**:
73
+
74
+ - Positive: `--ndw-color-positive-100` to `--ndw-color-positive-600`
75
+ - Warning: `--ndw-color-warning-100` to `--ndw-color-warning-600`
76
+ - Critical: `--ndw-color-critical-100` to `--ndw-color-critical-500`
77
+ - Info: `--ndw-color-info-100` to `--ndw-color-info-500`
78
+ - Alternative: `--ndw-color-alternative-100`, `--ndw-color-alternative-500`
79
+
80
+ **Data visualization**: `--ndw-color-data-{a..f}-{100,500}`
81
+
82
+ **Semantic**: `--ndw-color-background`, `--ndw-color-background-hover`, `--ndw-color-background-active`, `--ndw-color-background-disabled`, `--ndw-color-foreground`, `--ndw-color-text`
83
+
84
+ **Alpha**: `--ndw-alpha-black-007`, `--ndw-alpha-black-015`, `--ndw-alpha-black-040`, `--ndw-alpha-white-040`, `--ndw-alpha-primary-007`, `--ndw-alpha-primary-015`
85
+
86
+ ### Borders & Radius
87
+
88
+ | Variable | Value |
89
+ | ------------------------ | -------- |
90
+ | `--ndw-border-size-sm` | 1px |
91
+ | `--ndw-border-size-md` | 2px |
92
+ | `--ndw-border-size-lg` | 3px |
93
+ | `--ndw-border-radius-xs` | 0.125rem |
94
+ | `--ndw-border-radius-sm` | 0.25rem |
95
+ | `--ndw-border-radius-md` | 0.5rem |
96
+ | `--ndw-border-radius-lg` | 1.5rem |
97
+
98
+ ### Typography
99
+
100
+ | Variable | Value |
101
+ | --------------------------- | ------------------------- |
102
+ | `--ndw-font-family-body` | 'Nunito Sans', sans-serif |
103
+ | `--ndw-font-family-heading` | 'DM Sans', sans-serif |
104
+ | `--ndw-font-size-2xs` | 0.5625rem |
105
+ | `--ndw-font-size-xs` | 0.6875rem |
106
+ | `--ndw-font-size-sm` | 0.8125rem |
107
+ | `--ndw-font-size-md` | 1.125rem |
108
+ | `--ndw-font-size-lg` | 1.25rem |
109
+ | `--ndw-font-size-xl` | 1.5rem |
110
+ | `--ndw-font-weight-regular` | 400 |
111
+ | `--ndw-font-weight-bold` | 650 |
112
+
113
+ ### Elevation
114
+
115
+ | Variable | Use case |
116
+ | -------------------------- | ----------------------- |
117
+ | `--ndw-elevation-content` | Cards, subtle elevation |
118
+ | `--ndw-elevation-dropdown` | Dropdowns |
119
+ | `--ndw-elevation-popover` | Popovers |
120
+ | `--ndw-elevation-toast` | Toast notifications |
121
+ | `--ndw-elevation-info` | Focus rings |
122
+
123
+ ### Animation
124
+
125
+ | Variable | Value |
126
+ | --------------------------------- | ----- |
127
+ | `--ndw-animation-speed-very-fast` | 100ms |
128
+ | `--ndw-animation-speed-fast` | 200ms |
129
+ | `--ndw-animation-speed-default` | 300ms |
130
+ | `--ndw-animation-speed-slow` | 500ms |
131
+
132
+ ### Screen Breakpoints (SCSS variables)
133
+
134
+ | Variable | Value |
135
+ | ----------------- | ------ |
136
+ | `$ndw-screen-2xs` | 480px |
137
+ | `$ndw-screen-xs` | 768px |
138
+ | `$ndw-screen-sm` | 1024px |
139
+ | `$ndw-screen-md` | 1440px |
140
+
141
+ ---
142
+
143
+ ## Typography CSS Classes & SCSS Mixins
144
+
145
+ Classes for use in HTML, mixins for use in SCSS:
146
+
147
+ | Class | Mixin | Font | Size |
148
+ | ------------------------ | -------------------------------- | ------------------- | --------- |
149
+ | `.ndw-heading-xl` | `@include ndw-heading-xl` | DM Sans Bold | 2.5rem |
150
+ | `.ndw-heading-lg` | `@include ndw-heading-lg` | DM Sans Bold | 2rem |
151
+ | `.ndw-heading-md` | `@include ndw-heading-md` | DM Sans Bold | 1.5rem |
152
+ | `.ndw-heading-sm` | `@include ndw-heading-sm` | DM Sans Bold | 1.25rem |
153
+ | `.ndw-paragraph-bold-xl` | `@include ndw-paragraph-bold-xl` | Nunito Sans Bold | 1.125rem |
154
+ | `.ndw-paragraph-xl` | `@include ndw-paragraph-xl` | Nunito Sans Regular | 1.125rem |
155
+ | `.ndw-paragraph-bold-lg` | `@include ndw-paragraph-bold-lg` | Nunito Sans Bold | 1rem |
156
+ | `.ndw-paragraph-lg` | `@include ndw-paragraph-lg` | Nunito Sans Regular | 1rem |
157
+ | `.ndw-paragraph-bold-md` | `@include ndw-paragraph-bold-md` | Nunito Sans Bold | 0.8125rem |
158
+ | `.ndw-paragraph-md` | `@include ndw-paragraph-md` | Nunito Sans Regular | 0.8125rem |
159
+ | `.ndw-paragraph-bold-sm` | `@include ndw-paragraph-bold-sm` | Nunito Sans Bold | 0.6875rem |
160
+ | `.ndw-paragraph-sm` | `@include ndw-paragraph-sm` | Nunito Sans Regular | 0.6875rem |
161
+
162
+ ---
163
+
164
+ ## Layout Utilities
165
+
166
+ ### Grid (`.ndw-grid`)
167
+
168
+ Responsive CSS Grid with 12-column system. Columns per breakpoint: 2xs=2, xs=4, sm=6, md=12.
169
+
170
+ ```html
171
+ <div class="ndw-grid">
172
+ <div class="column-6 column-sm-3">Half / quarter</div>
173
+ <div class="column-6 column-sm-9">Half / three-quarter</div>
174
+ </div>
175
+ ```
176
+
177
+ Modifier: `.ndw-grid--no-padding` removes horizontal padding.
178
+
179
+ ### Container Grid (`.ndw-c-grid`)
180
+
181
+ Container-query based 12-column flex grid.
182
+
183
+ ```html
184
+ <div class="ndw-c-grid">
185
+ <div class="ndw-c-col ndw-c-col-6 ndw-c-col-sm-4">Content</div>
186
+ <div class="ndw-c-col ndw-c-col-6 ndw-c-col-sm-8">Content</div>
187
+ </div>
188
+ ```
189
+
190
+ Ordering: `.ndw-c-order-{0-5}`, `.ndw-c-order-{breakpoint}-{0-5}`
191
+
192
+ ### Screenreader Only
193
+
194
+ ```html
195
+ <span class="sr-only">Hidden visually, available to screen readers</span>
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Directive-based Styling
201
+
202
+ ### Button `[ndwButton]`
203
+
204
+ Apply to `<button>` or `<a>` elements. Provides full button styling.
205
+
206
+ ```html
207
+ <button ndwButton>Primary</button>
208
+ <button ndwButton secondary>Secondary (outlined)</button>
209
+ <button ndwButton tertiary>Tertiary (ghost)</button>
210
+ <button ndwButton secondary alternative>Alternative secondary</button>
211
+ <button ndwButton tertiary alternative>Alternative tertiary</button>
212
+ <button ndwButton extra-small>Extra small</button>
213
+ <button ndwButton large>Large</button>
214
+ <button ndwButton disabled>Disabled</button>
215
+ <button ndwButton filter>Filter button style</button>
216
+ ```
217
+
218
+ **Import**: `ButtonDirective` from `@ndwnu/design-system`
219
+
220
+ ### Input `[ndwInput]`
221
+
222
+ Apply to `<input>`, `<select>`, or `<textarea>` elements. Supports `error`, `success`, `disabled`, `readonly` attributes.
223
+
224
+ ```html
225
+ <input ndwInput type="text" placeholder="Enter text" />
226
+ <select ndwInput>
227
+ <option>Choose...</option>
228
+ </select>
229
+ <textarea ndwInput></textarea>
230
+ ```
231
+
232
+ Wrap in `.input-container` for prefix/suffix support:
233
+
234
+ ```html
235
+ <div class="input-container">
236
+ <ndw-input-icon><ndw-icon>search</ndw-icon></ndw-input-icon>
237
+ <input ndwInput type="search" />
238
+ </div>
239
+ ```
240
+
241
+ **Import**: `InputDirective` from `@ndwnu/design-system`
242
+
243
+ ### Link `[ndwLink]`
244
+
245
+ ```html
246
+ <a ndwLink href="/page">Link text <ndw-icon>arrow_forward</ndw-icon></a>
247
+ ```
248
+
249
+ ### Label `[ndwLabel]`
250
+
251
+ ```html
252
+ <label ndwLabel>Field label</label>
253
+ ```
254
+
255
+ ### Divider `[ndwDivider]`
256
+
257
+ ```html
258
+ <hr ndwDivider />
259
+ <hr ndwDivider noMargin />
260
+ ```
261
+
262
+ ---
263
+
264
+ ## Components
265
+
266
+ ### AccordionComponent
267
+
268
+ **Selector**: `ndw-accordion`
269
+ **Import**: `AccordionComponent`
270
+
271
+ | Input | Type | Default | Description |
272
+ | ---------------- | --------- | ------- | -------------------------------------------------- |
273
+ | `collapseOthers` | `boolean` | `false` | When true, expanding one collapsible closes others |
274
+
275
+ Contains `ndw-collapsible` children.
276
+
277
+ ```html
278
+ <ndw-accordion [collapseOthers]="true">
279
+ <ndw-collapsible [index]="0" title="Section 1">Content 1</ndw-collapsible>
280
+ <ndw-collapsible [index]="1" title="Section 2">Content 2</ndw-collapsible>
281
+ </ndw-accordion>
282
+ ```
283
+
284
+ ---
285
+
286
+ ### AlertComponent
287
+
288
+ **Selector**: `ndw-alert`
289
+ **Import**: `AlertComponent`
290
+
291
+ | Input | Type | Default | Description |
292
+ | ------------ | -------------------------------------------------- | ------------- | ---------------------------- |
293
+ | `type` | `'critical' \| 'info' \| 'info-grey' \| 'warning'` | `'info-grey'` | Visual style |
294
+ | `actionable` | `boolean` | `false` | Shows close button and title |
295
+ | `title` | `string` | `undefined` | Title (only when actionable) |
296
+ | `ariaLive` | `'polite' \| 'assertive' \| 'off'` | `'off'` | Screen reader announcement |
297
+
298
+ | Output | Type | Description |
299
+ | ------- | ------ | --------------------------------- |
300
+ | `close` | `void` | Emitted when close button clicked |
301
+
302
+ ```html
303
+ <ndw-alert type="warning" [actionable]="true" title="Warning" (close)="onClose()">
304
+ Alert message content
305
+ </ndw-alert>
306
+ ```
307
+
308
+ ---
309
+
310
+ ### AvatarComponent
311
+
312
+ **Selector**: `ndw-avatar`
313
+ **Import**: `AvatarComponent`
314
+
315
+ | Input | Type | Default | Description |
316
+ | ---------- | ----------------------------------------------------------------------------- | ----------- | ------------------ |
317
+ | `status` | `'approved' \| 'declined' \| 'open' \| 'none'` | `'none'` | Status indicator |
318
+ | `initials` | `boolean` | `false` | Show initials mode |
319
+ | `color` | `'default' \| 'blue' \| 'purple' \| 'red' \| 'green' \| 'yellow' \| 'orange'` | `undefined` | Background color |
320
+
321
+ ```html
322
+ <ndw-avatar status="approved" color="blue">AB</ndw-avatar>
323
+ ```
324
+
325
+ ---
326
+
327
+ ### BadgeComponent
328
+
329
+ **Selector**: `ndw-badge`
330
+ **Import**: `BadgeComponent`
331
+
332
+ | Input | Type | Default | Description |
333
+ | --------------------- | ---------------------- | ----------- | ------------------------------------------ |
334
+ | `value` | `number` | `undefined` | Numeric value |
335
+ | `ariaLabel` | `string` | `undefined` | Screen reader label |
336
+ | `displayLargeNumbers` | `boolean` | `false` | Show values above 99 (otherwise shows dot) |
337
+ | `size` | `'default' \| 'small'` | `'default'` | Badge size |
338
+ | `variant` | `'default' \| 'error'` | `'default'` | Error variant shows `!` |
339
+
340
+ ```html
341
+ <ndw-badge [value]="5" ariaLabel="5 notifications"></ndw-badge>
342
+ ```
343
+
344
+ ---
345
+
346
+ ### BannerComponent
347
+
348
+ **Selector**: `ndw-banner`
349
+ **Import**: `BannerComponent`
350
+
351
+ | Input | Type | Default | Description |
352
+ | --------------- | ---------------------------------------------------------------- | --------------- | ----------------------------- |
353
+ | `title` | `string` | **required** | Banner title |
354
+ | `message` | `string` | **required** | Banner message |
355
+ | `type` | `'critical' \| 'info' \| 'info-grey' \| 'warning' \| 'positive'` | `'info'` | Visual style |
356
+ | `readMoreLabel` | `string` | `'lees verder'` | Read more text when truncated |
357
+ | `ariaLive` | `'polite' \| 'assertive' \| 'off'` | `'off'` | Screen reader announcement |
358
+
359
+ | Output | Type | Description |
360
+ | ------- | ------ | --------------------------------- |
361
+ | `close` | `void` | Emitted when close button clicked |
362
+
363
+ ```html
364
+ <ndw-banner
365
+ title="Update"
366
+ message="System maintenance planned"
367
+ type="warning"
368
+ (close)="dismiss()"
369
+ ></ndw-banner>
370
+ ```
371
+
372
+ ---
373
+
374
+ ### BreadcrumbComponent & BreadcrumbGroupComponent
375
+
376
+ **Selectors**: `ndw-breadcrumb`, `ndw-breadcrumb-group`
377
+ **Import**: `BreadcrumbComponent`, `BreadcrumbGroupComponent`
378
+
379
+ **BreadcrumbComponent**:
380
+ | Input | Type | Default | Description |
381
+ |---|---|---|---|
382
+ | `link` | `string \| null` | `null` | Route path (last breadcrumb typically has no link) |
383
+
384
+ Content projection for the breadcrumb label text.
385
+
386
+ ```html
387
+ <ndw-breadcrumb-group>
388
+ <ndw-breadcrumb link="/">Home</ndw-breadcrumb>
389
+ <ndw-breadcrumb link="/projects">Projects</ndw-breadcrumb>
390
+ <ndw-breadcrumb>Current Page</ndw-breadcrumb>
391
+ </ndw-breadcrumb-group>
392
+ ```
393
+
394
+ ### RouterBreadcrumbsComponent
395
+
396
+ **Selector**: `ndw-router-breadcrumbs`
397
+ **Import**: `RouterBreadcrumbsComponent`
398
+
399
+ Auto-generates breadcrumbs from Angular router `data` properties.
400
+
401
+ | Input | Type | Default | Description |
402
+ | -------------- | -------- | -------------- | ------------------------------------------------ |
403
+ | `routeDataKey` | `string` | `'breadcrumb'` | The key in route data to use as breadcrumb title |
404
+
405
+ ```typescript
406
+ // In your route config:
407
+ { path: 'projects', component: ProjectsComponent, data: { breadcrumb: 'Projects' } }
408
+ ```
409
+
410
+ ```html
411
+ <ndw-router-breadcrumbs></ndw-router-breadcrumbs>
412
+ ```
413
+
414
+ ---
415
+
416
+ ### CardComponent
417
+
418
+ **Selector**: `ndw-card`
419
+ **Import**: `CardComponent`, `CardHeaderComponent`, `CardContentComponent`, `CardFooterComponent`
420
+
421
+ | Input/Model | Type | Default | Description |
422
+ | --------------------- | --------- | ------- | --------------------------------- |
423
+ | `isCollapsed` (model) | `boolean` | `false` | Collapsed state (two-way binding) |
424
+
425
+ **CardHeaderComponent** (`ndw-card-header`):
426
+ | Input | Type | Default | Description |
427
+ |---|---|---|---|
428
+ | `isCollapsible` | `boolean` | `false` | Show expand/collapse toggle in header |
429
+
430
+ ```html
431
+ <ndw-card>
432
+ <ndw-card-header [isCollapsible]="true">Card Title</ndw-card-header>
433
+ <ndw-card-content>Card body content</ndw-card-content>
434
+ <ndw-card-footer>Footer actions</ndw-card-footer>
435
+ </ndw-card>
436
+ ```
437
+
438
+ ---
439
+
440
+ ### CollapsibleComponent
441
+
442
+ **Selector**: `ndw-collapsible`
443
+ **Import**: `CollapsibleComponent`
444
+
445
+ | Input | Type | Default | Description |
446
+ | --------------------- | --------- | ------------ | --------------------- |
447
+ | `index` | `number` | **required** | Position index |
448
+ | `title` | `string` | **required** | Header title |
449
+ | `subtitle` | `string` | `undefined` | Subtitle text |
450
+ | `value` | `number` | `undefined` | Numeric value display |
451
+ | `checkable` | `boolean` | `false` | Show checkbox |
452
+ | `displayLargeNumbers` | `boolean` | `false` | Show large numbers |
453
+
454
+ | Model | Type | Default |
455
+ | ---------- | --------- | ------- |
456
+ | `expanded` | `boolean` | `false` |
457
+
458
+ Supports `<custom-header-content>` content projection slot for custom header content next to the title.
459
+
460
+ ```html
461
+ <ndw-collapsible [index]="0" title="Section" [(expanded)]="isExpanded">
462
+ <custom-header-content><ndw-pill>New</ndw-pill></custom-header-content>
463
+ Expandable content
464
+ </ndw-collapsible>
465
+ ```
466
+
467
+ ---
468
+
469
+ ### DashboardCardComponent
470
+
471
+ **Selector**: `ndw-dashboard-card`
472
+ **Import**: `DashboardCardComponent`
473
+
474
+ | Input | Type | Default | Description |
475
+ | ------------ | -------- | ------------ | ------------------------------------- |
476
+ | `title` | `string` | **required** | Card title |
477
+ | `imgSource` | `string` | **required** | Icon URL |
478
+ | `link` | `string` | **required** | URL to open on click |
479
+ | `linkTarget` | `string` | `'_blank'` | Link target (`_blank`, `_self`, etc.) |
480
+
481
+ ```html
482
+ <ndw-dashboard-card
483
+ title="Traffic Data"
484
+ imgSource="/assets/icon.svg"
485
+ link="https://example.com"
486
+ ></ndw-dashboard-card>
487
+ ```
488
+
489
+ ---
490
+
491
+ ### DropdownComponent
492
+
493
+ **Selector**: `ndw-dropdown`
494
+ **Import**: `DropdownComponent`
495
+
496
+ | Input | Type | Default | Description |
497
+ | -------------- | --------- | ------------ | ------------------------- |
498
+ | `buttonText` | `string` | **required** | Button label |
499
+ | `disabled` | `boolean` | `false` | Disabled state |
500
+ | `chevron` | `boolean` | `true` | Show chevron icon |
501
+ | `prefixIcon` | `string` | `''` | Material icon before text |
502
+ | `selectAmount` | `number` | `0` | Selected count badge |
503
+
504
+ | Output | Type | Description |
505
+ | -------------- | --------- | --------------------------- |
506
+ | `tagClicked` | `void` | Tag clear button clicked |
507
+ | `isOpenChange` | `boolean` | Dropdown open state changed |
508
+
509
+ Content is projected into the dropdown popover.
510
+
511
+ ```html
512
+ <ndw-dropdown buttonText="Filter" [selectAmount]="3" (tagClicked)="clearFilter()">
513
+ <div>Dropdown content here</div>
514
+ </ndw-dropdown>
515
+ ```
516
+
517
+ ---
518
+
519
+ ### EditBarComponent
520
+
521
+ **Selector**: `ndw-edit-bar`
522
+ **Import**: `EditBarComponent`
523
+
524
+ | Input | Type | Default | Description |
525
+ | -------------------- | ----------------------------------------------- | ---------- | ---------------- |
526
+ | `grid` | `boolean` | `false` | Use grid layout |
527
+ | `position` | `'fixed' \| 'sticky' \| 'static' \| 'absolute'` | `'fixed'` | Position type |
528
+ | `positionFixedWidth` | `'full' \| 'layout'` | `'layout'` | Width when fixed |
529
+
530
+ ```html
531
+ <ndw-edit-bar position="fixed">
532
+ <button ndwButton>Save</button>
533
+ <button ndwButton secondary>Cancel</button>
534
+ </ndw-edit-bar>
535
+ ```
536
+
537
+ ---
538
+
539
+ ### FavoriteComponent
540
+
541
+ **Selector**: `ndw-favorite`
542
+ **Import**: `FavoriteComponent`
543
+
544
+ | Model | Type | Default |
545
+ | --------- | --------- | ------- |
546
+ | `checked` | `boolean` | `false` |
547
+
548
+ ```html
549
+ <ndw-favorite [(checked)]="isFavorite"></ndw-favorite>
550
+ ```
551
+
552
+ ---
553
+
554
+ ### FormFieldComponent
555
+
556
+ **Selector**: `ndw-form-field`
557
+ **Import**: `FormFieldComponent`
558
+
559
+ Wraps any form element (inputs, selects, textareas, checkboxes, radio groups, option groups, datepickers, month inputs) with a label, error, success, info messages, and tooltip. Automatically uses `<fieldset>`/`<legend>` for non-input elements (like checkbox groups) and `<label>`/`for` for regular inputs.
560
+
561
+ | Input | Type | Default | Description |
562
+ | ----------------- | --------- | ------------ | --------------------- |
563
+ | `label` | `string` | **required** | Field label |
564
+ | `hideLabel` | `boolean` | `false` | Visually hide label |
565
+ | `disabled` | `boolean` | `false` | Disable the field |
566
+ | `error` | `string` | `undefined` | Error message |
567
+ | `success` | `string` | `undefined` | Success message |
568
+ | `info` | `string` | `undefined` | Info/help message |
569
+ | `tooltip` | `string` | `''` | Tooltip text |
570
+ | `suffixAriaLabel` | `string` | `''` | Aria label for suffix |
571
+
572
+ | Output | Type | Description |
573
+ | -------------------- | ------ | --------------------------- |
574
+ | `clearButtonClicked` | `void` | Search clear button clicked |
575
+
576
+ ```html
577
+ <!-- With input -->
578
+ <ndw-form-field label="Email" [error]="emailError" tooltip="Enter your work email">
579
+ <input ndwInput type="email" [formControl]="emailControl" />
580
+ </ndw-form-field>
581
+
582
+ <!-- With select -->
583
+ <ndw-form-field label="Country">
584
+ <select ndwInput [formControl]="countryControl">
585
+ <option disabled selected>Choose...</option>
586
+ <option value="nl">Netherlands</option>
587
+ </select>
588
+ </ndw-form-field>
589
+
590
+ <!-- With checkbox group (uses fieldset/legend automatically) -->
591
+ <ndw-form-field label="Options" [error]="optionsError">
592
+ <ndw-checkbox-group>
593
+ <ndw-checkbox>Option A</ndw-checkbox>
594
+ <ndw-checkbox>Option B</ndw-checkbox>
595
+ </ndw-checkbox-group>
596
+ </ndw-form-field>
597
+
598
+ <!-- With datepicker -->
599
+ <ndw-form-field label="Start date">
600
+ <ndw-datepicker [formControl]="dateControl"></ndw-datepicker>
601
+ </ndw-form-field>
602
+ ```
603
+
604
+ ---
605
+
606
+ ### CheckboxComponent
607
+
608
+ **Selector**: `ndw-checkbox`
609
+ **Import**: `CheckboxComponent`
610
+ **CVA**: Yes (use with `formControl` or `ngModel`)
611
+
612
+ | Input/Model | Type | Default | Description |
613
+ | ------------------ | --------- | ------- | ----------------------- |
614
+ | `checked` (model) | `boolean` | `false` | Checked state |
615
+ | `disabled` (model) | `boolean` | `false` | Disabled state |
616
+ | `error` | `boolean` | `false` | Error state |
617
+ | `indeterminate` | `boolean` | `false` | Indeterminate state |
618
+ | `required` | `boolean` | `false` | Required |
619
+ | `switch` | `boolean` | `false` | Render as toggle switch |
620
+ | `success` | `boolean` | `false` | Success state |
621
+
622
+ Content projection for the label text.
623
+
624
+ ```html
625
+ <ndw-checkbox [formControl]="myControl">Label text</ndw-checkbox>
626
+ <ndw-checkbox [switch]="true" [(ngModel)]="toggleValue">Toggle</ndw-checkbox>
627
+ ```
628
+
629
+ ### CheckboxGroupComponent
630
+
631
+ **Selector**: `ndw-checkbox-group`
632
+ **Import**: `CheckboxGroupComponent`
633
+
634
+ Container for grouping checkboxes.
635
+
636
+ ```html
637
+ <ndw-checkbox-group>
638
+ <ndw-checkbox>Option A</ndw-checkbox>
639
+ <ndw-checkbox>Option B</ndw-checkbox>
640
+ </ndw-checkbox-group>
641
+ ```
642
+
643
+ ---
644
+
645
+ ### RadioButtonComponent & RadioGroupComponent
646
+
647
+ **Selectors**: `ndw-radio-button`, `ndw-radio-group`
648
+ **Import**: `RadioButtonComponent`, `RadioGroupComponent`
649
+ **CVA**: Yes
650
+
651
+ RadioButton inputs:
652
+ | Input/Model | Type | Default | Description |
653
+ |---|---|---|---|
654
+ | `checked` (model) | `boolean` | `false` | Checked state |
655
+ | `disabled` (model) | `boolean` | `false` | Disabled state |
656
+ | `value` (model) | `unknown` | `undefined` | Option value emitted on selection |
657
+ | `error` | `boolean` | `false` | Error state styling |
658
+ | `success` | `boolean` | `false` | Success state styling |
659
+ | `required` | `boolean` | `false` | Mark as required |
660
+
661
+ ```html
662
+ <ndw-radio-group [formControl]="selection">
663
+ <ndw-radio-button [value]="'a'">Option A</ndw-radio-button>
664
+ <ndw-radio-button [value]="'b'">Option B</ndw-radio-button>
665
+ </ndw-radio-group>
666
+ ```
667
+
668
+ ---
669
+
670
+ ### DatepickerComponent
671
+
672
+ **Selector**: `ndw-datepicker`
673
+ **Import**: `DatepickerComponent`
674
+ **CVA**: Yes
675
+
676
+ | Input | Type | Default | Description |
677
+ | ------------------------- | ----------------------------------- | ----------------------- | ------------------- |
678
+ | `mode` | `'single' \| 'multiple' \| 'month'` | `'single'` | Selection mode |
679
+ | `required` | `boolean` | `false` | Required field |
680
+ | `placeholder` | `string` | `'Selecteer een datum'` | Placeholder text |
681
+ | `openCalendarLabel` | `string` | `'Open kalender'` | Aria label |
682
+ | `minDate` | `Date \| null` | `null` | Minimum date |
683
+ | `maxDate` | `Date \| null` | `null` | Maximum date |
684
+ | `dateEnabledFilter` | `DateFilterFn<Date \| null>` | `() => true` | Custom date filter |
685
+ | `dateWithIndicatorFilter` | `DateFilterFn<Date \| null>` | `() => false` | Show indicator dots |
686
+ | `showWeekNumbers` | `boolean` | `false` | Show week numbers |
687
+
688
+ | Model | Type | Default | Description |
689
+ | ---------- | --------- | ------- | -------------- |
690
+ | `disabled` | `boolean` | `false` | Disabled state |
691
+
692
+ ```html
693
+ <ndw-form-field label="Date">
694
+ <ndw-datepicker [formControl]="dateControl" [minDate]="minDate"></ndw-datepicker>
695
+ </ndw-form-field>
696
+ ```
697
+
698
+ ---
699
+
700
+ ### MonthInputComponent
701
+
702
+ **Selector**: `ndw-month-input`
703
+ **Import**: `MonthInputComponent`
704
+ **CVA**: Yes
705
+
706
+ | Input | Type | Default | Description |
707
+ | ---------------------- | ---------- | ------------------------- | ---------------------------------- |
708
+ | `required` | `boolean` | `false` | Mark as required |
709
+ | `placeholder` | `string` | `'JJJJ-MM'` | Placeholder text |
710
+ | `showMonthPickerLabel` | `string` | `'Selecteer maand'` | Aria label for month picker button |
711
+ | `yearPlaceholder` | `string` | `'Jaar'` | Placeholder for year input |
712
+ | `minDate` | `Date` | `2020-01-01` | Earliest selectable month |
713
+ | `maxDate` | `Date` | `2099-12-31` | Latest selectable month |
714
+ | `monthLabels` | `string[]` | Dutch month abbreviations | Labels for month buttons |
715
+ | `addValidators` | `boolean` | `true` | Auto-add min/max validators |
716
+ | `readonly` | `boolean` | `false` | Readonly state |
717
+
718
+ ```html
719
+ <ndw-form-field label="Month">
720
+ <ndw-month-input [formControl]="monthControl"></ndw-month-input>
721
+ </ndw-form-field>
722
+ ```
723
+
724
+ ---
725
+
726
+ ### FileUploadComponent
727
+
728
+ **Selector**: `ndw-file-upload`
729
+ **Import**: `FileUploadComponent`
730
+ **CVA**: Yes
731
+
732
+ | Input | Type | Default | Description |
733
+ | ---------------------- | ------------------------ | -------------- | ------------------------ |
734
+ | `allowedFileTypeRegex` | `RegExp` | `undefined` | Allowed MIME types regex |
735
+ | `maxFileSizeInBytes` | `number` | `1000000` | Max file size (1MB) |
736
+ | `fileUploadText` | `FileUploadText` | Dutch defaults | Drag/drop text |
737
+ | `mimeTypeMap` | `Record<string, string>` | `{}` | Extra MIME type mappings |
738
+ | `readonly` | `boolean` | `false` | Readonly state |
739
+ | `uploadDate` | `string` | `''` | Display upload date |
740
+
741
+ | Model | Type | Description |
742
+ | ------------------- | -------------- | ----------------------- |
743
+ | `disabled` | `boolean` | Disabled state |
744
+ | `selectedFile` | `File \| null` | Currently selected file |
745
+ | `error` | `boolean` | General error state |
746
+ | `incorrectFileSize` | `boolean` | File exceeds max size |
747
+ | `incorrectFileType` | `boolean` | File type not allowed |
748
+
749
+ ```html
750
+ <ndw-file-upload
751
+ [formControl]="fileControl"
752
+ [allowedFileTypeRegex]="pdfRegex"
753
+ [maxFileSizeInBytes]="5000000"
754
+ ></ndw-file-upload>
755
+ ```
756
+
757
+ ---
758
+
759
+ ### MarkdownEditorComponent
760
+
761
+ **Selector**: `ndw-markdown-editor`
762
+ **Import**: `MarkdownEditorComponent`
763
+ **CVA**: Yes
764
+
765
+ | Input | Type | Default | Description |
766
+ | ---------- | ---------- | ------------------------------------------------------------------------ | --------------- |
767
+ | `toolbar` | `string[]` | `['bold','header','italic','unordered-list','ordered-list','task-list']` | Toolbar buttons |
768
+ | `error` | `boolean` | `false` | Error state |
769
+ | `readonly` | `boolean` | `false` | Readonly |
770
+ | `success` | `boolean` | `false` | Success state |
771
+ | `required` | `boolean` | `false` | Required |
772
+
773
+ | Model | Type | Description |
774
+ | ---------- | --------- | ---------------- |
775
+ | `value` | `string` | Markdown content |
776
+ | `disabled` | `boolean` | Disabled state |
777
+
778
+ ```html
779
+ <ndw-markdown-editor
780
+ [formControl]="mdControl"
781
+ [toolbar]="['bold','italic','unordered-list']"
782
+ ></ndw-markdown-editor>
783
+ ```
784
+
785
+ ---
786
+
787
+ ### OptionGroupComponent
788
+
789
+ **Selector**: `ndw-option-group`
790
+ **Import**: `OptionGroupComponent`
791
+ **CVA**: Yes
792
+
793
+ | Input | Type | Default | Description |
794
+ | -------------- | ------------------------ | ------------ | -------------- |
795
+ | `mode` | `'single' \| 'multiple'` | `'multiple'` | Selection mode |
796
+ | `error` | `boolean` | `false` | Error state |
797
+ | `success` | `boolean` | `false` | Success state |
798
+ | `readonly` | `boolean` | `false` | Readonly |
799
+ | `required` | `boolean` | `false` | Required |
800
+ | `fluid` | `boolean` | `false` | Fluid width |
801
+ | `fluidColumns` | `number \| null` | `null` | Grid columns |
802
+
803
+ | Model | Type | Description |
804
+ | --------------- | ----------- | -------------------------------- |
805
+ | `checkedValues` | `unknown[]` | Currently selected option values |
806
+ | `disabled` | `boolean` | Disabled state |
807
+
808
+ Uses `ndw-option` children:
809
+
810
+ **OptionComponent** (`ndw-option`):
811
+ | Input | Type | Default | Description |
812
+ |---|---|---|---|
813
+ | `value` | `unknown` | **required** | Option value |
814
+ | `label` | `string` | **required** | Display label |
815
+ | `description` | `string` | `undefined` | Secondary description text |
816
+ | `required` | `boolean` | `false` | Mark as required |
817
+ | `disabled` | `boolean` | `false` | Disabled state |
818
+
819
+ ```html
820
+ <ndw-form-field label="Priority">
821
+ <ndw-option-group [formControl]="priorityControl" mode="single">
822
+ <ndw-option [value]="'low'" label="Low"></ndw-option>
823
+ <ndw-option [value]="'medium'" label="Medium"></ndw-option>
824
+ <ndw-option [value]="'high'" label="High" description="Urgent items only"></ndw-option>
825
+ </ndw-option-group>
826
+ </ndw-form-field>
827
+ ```
828
+
829
+ ---
830
+
831
+ ### AutosuggestDirective
832
+
833
+ **Selector**: `input[ndwAutosuggest]`, `textarea[ndwAutosuggest]`
834
+ **Import**: `AutosuggestDirective`, `AutosuggestPanelComponent`, `AutosuggestOptionComponent`, `AutosuggestAddOptionComponent`
835
+ **CVA**: Yes
836
+
837
+ **AutosuggestPanelComponent** (`ndw-autosuggest`):
838
+ | Input | Type | Default | Description |
839
+ |---|---|---|---|
840
+ | `options` | `AutosuggestOption[]` | `[]` | List of options to search/filter |
841
+ | `customSearch` | `AutosuggestSearchFn \| null` | `null` | Custom search function `(searchTerm, option) => boolean` |
842
+ | `enableAddOption` | `boolean` | `false` | Allow adding new options |
843
+ | `maxResults` | `number` | `100` | Max results shown |
844
+ | `noResultText` | `string` | `'Geen resultaten gevonden...'` | No results text |
845
+ | `hiddenResultsText` | `string` | Dutch default | Text when results are truncated |
846
+
847
+ | Output | Type | Description |
848
+ | ---------------------- | --------------------------- | ------------------------------------------------- |
849
+ | `optionSelected` | `AutosuggestOption \| null` | Option selected by user |
850
+ | `addOption` | `string` | New option added (when `enableAddOption` is true) |
851
+ | `opened` | `void` | Panel opened |
852
+ | `closed` | `void` | Panel closed |
853
+ | `results` | `AutosuggestOption[]` | Filtered results |
854
+ | `numberOfResultsShown` | `number` | Number of hidden results |
855
+
856
+ **AutosuggestOptionComponent** (`ndw-autosuggest-option`):
857
+ | Input | Type | Default | Description |
858
+ |---|---|---|---|
859
+ | `value` | `AutosuggestOption` | **required** | Option data object |
860
+ | `label` | `string` | **required** | Display label (used for filtering and bold matching) |
861
+ | `searchTerm` | `string` | `''` | Current search term (for bold text matching) |
862
+ | `icon` | `string` | `undefined` | Material icon prefix |
863
+ | `image` | `Image` | `undefined` | Image to display alongside option |
864
+
865
+ **AutosuggestOption interface**: `{ label: string; value: string \| number \| boolean; icon?: string; image?: Image }`
866
+
867
+ ```html
868
+ <ndw-form-field label="Search">
869
+ <input ndwInput [ndwAutosuggest]="panel" [formControl]="searchControl" />
870
+ </ndw-form-field>
871
+ <ndw-autosuggest #panel [options]="options" (optionSelected)="onSelect($event)">
872
+ @for (option of panel.slicedOptions(); track option.value) {
873
+ <ndw-autosuggest-option [value]="option" [label]="option.label" [searchTerm]="panel.searchTerm()">
874
+ </ndw-autosuggest-option>
875
+ }
876
+ </ndw-autosuggest>
877
+ ```
878
+
879
+ ---
880
+
881
+ ### InputIconComponent
882
+
883
+ **Selector**: `ndw-input-icon`
884
+ **Import**: `InputIconComponent`
885
+
886
+ Used inside `.input-container` to add prefix/suffix icons to inputs.
887
+
888
+ ```html
889
+ <div class="input-container">
890
+ <ndw-input-icon><ndw-icon>search</ndw-icon></ndw-input-icon>
891
+ <input ndwInput type="text" />
892
+ </div>
893
+ ```
894
+
895
+ ---
896
+
897
+ ### IconComponent
898
+
899
+ **Selector**: `ndw-icon`
900
+ **Import**: `IconComponent`
901
+
902
+ Uses Material Symbols Rounded. Content is the icon name.
903
+
904
+ | Input | Type | Default | Description |
905
+ | -------- | -------------- | ------- | ------------------- |
906
+ | `filled` | `boolean` | `false` | Filled icon variant |
907
+ | `size` | `'sm' \| 'md'` | `'md'` | Icon size |
908
+
909
+ ```html
910
+ <ndw-icon>home</ndw-icon> <ndw-icon [filled]="true" size="sm">favorite</ndw-icon>
911
+ ```
912
+
913
+ ---
914
+
915
+ ### HoverableListItemComponent
916
+
917
+ **Selector**: `ndw-hoverable-list-item`
918
+ **Import**: `HoverableListItemComponent`
919
+
920
+ | Input | Type | Default | Description |
921
+ | ------------- | --------------------------- | ----------- | ----------------------------- |
922
+ | `title` | `string` | `undefined` | Item title |
923
+ | `subtitle` | `string` | `undefined` | Secondary text |
924
+ | `prefixIcon` | `string` | `undefined` | Material icon before title |
925
+ | `disabled` | `boolean` | `false` | Disabled state |
926
+ | `path` | `string[]` | `undefined` | Router link path |
927
+ | `queryParams` | `Params` | `undefined` | Router query params |
928
+ | `actions` | `HoverableListItemAction[]` | `undefined` | Action buttons shown on hover |
929
+
930
+ | Output | Type | Description |
931
+ | ---------------- | ------------------------- | ---------------------------------------- |
932
+ | `selected` | `void` | Emitted when item is clicked |
933
+ | `actionSelected` | `HoverableListItemAction` | Emitted when an action button is clicked |
934
+
935
+ ---
936
+
937
+ ### KeyValueListComponent
938
+
939
+ **Selector**: `ndw-key-value-list`
940
+ **Import**: `KeyValueListComponent`
941
+
942
+ | Input | Type | Default | Description |
943
+ | ----------------- | ---------------------- | ------------ | ---------------------------------------- |
944
+ | `data` | `KeyValueRow[]` | **required** | Array of `{key, value}` pairs to display |
945
+ | `hasZebraStripes` | `boolean` | `false` | Alternating row backgrounds |
946
+ | `fontSize` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Text size |
947
+ | `fluid` | `boolean` | `false` | Full width layout |
948
+
949
+ ```html
950
+ <ndw-key-value-list
951
+ [data]="[{key: 'Name', value: 'NDW'}, {key: 'Type', value: 'Organization'}]"
952
+ ></ndw-key-value-list>
953
+ ```
954
+
955
+ ---
956
+
957
+ ### LanguageSwitcherComponent
958
+
959
+ **Selector**: `ndw-language-switcher`
960
+ **Import**: `LanguageSwitcherComponent`
961
+
962
+ | Input | Type | Default | Description |
963
+ | ------------- | ----------------------------------------- | -------------- | -------------------------------- |
964
+ | `languages` | `Language[]` | `['NL', 'EN']` | Available language options |
965
+ | `displayMode` | `'flag-only' \| 'abbreviation' \| 'full'` | `'flag-only'` | How to render the language label |
966
+
967
+ | Model | Type | Default | Description |
968
+ | ---------- | ---------- | ------- | --------------------------- |
969
+ | `selected` | `Language` | `'NL'` | Currently selected language |
970
+
971
+ ```html
972
+ <ndw-language-switcher
973
+ [(selected)]="currentLang"
974
+ displayMode="abbreviation"
975
+ ></ndw-language-switcher>
976
+ ```
977
+
978
+ ---
979
+
980
+ ### LayoutComponent
981
+
982
+ **Selector**: `ndw-layout`
983
+ **Import**: `LayoutComponent`
984
+
985
+ Main application layout with navigation sidebar.
986
+
987
+ | Input | Type | Default | Description |
988
+ | ----------------- | -------------------------------------- | ------------ | ----------------------- |
989
+ | `applicationName` | `string` | **required** | App name in sidebar |
990
+ | `topMenuItems` | `MenuItem[]` | **required** | Top navigation items |
991
+ | `bottomMenuItems` | `MenuItem[]` | `undefined` | Bottom navigation items |
992
+ | `environment` | `'Local' \| 'Staging' \| 'Acceptance'` | `undefined` | Environment badge |
993
+ | `isCollapsible` | `boolean` | `true` | Sidebar collapsible |
994
+ | `isExpanded` | `boolean` | `true` | Sidebar expanded |
995
+ | `menuFooterTexts` | `string[]` | `undefined` | Footer text lines |
996
+ | `version` | `string` | `undefined` | Version display |
997
+
998
+ **MenuItem interface**:
999
+
1000
+ ```typescript
1001
+ interface MenuItem {
1002
+ label: string;
1003
+ icon: string; // Material icon name
1004
+ active?: boolean;
1005
+ path?: string[];
1006
+ queryParams?: Params;
1007
+ notifications?: number;
1008
+ callback?: () => void;
1009
+ children?: SubMenuItem[];
1010
+ }
1011
+ ```
1012
+
1013
+ ```html
1014
+ <ndw-layout applicationName="My App" [topMenuItems]="menuItems" environment="Acceptance">
1015
+ <ndw-layout-banners>
1016
+ <ndw-banner title="Notice" message="System update"></ndw-banner>
1017
+ </ndw-layout-banners>
1018
+ <router-outlet></router-outlet>
1019
+ </ndw-layout>
1020
+ ```
1021
+
1022
+ ---
1023
+
1024
+ ### LayoutBannersComponent
1025
+
1026
+ **Selector**: `ndw-layout-banners`
1027
+ **Import**: `LayoutBannersComponent`
1028
+
1029
+ Container for banners inside `ndw-layout`. No inputs - content projection only.
1030
+
1031
+ ---
1032
+
1033
+ ### ListComponent
1034
+
1035
+ **Selector**: `ndw-list`
1036
+ **Import**: `ListComponent`
1037
+
1038
+ | Input | Type | Default | Description |
1039
+ | ---------- | --------- | ------- | -------------------------------- |
1040
+ | `elevated` | `boolean` | `false` | Add shadow elevation to the list |
1041
+
1042
+ ### ListItemComponent
1043
+
1044
+ **Selector**: `ndw-list-item`
1045
+ **Import**: `ListItemComponent`
1046
+
1047
+ | Input | Type | Default | Description |
1048
+ | --------------------- | ----------------------- | ------------ | ---------------------------------------- |
1049
+ | `title` | `string` | `undefined` | Item title text |
1050
+ | `subtitle` | `string` | `undefined` | Secondary text below title |
1051
+ | `prefixIcon` | `string` | `undefined` | Material icon before title |
1052
+ | `buttonIcon` | `string` | `undefined` | Icon for the action button |
1053
+ | `buttonLabel` | `string` | `undefined` | Label for the action button |
1054
+ | `badgeValue` | `number` | `undefined` | Numeric badge value |
1055
+ | `pillLabel` | `string` | `undefined` | Pill label text |
1056
+ | `pillColor` | `PillColor` | `'green'` | Pill color variant |
1057
+ | `checkable` | `boolean` | `false` | Show checkbox/radio |
1058
+ | `checkType` | `'checkbox' \| 'radio'` | `'checkbox'` | Check input type |
1059
+ | `collapsible` | `boolean` | `false` | Enable expand/collapse of nested content |
1060
+ | `disabled` | `boolean` | `false` | Disabled state |
1061
+ | `indented` | `boolean` | `false` | Indent item (for nested lists) |
1062
+ | `showButton` | `boolean` | `false` | Show action button |
1063
+ | `displayLargeNumbers` | `boolean` | `false` | Show badge values > 99 |
1064
+
1065
+ | Model | Type | Default | Description |
1066
+ | ---------- | --------- | ------- | --------------------------------- |
1067
+ | `active` | `boolean` | `false` | Active/highlighted state |
1068
+ | `checked` | `boolean` | `false` | Checked state (when checkable) |
1069
+ | `expanded` | `boolean` | `false` | Expanded state (when collapsible) |
1070
+
1071
+ | Output | Type | Description |
1072
+ | --------------- | ------ | ------------------------------------- |
1073
+ | `buttonClicked` | `void` | Emitted when action button is clicked |
1074
+
1075
+ ```html
1076
+ <ndw-list [elevated]="true">
1077
+ <ndw-list-item
1078
+ title="Item 1"
1079
+ subtitle="Description"
1080
+ prefixIcon="folder"
1081
+ [checkable]="true"
1082
+ [(checked)]="item1Checked"
1083
+ ></ndw-list-item>
1084
+ <ndw-list-item title="Item 2" [collapsible]="true"> Nested content </ndw-list-item>
1085
+ </ndw-list>
1086
+ ```
1087
+
1088
+ ---
1089
+
1090
+ ### LoaderComponent
1091
+
1092
+ **Selector**: `ndw-loader`
1093
+ **Import**: `LoaderComponent`
1094
+
1095
+ No inputs. Displays a spinning loader animation.
1096
+
1097
+ ```html
1098
+ <ndw-loader></ndw-loader>
1099
+ ```
1100
+
1101
+ ---
1102
+
1103
+ ### MapButtonComponent
1104
+
1105
+ **Selector**: `ndw-map-button`
1106
+ **Import**: `MapButtonComponent`
1107
+
1108
+ | Input | Type | Default | Description |
1109
+ | ----------- | --------------- | ----------- | -------------------- |
1110
+ | `icon` | `MapButtonIcon` | `undefined` | Button icon |
1111
+ | `active` | `boolean` | `false` | Active/pressed state |
1112
+ | `ariaLabel` | `string` | `undefined` | Custom aria label |
1113
+ | `disabled` | `boolean` | `false` | Disabled state |
1114
+
1115
+ | Output | Type | Description |
1116
+ | --------- | ------ | ------------------------------ |
1117
+ | `clicked` | `void` | Emitted when button is clicked |
1118
+
1119
+ **MapButtonIcon values**: `'direction'`, `'gps'`, `'high-res'`, `'layers'`, `'polygon'`, `'search'`, `'zoom-in'`, `'zoom-out'`, `'mail'`, `'add-traffic-sign'`, `'add-zone'`, `'download'`, `'hectometer'`, `'low-res'`, `'zoom-to-content'`
1120
+
1121
+ ```html
1122
+ <ndw-map-button icon="zoom-in" (clicked)="zoomIn()"></ndw-map-button>
1123
+ <ndw-map-button icon="layers" [active]="showLayers" (clicked)="toggleLayers()"></ndw-map-button>
1124
+ ```
1125
+
1126
+ ---
1127
+
1128
+ ### MapDisplayComponent
1129
+
1130
+ **Selector**: `ndw-map-display`
1131
+ **Import**: `MapDisplayComponent`
1132
+
1133
+ | Input | Type | Default | Description |
1134
+ | ------------------- | ----------------------- | ----------------- | ------------------------------- |
1135
+ | `title` | `string` | `'Kaartweergave'` | Panel title |
1136
+ | `backgroundsTitle` | `string` | `'Achtergrond'` | Background section title |
1137
+ | `layersTitle` | `string` | `'Gegevens'` | Layers section title |
1138
+ | `enableClearLayers` | `boolean` | `true` | Show clear-all layers button |
1139
+ | `layerOptionType` | `'radio' \| 'checkbox'` | `'checkbox'` | Single or multi layer selection |
1140
+ | `backgroundOptions` | `MapBackgroundOption[]` | `[]` | Available map backgrounds |
1141
+ | `layerOptions` | `MapDisplayOption[]` | `[]` | Available data layers |
1142
+
1143
+ | Model | Type | Description |
1144
+ | ------ | --------- | ----------------------- |
1145
+ | `open` | `boolean` | Panel open/closed state |
1146
+
1147
+ | Output | Type | Description |
1148
+ | ------------------ | --------------------- | ----------------------------------------- |
1149
+ | `backgroundChange` | `MapBackgroundOption` | Emitted when background selection changes |
1150
+ | `layerChange` | `MapDisplayOption` | Emitted when a layer is toggled |
1151
+ | `clearAllLayers` | `void` | Emitted when clear-all is clicked |
1152
+
1153
+ **MapDisplayOption interface**:
1154
+
1155
+ ```typescript
1156
+ interface MapDisplayOption {
1157
+ id: string;
1158
+ name: string;
1159
+ active?: boolean;
1160
+ imageLink?: string;
1161
+ icon?: string;
1162
+ description?: string;
1163
+ }
1164
+ type MapBackgroundOption<T = string> = MapDisplayOption & { style: T };
1165
+ ```
1166
+
1167
+ ---
1168
+
1169
+ ### MapLegendComponent
1170
+
1171
+ **Selector**: `ndw-map-legend`
1172
+ **Import**: `MapLegendComponent`
1173
+
1174
+ | Input | Type | Default | Description |
1175
+ | ---------- | ------------------------ | ----------------- | ---------------------------------- |
1176
+ | `groups` | `MapLegendOptionGroup[]` | **required** | Legend option groups |
1177
+ | `icon` | `string` | `'legend_toggle'` | Trigger button icon |
1178
+ | `title` | `string` | `'Legenda'` | Panel title |
1179
+ | `viewMode` | `'dropdown' \| 'fixed'` | `'dropdown'` | Dropdown popover or always-visible |
1180
+
1181
+ | Model | Type | Description |
1182
+ | ------ | --------- | ----------------------- |
1183
+ | `open` | `boolean` | Panel open/closed state |
1184
+
1185
+ **MapLegendOptionGroup interface**:
1186
+
1187
+ ```typescript
1188
+ interface MapLegendOptionGroup {
1189
+ label?: string;
1190
+ options: MapLegendOption[];
1191
+ }
1192
+ // MapLegendOption types: 'image', 'line', 'circle', 'polygon', 'area', 'icon'
1193
+ ```
1194
+
1195
+ ---
1196
+
1197
+ ### ModalComponent & ModalService
1198
+
1199
+ **Selector**: `ndw-modal`
1200
+ **Import**: `ModalComponent`, `ModalHeaderComponent`, `ModalService`
1201
+
1202
+ | Input | Type | Default | Description |
1203
+ | ------ | -------------- | ------- | ---------------------------- |
1204
+ | `size` | `'sm' \| 'md'` | `'sm'` | Modal width (500px or 720px) |
1205
+
1206
+ **ModalService** (injectable):
1207
+
1208
+ - `open(componentOrTemplate, config?)` - Opens a modal, returns `ModalRef`
1209
+ - `close()` - Closes the current modal
1210
+ - `backdropClick$` - Observable for backdrop clicks
1211
+ - `closed$` - Observable for modal close
1212
+
1213
+ ```typescript
1214
+ // Opening a modal
1215
+ const ref = this.modalService.open(MyDialogComponent, {
1216
+ backdropClass: 'ndw-overlay-backdrop',
1217
+ });
1218
+ ```
1219
+
1220
+ ```html
1221
+ <!-- Inside the modal component template -->
1222
+ <ndw-modal size="md">
1223
+ <ndw-modal-header>Modal Title</ndw-modal-header>
1224
+ <ndw-card-content>Modal body</ndw-card-content>
1225
+ <ndw-card-footer>
1226
+ <button ndwButton (click)="close()">Close</button>
1227
+ </ndw-card-footer>
1228
+ </ndw-modal>
1229
+ ```
1230
+
1231
+ ---
1232
+
1233
+ ### MultiSelectComponent
1234
+
1235
+ **Selector**: `ndw-multi-select`
1236
+ **Import**: `MultiSelectComponent`
1237
+
1238
+ | Input | Type | Default | Description |
1239
+ | -------------------- | --------------- | ------------------------------------------------------------ | ------------------------------------- |
1240
+ | `buttonText` | `string` | **required** | Dropdown trigger button label |
1241
+ | `searchLabel` | `string` | `'Zoek in lijst.'` | Aria label for search input |
1242
+ | `searchPlaceholder` | `string` | `'Zoek in lijst...'` | Search input placeholder |
1243
+ | `noResultText` | `string` | Dutch default | Text shown when search has no matches |
1244
+ | `prefixIcon` | `string` | `''` | Material icon before button text |
1245
+ | `chevron` | `boolean` | `true` | Show dropdown chevron icon |
1246
+ | `disabled` | `boolean` | `false` | Disabled state |
1247
+ | `selectAmountHidden` | `boolean` | `false` | Hide selected count badge |
1248
+ | `selectAllText` | `SelectAllText` | `{deselect: 'Deselecteer alles', select: 'Selecteer alles'}` | Select/deselect all button labels |
1249
+ | `allowSelectAll` | `boolean` | `true` | Show select-all toggle |
1250
+
1251
+ | Model | Type | Description |
1252
+ | ------------ | ---------------- | ----------------------------------- |
1253
+ | `dataSource` | `CheckboxData[]` | Checkbox items with selection state |
1254
+
1255
+ **CheckboxData interface**: `{ id: string \| number; label: string; value: boolean }`
1256
+
1257
+ ```html
1258
+ <ndw-multi-select buttonText="Categories" [(dataSource)]="categories"></ndw-multi-select>
1259
+ ```
1260
+
1261
+ ---
1262
+
1263
+ ### PillComponent
1264
+
1265
+ **Selector**: `ndw-pill`
1266
+ **Import**: `PillComponent`
1267
+
1268
+ | Input | Type | Default | Description |
1269
+ | ------- | -------------------------------------------------------------- | --------- | ------------- |
1270
+ | `color` | `'blue' \| 'gray' \| 'green' \| 'purple' \| 'red' \| 'yellow'` | `'green'` | Color variant |
1271
+
1272
+ ```html
1273
+ <ndw-pill color="blue">Active</ndw-pill>
1274
+ ```
1275
+
1276
+ ---
1277
+
1278
+ ### PopoverTriggerDirective
1279
+
1280
+ **Selector**: `[ndwPopoverTrigger]`
1281
+ **Import**: `PopoverTriggerDirective`
1282
+ **Export as**: `ndwPopoverTrigger`
1283
+
1284
+ | Input | Type | Default | Description |
1285
+ | ------------------- | ---------------------------------------------- | ----------------------- | ------------------------------------- |
1286
+ | `ndwPopoverTrigger` | `TemplateRef` | **required** | Template to render as popover content |
1287
+ | `popoverPosition` | `'nextToTriggerButton' \| 'overTriggerButton'` | `'nextToTriggerButton'` | Popover placement relative to trigger |
1288
+ | `toggleOnClick` | `boolean` | `true` | Toggle open/close on click |
1289
+
1290
+ | Model | Type | Description |
1291
+ | -------- | --------- | ------------------------- |
1292
+ | `isOpen` | `boolean` | Popover open/closed state |
1293
+
1294
+ | Output | Type | Description |
1295
+ | ---------------- | --------- | ------------------------------------ |
1296
+ | `popoverToggled` | `boolean` | Emitted when popover opens or closes |
1297
+
1298
+ Close popover from inside with `ndwPopoverCloseTrigger` attribute.
1299
+
1300
+ ```html
1301
+ <button [ndwPopoverTrigger]="popoverContent">Open</button>
1302
+ <ng-template #popoverContent>
1303
+ <div>Popover content</div>
1304
+ <button ndwPopoverCloseTrigger>Close</button>
1305
+ </ng-template>
1306
+ ```
1307
+
1308
+ ---
1309
+
1310
+ ### SplitterComponent
1311
+
1312
+ **Selector**: `ndw-splitter`
1313
+ **Import**: `SplitterComponent`
1314
+
1315
+ | Input | Type | Default | Description |
1316
+ | ------------------ | ---------------- | ------- | ----------------------------- |
1317
+ | `disabled` | `boolean` | `false` | Disable dragging |
1318
+ | `horizontal` | `boolean` | `false` | Horizontal split (top/bottom) |
1319
+ | `initialPanelSize` | `number \| null` | `null` | Initial first panel size (px) |
1320
+ | `minimumPanelSize` | `number` | `0` | Minimum panel size (px) |
1321
+
1322
+ Projects two panels via `ng-content` using `[first-panel]` and `[second-panel]` attribute selectors.
1323
+
1324
+ ```html
1325
+ <ndw-splitter [initialPanelSize]="300" [minimumPanelSize]="100">
1326
+ <div first-panel>Left panel</div>
1327
+ <div second-panel>Right panel</div>
1328
+ </ndw-splitter>
1329
+ ```
1330
+
1331
+ ---
1332
+
1333
+ ### SwitcherComponent
1334
+
1335
+ **Selector**: `ndw-switcher`
1336
+ **Import**: `SwitcherComponent`
1337
+ **CVA**: Yes
1338
+
1339
+ | Input | Type | Default | Description |
1340
+ | ---------- | ------------------ | ------- | ------------------------ |
1341
+ | `options` | `SwitcherOption[]` | `[]` | Available toggle options |
1342
+ | `vertical` | `boolean` | `false` | Vertical layout |
1343
+
1344
+ | Model | Type | Description |
1345
+ | --------------- | --------------- | ------------------------ |
1346
+ | `selectedValue` | `SwitcherValue` | Currently selected value |
1347
+ | `disabled` | `boolean` | Disabled state |
1348
+
1349
+ | Output | Type | Description |
1350
+ | ----------------- | --------------- | ------------------------------ |
1351
+ | `selectionChange` | `SwitcherValue` | Emitted when selection changes |
1352
+
1353
+ **SwitcherOption**: `{ value: string \| number \| boolean; label: string; icon?: string; disabled?: boolean }`
1354
+
1355
+ ```html
1356
+ <ndw-switcher
1357
+ [options]="[{value: 'map', label: 'Map'}, {value: 'list', label: 'List'}]"
1358
+ [(selectedValue)]="view"
1359
+ ></ndw-switcher>
1360
+ ```
1361
+
1362
+ ---
1363
+
1364
+ ### TabComponent & TabGroupComponent
1365
+
1366
+ **Selectors**: `ndw-tab`, `ndw-tab-group`
1367
+ **Import**: `TabComponent`, `TabGroupComponent`
1368
+
1369
+ **TabGroupComponent**:
1370
+ | Input | Type | Default | Description |
1371
+ |---|---|---|---|
1372
+ | `hasPadding` | `boolean` | `false` | Add padding around tab content |
1373
+ | `inlinePadding` | `number` | `0` | Inline padding in px for tab bar |
1374
+
1375
+ | Model | Type | Description |
1376
+ | ------------- | ------------------ | -------------------- |
1377
+ | `activeTabId` | `string \| number` | ID of the active tab |
1378
+
1379
+ **TabComponent**:
1380
+ | Input | Type | Default | Description |
1381
+ |---|---|---|---|
1382
+ | `title` | `string` | **required** | Tab label text |
1383
+ | `disabled` | `boolean` | `false` | Disabled state |
1384
+ | `hasError` | `boolean` | `false` | Show error indicator |
1385
+ | `icon` | `string` | `undefined` | Material icon in tab header |
1386
+ | `id` | `string \| number` | auto-generated | Unique tab identifier |
1387
+
1388
+ ```html
1389
+ <ndw-tab-group [(activeTabId)]="activeTab">
1390
+ <ndw-tab title="Overview" id="overview">Tab 1 content</ndw-tab>
1391
+ <ndw-tab title="Details" id="details" icon="info">Tab 2 content</ndw-tab>
1392
+ <ndw-tab title="Disabled" [disabled]="true">Tab 3</ndw-tab>
1393
+ </ndw-tab-group>
1394
+ ```
1395
+
1396
+ ---
1397
+
1398
+ ### TagComponent
1399
+
1400
+ **Selector**: `ndw-tag`
1401
+ **Import**: `TagComponent`
1402
+
1403
+ | Input | Type | Default | Description |
1404
+ | ----------------- | --------- | ------------- | -------------------------------------- |
1405
+ | `disabled` | `boolean` | `false` | Disabled state |
1406
+ | `suffixAriaLabel` | `string` | `'Verwijder'` | Aria label for the suffix/close button |
1407
+ | `suffixIcon` | `string` | `'close'` | Material icon for the suffix button |
1408
+
1409
+ | Output | Type | Description |
1410
+ | --------- | ------- | ------------------------------------- |
1411
+ | `clicked` | `Event` | Emitted when suffix button is clicked |
1412
+
1413
+ ```html
1414
+ <ndw-tag (clicked)="removeTag()">Tag label</ndw-tag>
1415
+ ```
1416
+
1417
+ ---
1418
+
1419
+ ### ToastService & ToastComponent
1420
+
1421
+ **Import**: `ToastService` (injectable)
1422
+
1423
+ | Method | Parameters | Returns | Description |
1424
+ | ---------- | ----------------------------------------------------------------------------------------- | ------------------- | ---------------------------- |
1425
+ | `open()` | `message: string, duration?: number, closeButtonLabel?: string, type?: 'info' \| 'error'` | `string` (toast ID) | Show a toast |
1426
+ | `close()` | `id?: string` | `void` | Close specific or all toasts |
1427
+ | `pause()` | - | `void` | Pause auto-dismiss timer |
1428
+ | `resume()` | - | `void` | Resume timer |
1429
+
1430
+ Error toasts do not auto-dismiss. Max 3 toasts shown simultaneously.
1431
+
1432
+ ```typescript
1433
+ this.toastService.open('Changes saved successfully');
1434
+ this.toastService.open('Something went wrong', 0, 'Dismiss', 'error');
1435
+ ```
1436
+
1437
+ ---
1438
+
1439
+ ### TooltipDirective
1440
+
1441
+ **Selector**: `[ndwTooltip]`
1442
+ **Import**: `TooltipDirective`
1443
+
1444
+ | Input | Type | Description |
1445
+ | ------------ | -------- | ----------------------- |
1446
+ | `ndwTooltip` | `string` | Tooltip text (required) |
1447
+
1448
+ ```html
1449
+ <ndw-icon [ndwTooltip]="'More information'">info</ndw-icon>
1450
+ ```
1451
+
1452
+ ---
1453
+
1454
+ ### Summary Card (CSS-only)
1455
+
1456
+ Uses CSS classes, not a component:
1457
+
1458
+ ```html
1459
+ <div class="ndw-summary-card">
1460
+ <div class="ndw-summary-card__wrapper">
1461
+ <div class="ndw-summary-card__content">
1462
+ <div class="ndw-summary-card-header">
1463
+ <div class="ndw-summary-card-header__wrapper">
1464
+ <h3 class="ndw-summary-card-header__title">
1465
+ <a href="/detail">Card Title</a>
1466
+ </h3>
1467
+ <div class="ndw-summary-card-subtitle">
1468
+ <ndw-icon>location_on</ndw-icon>
1469
+ <span class="ndw-summary-card-subtitle__text">Subtitle</span>
1470
+ </div>
1471
+ </div>
1472
+ </div>
1473
+ <p class="ndw-summary-card-content">Description text</p>
1474
+ <div class="ndw-summary-card-tags">
1475
+ <span class="ndw-summary-card-tag"><ndw-icon>label</ndw-icon> Tag</span>
1476
+ </div>
1477
+ </div>
1478
+ </div>
1479
+ </div>
1480
+ ```
1481
+
1482
+ ---
1483
+
1484
+ ### GuidedTourService
1485
+
1486
+ **Import**: `GuidedTourService` (injectable)
1487
+
1488
+ Wraps [Shepherd.js](https://shepherdjs.dev/) for guided tours.
1489
+
1490
+ | Method | Description |
1491
+ | -------------------------------- | -------------------------- |
1492
+ | `addSteps(steps: StepOptions[])` | Initialize tour with steps |
1493
+ | `start()` | Start the tour |
1494
+ | `next()` / `back()` | Navigate steps |
1495
+ | `cancel()` / `complete()` | End the tour |
1496
+ | `show(id)` | Show specific step |
1497
+
1498
+ | Property | Type | Default |
1499
+ | -------------------- | --------- | ------- |
1500
+ | `modal` | `boolean` | `false` |
1501
+ | `exitOnEsc` | `boolean` | `true` |
1502
+ | `keyboardNavigation` | `boolean` | `true` |
1503
+
1504
+ ---
1505
+
1506
+ ### AG Grid Theme
1507
+
1508
+ **Import**: `ndwAgGridTheme`
1509
+
1510
+ Pre-configured AG Grid theme using NDW design tokens.
1511
+
1512
+ ```typescript
1513
+ import { ndwAgGridTheme } from '@ndwnu/design-system';
1514
+ import { themeQuartz } from 'ag-grid-community';
1515
+
1516
+ const theme = themeQuartz.withParams(ndwAgGridTheme);
1517
+ ```
1518
+
1519
+ ---
1520
+
1521
+ ## List-item CSS directive
1522
+
1523
+ ```html
1524
+ <div ndw-list-item>Styled list item with ndw-paragraph-md and cursor pointer</div>
1525
+ ```