feat(fp): WP-13 — CIBG-gap register + hygiene + MDX

Mark every hand-rolled shared/ui surface with a `// CIBG-GAP EXTENSION:`
comment + `cibgGap` story parameter (skeleton, spinner, rich-text-editor,
wizard-shell's error summary, application-link's non-navigating row,
debug-state, status-badge, card, placeholder-chip) so deviations from the
CIBG design system are auditable. Add the register MDX
(Foundations/CIBG Gap Register), cross-linked from ADR-0003. Delete the
near-identity upload-status-banner wrapper; its one consumer now uses
<app-alert> directly (a story added to keep the info-banner state covered).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 22:42:13 +02:00
parent 69880efd38
commit 9d58f597ea
27 changed files with 1676 additions and 1735 deletions

View File

@@ -48,3 +48,7 @@ layer — not a palette swap.
alternative (adding the CSS to `angular.json` `styles`) would force-bundle the licensed fonts we alternative (adding the CSS to `angular.json` `styles`) would force-bundle the licensed fonts we
intentionally dropped, so we accept the warning. intentionally dropped, so we accept the warning.
- Renaming the internal token names from `--rhc-*` to `--app-*` is possible later but out of scope. - Renaming the internal token names from `--rhc-*` to `--app-*` is possible later but out of scope.
- Hand-rolled components (point 4) are tracked in the **CIBG gap register**
(`src/docs/cibg-gaps.mdx`, Storybook "Foundations/CIBG Gap Register"): every deviation from the
design system carries a `// CIBG-GAP EXTENSION:` marker so it's auditable rather than silently
drifting.

View File

@@ -49,10 +49,10 @@ for its existing violations, so every WP ends green.
| [WP-07](WP-07-brief-idioms.md) | Brief on the shared idioms + RemoteData MDX | 1 · FP/DDD | done | | [WP-07](WP-07-brief-idioms.md) | Brief on the shared idioms + RemoteData MDX | 1 · FP/DDD | done |
| [WP-08](WP-08-store-idiom.md) | One store idiom + machine naming + TEA MDX | 1 · FP/DDD | done | | [WP-08](WP-08-store-idiom.md) | One store idiom + machine naming + TEA MDX | 1 · FP/DDD | done |
| [WP-09](WP-09-pure-logic.md) | Pure-logic closure: dates + missing command specs | 1 · FP/DDD | done | | [WP-09](WP-09-pure-logic.md) | Pure-logic closure: dates + missing command specs | 1 · FP/DDD | done |
| [WP-10](WP-10-button-fidelity.md) | CIBG button fidelity | 2 · CIBG | todo | | [WP-10](WP-10-button-fidelity.md) | CIBG button fidelity | 2 · CIBG | done |
| [WP-11](WP-11-markup-fidelity.md) | CIBG markup fidelity: application-link + absent-class triage | 2 · CIBG | done | | [WP-11](WP-11-markup-fidelity.md) | CIBG markup fidelity: application-link + absent-class triage | 2 · CIBG | done |
| [WP-12](WP-12-datablock.md) | CIBG Datablock for application data | 2 · CIBG | done | | [WP-12](WP-12-datablock.md) | CIBG Datablock for application data | 2 · CIBG | done |
| [WP-13](WP-13-cibg-gap-register.md) | CIBG-gap register + hygiene + MDX | 2 · CIBG | todo | | [WP-13](WP-13-cibg-gap-register.md) | CIBG-gap register + hygiene + MDX | 2 · CIBG | done |
| [WP-14](WP-14-storybook-taxonomy.md) | Storybook taxonomy reorg + Layers MDX | 3 · Storybook | todo | | [WP-14](WP-14-storybook-taxonomy.md) | Storybook taxonomy reorg + Layers MDX | 3 · Storybook | todo |
| [WP-15](WP-15-missing-stories.md) | Missing stories: shell + brief components | 3 · Storybook | todo | | [WP-15](WP-15-missing-stories.md) | Missing stories: shell + brief components | 3 · Storybook | todo |
| [WP-16](WP-16-component-a11y.md) | Component a11y: description wiring + alert role | 4 · a11y | todo | | [WP-16](WP-16-component-a11y.md) | Component a11y: description wiring + alert role | 4 · a11y | todo |

View File

@@ -1,8 +1,20 @@
# WP-13 — CIBG-gap register + hygiene + MDX # WP-13 — CIBG-gap register + hygiene + MDX
Status: todo Status: done (<pending commit>)
Phase: 2 — CIBG fidelity Phase: 2 — CIBG fidelity
> **Deviation:** WP-11/12 ran first but left no markers (deferred to this WP, as their own
> files note), so this WP defines the marker format fresh per its own Decisions block —
> not adopted from 11/12. The Decisions block's `task-list → Actieblok` mapping is stale:
> no `.actieblok`/`actie` class exists in the vendored CSS, and `task-list`'s own header
> comment already (accurately) documents it as composing `choice-list`'s Keuzelijst
> pattern rather than a distinct Actieblok one — left as-is rather than forced to claim a
> nonexistent mapping. `application-link`'s `.static-row` (flagged as a marked-gap
> candidate in this file's own correction note) got the marker too. The optional
> `check:cibg-gaps` script (step 4) is skipped: the register is nine rows, reviewed at PR
> time same as any other doc — a CI script to diff it against code markers is complexity
> the size of the problem doesn't warrant (noted, not built).
> **Correction (CIBG UI fidelity pass, b5c5d30):** this WP assumed the `upload/` suite > **Correction (CIBG UI fidelity pass, b5c5d30):** this WP assumed the `upload/` suite
> had no vendored CIBG classes and would be marked as a CIBG-gap ("Bestand-upload"). > had no vendored CIBG classes and would be marked as a CIBG-gap ("Bestand-upload").
> The vendored build actually ships a full upload vocabulary (`.file-picker-drop-area`, > The vendored build actually ships a full upload vocabulary (`.file-picker-drop-area`,
@@ -72,11 +84,11 @@ Components to mark (closest CIBG concept in parens):
## Acceptance criteria ## Acceptance criteria
- [ ] Every component with hand-rolled surface CSS either wraps vendored classes or - [x] Every component with hand-rolled surface CSS either wraps vendored classes or
carries the marker (spot-check with a grep for `styles: [` vs markers). carries the marker (spot-check with a grep for `styles: [` vs markers).
- [ ] Register MDX complete, linked from ADR-0003. - [x] Register MDX complete, linked from ADR-0003.
- [ ] `upload-status-banner` gone; consumer green; no story coverage lost. - [x] `upload-status-banner` gone; consumer green; no story coverage lost.
- [ ] List trio documented. - [x] List trio documented.
## Verification ## Verification

File diff suppressed because one or more lines are too long

View File

@@ -32,6 +32,10 @@ export type WizardStatus = 'editing' | 'submitting' | 'submitted' | 'failed';
@Component({ @Component({
selector: 'app-wizard-shell', selector: 'app-wizard-shell',
imports: [FormsModule, ButtonComponent, AlertComponent, SpinnerComponent, StepperComponent], imports: [FormsModule, ButtonComponent, AlertComponent, SpinnerComponent, StepperComponent],
// CIBG-GAP EXTENSION: Foutmelding — the vendored build has no error-summary/
// Veldvalidatie list pattern (verified absent from huisstijl.min.css); the
// .es-title/.es-list rules below are the hand-rolled surface, see cibg-gaps.mdx.
// They render inside a vendored `.feedback-error` alert (app-alert).
styles: [ styles: [
` `
.es-title { .es-title {

View File

@@ -15,6 +15,15 @@ const meta: Meta<WizardShellComponent> = {
<div wizardSuccess><p class="rhc-paragraph">Uw aanvraag is ontvangen.</p></div> <div wizardSuccess><p class="rhc-paragraph">Uw aanvraag is ontvangen.</p></div>
</app-wizard-shell>`, </app-wizard-shell>`,
}), }),
parameters: {
cibgGap: true,
docs: {
description: {
component:
'CIBG-gap extension (error summary only) — see Foundations/CIBG Gap Register.',
},
},
},
}; };
export default meta; export default meta;
type Story = StoryObj<WizardShellComponent>; type Story = StoryObj<WizardShellComponent>;

View File

@@ -2,6 +2,9 @@ import { Component, input, output } from '@angular/core';
import { NgTemplateOutlet } from '@angular/common'; import { NgTemplateOutlet } from '@angular/common';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
// CIBG-GAP EXTENSION: Aanvragen (non-navigating row) — the vendored
// `.dashboard-block.applications li a` chain only styles `<a>`; `.static-row`
// mirrors it from tokens for the non-navigating case, see cibg-gaps.mdx.
/** Molecule: one row in a CIBG Huisstijl "aanvragen" list /** Molecule: one row in a CIBG Huisstijl "aanvragen" list
(designsystem.cibg.nl/componenten/aanvragen) — a white card-link styled by the (designsystem.cibg.nl/componenten/aanvragen) — a white card-link styled by the
vendored `.dashboard-block.applications li a` chain (bg, chevron, link-blue `h3`), vendored `.dashboard-block.applications li a` chain (bg, chevron, link-blue `h3`),

View File

@@ -12,6 +12,15 @@ const meta: Meta<ApplicationLinkComponent> = {
// Rows are <li>s in the "aanvragen" list — a real <ul> gives them their layout. // Rows are <li>s in the "aanvragen" list — a real <ul> gives them their layout.
template: `<div class="dashboard-block applications"><ul class="list-unstyled"><li app-application-link [heading]="heading" [subtitle]="subtitle" [status]="status" [cta]="cta" [to]="to" [clickable]="clickable"></li></ul></div>`, template: `<div class="dashboard-block applications"><ul class="list-unstyled"><li app-application-link [heading]="heading" [subtitle]="subtitle" [status]="status" [cta]="cta" [to]="to" [clickable]="clickable"></li></ul></div>`,
}), }),
parameters: {
cibgGap: true,
docs: {
description: {
component:
'CIBG-gap extension (non-navigating row only, see NietInteractief) — see Foundations/CIBG Gap Register.',
},
},
},
}; };
export default meta; export default meta;
type Story = StoryObj<ApplicationLinkComponent>; type Story = StoryObj<ApplicationLinkComponent>;

View File

@@ -1,6 +1,9 @@
import { Component, input } from '@angular/core'; import { Component, input } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component'; import { HeadingComponent } from '@shared/ui/heading/heading.component';
// CIBG-GAP EXTENSION: n/a — no vendored generic-card class; `.app-card` is a
// hand-rolled surface (NOT Bootstrap's `.card`), see cibg-gaps.mdx. Prefer the
// vendored Datablock (WP-12, `app-data-block`) for application/user data.
/** Molecule: a content card. Standardises the repeated card surface (white, /** Molecule: a content card. Standardises the repeated card surface (white,
subtle border, rounded, padded) so pages compose cards instead of hand-rolling subtle border, rounded, padded) so pages compose cards instead of hand-rolling
a hand-rolled card surface. Optional heading; the rest is projected. a hand-rolled card surface. Optional heading; the rest is projected.

View File

@@ -12,6 +12,10 @@ const meta: Meta<CardComponent> = {
</app-card>`, </app-card>`,
}), }),
args: { heading: 'Persoonsgegevens (BRP)', level: 3 }, args: { heading: 'Persoonsgegevens (BRP)', level: 3 },
parameters: {
cibgGap: true,
docs: { description: { component: 'CIBG-gap extension — see Foundations/CIBG Gap Register.' } },
},
}; };
export default meta; export default meta;
type Story = StoryObj<CardComponent>; type Story = StoryObj<CardComponent>;

View File

@@ -6,6 +6,8 @@ import { BigProfileStore } from '@registratie/application/big-profile.store';
import { map } from '@shared/application/remote-data'; import { map } from '@shared/application/remote-data';
import { maskBsn, redactProfile } from './mask'; import { maskBsn, redactProfile } from './mask';
// CIBG-GAP EXTENSION: n/a — devtool, no corresponding CIBG concept; deliberately
// off-theme by design (see the ponytail note below), see cibg-gaps.mdx.
/** /**
* Dev-only "show the current Model" panel (Elm-debugger style, read-only). * Dev-only "show the current Model" panel (Elm-debugger style, read-only).
* Observes the root singletons and renders them via the json pipe. Never a * Observes the root singletons and renders them via the json pipe. Never a

View File

@@ -5,6 +5,10 @@ import { DebugStateComponent } from './debug-state.component';
const meta: Meta<DebugStateComponent> = { const meta: Meta<DebugStateComponent> = {
title: 'Devtools/State debug', title: 'Devtools/State debug',
component: DebugStateComponent, component: DebugStateComponent,
parameters: {
cibgGap: true,
docs: { description: { component: 'CIBG-gap extension — see Foundations/CIBG Gap Register.' } },
},
}; };
export default meta; export default meta;
type Story = StoryObj<DebugStateComponent>; type Story = StoryObj<DebugStateComponent>;

View File

@@ -1,5 +1,7 @@
import { Component, computed, input } from '@angular/core'; import { Component, computed, input } from '@angular/core';
// CIBG-GAP EXTENSION: n/a — no vendored inline-chip/tag class; hand-rolled
// brace-wrapped chip, see cibg-gaps.mdx.
/** Atom: a highlighted, non-editable placeholder chip for READ-ONLY rendering /** Atom: a highlighted, non-editable placeholder chip for READ-ONLY rendering
(preview, diagnostics). Distinct styling for auto-resolvable vs manual fields and (preview, diagnostics). Distinct styling for auto-resolvable vs manual fields and
for linter error/warning states. Domain-free and presentational — the caller for linter error/warning states. Domain-free and presentational — the caller

View File

@@ -8,6 +8,10 @@ const meta: Meta<PlaceholderChipComponent> = {
props: args, props: args,
template: `<app-placeholder-chip [label]="label" [autoResolvable]="autoResolvable" [state]="state"></app-placeholder-chip>`, template: `<app-placeholder-chip [label]="label" [autoResolvable]="autoResolvable" [state]="state"></app-placeholder-chip>`,
}), }),
parameters: {
cibgGap: true,
docs: { description: { component: 'CIBG-gap extension — see Foundations/CIBG Gap Register.' } },
},
}; };
export default meta; export default meta;
type Story = StoryObj<PlaceholderChipComponent>; type Story = StoryObj<PlaceholderChipComponent>;

View File

@@ -12,6 +12,10 @@ export interface PlaceholderOption {
readonly autoResolvable?: boolean; readonly autoResolvable?: boolean;
} }
// CIBG-GAP EXTENSION: Tekstgebied — CIBG has no rich-text/WYSIWYG pattern (a
// contenteditable editor with formatting + placeholder chips); hand-rolled
// surface (toolbar + chip styling), see cibg-gaps.mdx. Buttons still use the
// vendored .btn-ghost class (WP-10).
/** /**
* Molecule: a minimal no-dependency WYSIWYG editor over a `RichTextBlock`. * Molecule: a minimal no-dependency WYSIWYG editor over a `RichTextBlock`.
* *

View File

@@ -34,6 +34,10 @@ const meta: Meta<RichTextEditorComponent> = {
props: args, props: args,
template: `<app-rich-text-editor [content]="content" [placeholders]="placeholders" [editable]="editable"></app-rich-text-editor>`, template: `<app-rich-text-editor [content]="content" [placeholders]="placeholders" [editable]="editable"></app-rich-text-editor>`,
}), }),
parameters: {
cibgGap: true,
docs: { description: { component: 'CIBG-gap extension — see Foundations/CIBG Gap Register.' } },
},
}; };
export default meta; export default meta;
type Story = StoryObj<RichTextEditorComponent>; type Story = StoryObj<RichTextEditorComponent>;

View File

@@ -1,5 +1,7 @@
import { Component, OnDestroy, OnInit, computed, input, signal } from '@angular/core'; import { Component, OnDestroy, OnInit, computed, input, signal } from '@angular/core';
// CIBG-GAP EXTENSION: Laadindicatie — no vendored loading-skeleton class exists
// (verified absent from huisstijl.min.css); hand-rolled shimmer, see cibg-gaps.mdx.
/** Atom: skeleton placeholder (grey shimmer). Delay-gated so it never flashes /** Atom: skeleton placeholder (grey shimmer). Delay-gated so it never flashes
on fast responses. Render `count` lines shaped roughly like the content. */ on fast responses. Render `count` lines shaped roughly like the content. */
@Component({ @Component({

View File

@@ -5,6 +5,10 @@ const meta: Meta<SkeletonComponent> = {
title: 'Atoms/Skeleton', title: 'Atoms/Skeleton',
component: SkeletonComponent, component: SkeletonComponent,
args: { delay: 0 }, args: { delay: 0 },
parameters: {
cibgGap: true,
docs: { description: { component: 'CIBG-gap extension — see Foundations/CIBG Gap Register.' } },
},
}; };
export default meta; export default meta;
type Story = StoryObj<SkeletonComponent>; type Story = StoryObj<SkeletonComponent>;

View File

@@ -1,5 +1,7 @@
import { Component, OnDestroy, OnInit, input, signal } from '@angular/core'; import { Component, OnDestroy, OnInit, input, signal } from '@angular/core';
// CIBG-GAP EXTENSION: Laadindicatie — no vendored loading-spinner class exists
// (verified absent from huisstijl.min.css); hand-rolled, see cibg-gaps.mdx.
/** Atom: spinner that only appears after `delay` ms — fast responses never /** Atom: spinner that only appears after `delay` ms — fast responses never
flash a spinner, slow ones get feedback. */ flash a spinner, slow ones get feedback. */
@Component({ @Component({

View File

@@ -4,6 +4,10 @@ import { SpinnerComponent } from './spinner.component';
const meta: Meta<SpinnerComponent> = { const meta: Meta<SpinnerComponent> = {
title: 'Atoms/Spinner', title: 'Atoms/Spinner',
component: SpinnerComponent, component: SpinnerComponent,
parameters: {
cibgGap: true,
docs: { description: { component: 'CIBG-gap extension — see Foundations/CIBG Gap Register.' } },
},
}; };
export default meta; export default meta;
type Story = StoryObj<SpinnerComponent>; type Story = StoryObj<SpinnerComponent>;

View File

@@ -1,5 +1,7 @@
import { Component, input } from '@angular/core'; import { Component, input } from '@angular/core';
// CIBG-GAP EXTENSION: n/a — deliberate custom surface, not Bootstrap's `.badge`
// (whose pill padding/colour don't fit a status dot); see cibg-gaps.mdx.
/** Atom: a coloured dot + label. Purely presentational and domain-free — the /** Atom: a coloured dot + label. Purely presentational and domain-free — the
caller decides what colour and label mean (e.g. via registration.policy). caller decides what colour and label mean (e.g. via registration.policy).
This keeps the shared UI kernel free of any domain knowledge. */ This keeps the shared UI kernel free of any domain knowledge. */

View File

@@ -4,6 +4,10 @@ import { StatusBadgeComponent } from './status-badge.component';
const meta: Meta<StatusBadgeComponent> = { const meta: Meta<StatusBadgeComponent> = {
title: 'Shared UI/Status Badge', title: 'Shared UI/Status Badge',
component: StatusBadgeComponent, component: StatusBadgeComponent,
parameters: {
cibgGap: true,
docs: { description: { component: 'CIBG-gap extension — see Foundations/CIBG Gap Register.' } },
},
}; };
export default meta; export default meta;
type Story = StoryObj<StatusBadgeComponent>; type Story = StoryObj<StatusBadgeComponent>;

View File

@@ -1,14 +1,14 @@
import { Component, input, output } from '@angular/core'; import { Component, input, output } from '@angular/core';
import type { DeliveryChannel, UploadState } from '@shared/upload/upload.machine'; import type { DeliveryChannel, UploadState } from '@shared/upload/upload.machine';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { DocumentCategoryComponent } from '../document-category/document-category.component'; import { DocumentCategoryComponent } from '../document-category/document-category.component';
import { UploadStatusBannerComponent } from '../upload-status-banner/upload-status-banner.component';
/** Organism: the full document-upload step — the category list, or a load-error /** Organism: the full document-upload step — the category list, or a load-error
banner. Pure UI: re-exposes the category events, tagging each with its category banner. Pure UI: re-exposes the category events, tagging each with its category
where the parent needs it. The container wires these to the upload reducer. */ where the parent needs it. The container wires these to the upload reducer. */
@Component({ @Component({
selector: 'app-document-upload', selector: 'app-document-upload',
imports: [DocumentCategoryComponent, UploadStatusBannerComponent], imports: [DocumentCategoryComponent, AlertComponent],
styles: [ styles: [
` `
:host { :host {
@@ -20,10 +20,10 @@ import { UploadStatusBannerComponent } from '../upload-status-banner/upload-stat
], ],
template: ` template: `
@if (state().categoriesError) { @if (state().categoriesError) {
<app-upload-status-banner type="error" [message]="state().categoriesError!" /> <app-alert type="error">{{ state().categoriesError }}</app-alert>
} @else { } @else {
@if (state().backgroundSyncAvailable === false && state().categories.length > 0) { @if (state().backgroundSyncAvailable === false && state().categories.length > 0) {
<app-upload-status-banner type="info" [message]="foregroundOnlyMessage" /> <app-alert type="info">{{ foregroundOnlyMessage }}</app-alert>
} }
@for (c of state().categories; track c.categoryId) { @for (c of state().categories; track c.categoryId) {
<app-document-category <app-document-category

View File

@@ -56,3 +56,7 @@ export const Default: Story = { args: { state } };
export const LoadError: Story = { export const LoadError: Story = {
args: { state: { ...state, categoriesError: 'De categorieën konden niet worden geladen.' } }, args: { state: { ...state, categoriesError: 'De categorieën konden niet worden geladen.' } },
}; };
export const ForegroundOnly: Story = {
args: { state: { ...state, backgroundSyncAvailable: false } },
};

View File

@@ -1,25 +0,0 @@
import { Component, computed, input } from '@angular/core';
import { AlertComponent } from '@shared/ui/alert/alert.component';
type BannerType = 'info' | 'warning' | 'error';
type AlertType = 'info' | 'ok' | 'warning' | 'error';
/** Molecule: a polite, announced status banner. Wraps the alert atom and maps the
banner type to an alert type. Pure UI: the container computes the message. */
@Component({
selector: 'app-upload-status-banner',
imports: [AlertComponent],
template: `
<div aria-live="polite">
<app-alert [type]="alertType()">{{ message() }}</app-alert>
</div>
`,
})
export class UploadStatusBannerComponent {
message = input.required<string>();
type = input<BannerType>('info');
protected readonly alertType = computed<AlertType>(() =>
this.type() === 'info' ? 'info' : this.type(),
);
}

View File

@@ -1,21 +0,0 @@
import type { Meta, StoryObj } from '@storybook/angular';
import { UploadStatusBannerComponent } from './upload-status-banner.component';
const meta: Meta<UploadStatusBannerComponent> = {
title: 'Molecules/UploadStatusBanner',
component: UploadStatusBannerComponent,
render: (args) => ({
props: args,
template: `<app-upload-status-banner [message]="message" [type]="type" />`,
}),
};
export default meta;
type Story = StoryObj<UploadStatusBannerComponent>;
export const Info: Story = {
args: { type: 'info', message: 'Uw documenten worden geüpload.' },
};
export const Error: Story = {
args: { type: 'error', message: 'De categorieën konden niet worden geladen.' },
};

52
src/docs/cibg-gaps.mdx Normal file
View File

@@ -0,0 +1,52 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Foundations/CIBG Gap Register" />
# CIBG gap register
CIBG Huisstijl (ADR-0003) is the design system of record — a component wraps a vendored class
before it hand-rolls anything. **Grep the vendored CSS
(`public/cibg-huisstijl/css/huisstijl.min.css`) before adding new surface CSS to a component.**
When no vendored pattern exists, the component is a **CIBG-gap extension**: allowed, but only
marked so every deviation from the design system is auditable.
## Marker format
```ts
// CIBG-GAP EXTENSION: <closest CIBG concept, or "n/a"> — <why hand-rolled>
```
placed above the `@Component` decorator, plus `parameters: { cibgGap: true }` and a
"CIBG-gap extension" line in the story's `docs.description.component`.
## The register
| Component | Closest CIBG concept | Why hand-rolled |
| --- | --- | --- |
| `skeleton` | Laadindicatie | No loading-skeleton class in the vendored build. |
| `spinner` | Laadindicatie | No loading-spinner class in the vendored build. |
| `rich-text-editor` | Tekstgebied | No rich-text/WYSIWYG pattern; toolbar buttons still use vendored `.btn-ghost` (WP-10). |
| `wizard-shell` (error summary only) | Foutmelding | No error-summary/Veldvalidatie list class; renders inside a vendored `.feedback-error` alert. |
| `application-link` (non-navigating row) | Aanvragen | The vendored `.dashboard-block.applications li a` chain only styles `<a>`; `.static-row` mirrors it from tokens for the informational (non-link) case. |
| `debug-state` | n/a | Dev-only tool, deliberately off-theme — see the component's own `ponytail:` note. |
| `status-badge` | n/a | Deliberate custom status dot, not Bootstrap's `.badge` (pill padding/colour don't fit). |
| `card` (`.app-card`) | n/a | No vendored generic-card class; prefer the vendored **Datablock** (`app-data-block`, WP-12) for application/user data. |
| `placeholder-chip` | n/a | No vendored inline-chip/tag class. |
Not a gap: `confirmation` renders entirely with vendored `.confirmation*` classes (no `styles:
[...]` block) — its header comment names the pattern, no marker needed. The `upload/` suite
renders entirely with vendored classes (`.file-picker-drop-area`, `.btn-upload`, …) — reworked
onto them rather than marked (see WP-11's correction note). `task-list`, `application-list`, and
`choice-list` each wrap a distinct vendored pattern (Keuzelijst / Aanvragen / Keuzelijst) and name
it in their own header comment — no marker needed, they don't hand-roll surface CSS.
## Hygiene
`upload-status-banner` (a 23-line near-identity wrapper over `app-alert` with one consumer) was
deleted; its consumer (`document-upload`) now uses `<app-alert>` directly.
## Keeping this register honest
No automated check diffs this table against the markers in code (skipped as not worth a CI
script for a table this small — reviewed at PR time instead, same as any other doc). If markers
and this table drift, trust the code and fix the table.