feat(design): adopt CIBG component patterns (header, forms, wizards, dashboard)

Re-skins the app's layout on top of the CIBG Huisstijl theme (previous commit) so it
matches designsystem.cibg.nl, not just its colour tokens — magenta ("robijn") header,
horizontal nav, and the CIBG component markup for forms/wizards/dashboard.

- Header: logo block + robijn titlebar (breadcrumb + user menu) + grey horizontal nav
  (4 links) replacing the dashboard side-nav; breadcrumb restyled for the titlebar
  (no background of its own — CIBG's global `header nav` rule otherwise bleeds a grey
  fill into it, fixed by scoping an override inside BreadcrumbComponent).
- Forms: form-field/radio-group/checkbox rebuilt on CIBG's horizontal `form-group row`
  / `form-check.styled` markup (label col-md-4, control col-md-8); same input() APIs.
- Wizards: stepper rebuilt as the CIBG "stappenindicator" (numbered circles, visited
  steps clickable for back-nav, title merged in); wizard-shell adopts the CIBG
  procesnavigatie button row. Back-navigation wired into all three wizard machines
  (registratie-wizard already had it; added `GaNaarStap` to intake/herregistratie
  machines, pure + spec'd).
- New shared/ui molecules: confirmation (animated bevestiging checkmark, replaces
  plain alerts on submit), review-section (controlestap sections with "Wijzigen"),
  application-list/application-link (CIBG "aanvragen" rows, replace the dashboard's
  card grid and aanvraag-block).
- Cleanup: delete side-nav and now-unused styles.scss utilities (.app-overview,
  .app-form-panel, .app-card-grid); correct design-tokens.mdx (it referenced tokens
  that no longer exist) and document the CIBG-value token bridge.

Verified: build/lint/check:tokens green, 178 tests pass (4 new GaNaarStap cases), and
manually driven end-to-end (dashboard, a full herregistratie submission through to the
confirmation screen, mobile width, keyboard focus).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 14:33:05 +02:00
parent 7887355ca3
commit 6257d7ede3
44 changed files with 769 additions and 591 deletions

View File

@@ -5,15 +5,21 @@ import { useState, useLayoutEffect, useRef } from 'react';
# Design tokens
We do not hand-write colours or spacing. The Rijkshuisstijl (RHC) design system ships them
as CSS custom properties; `src/styles.scss` imports them and adds a thin `--app-*` layer for
the few measures RHC has no token for. `npm run check:tokens` fails the build on any
hardcoded hex colour in atoms/molecules/chrome — so these tokens are the *only* source of
colour.
We do not hand-write colours or spacing. `src/styles.scss` defines a semantic `--rhc-*` token
vocabulary and redefines every one of those tokens onto the vendored **CIBG Huisstijl**
(Bootstrap 5.2) values — `--bs-*`/`--ro-*` custom properties where one exists, CIBG palette hex
otherwise (that one file is exempt from `npm run check:tokens`, which fails the build on any
*other* hardcoded hex colour in atoms/molecules/chrome). The `--rhc-*` names are an internal
alias set now; the values are CIBG's.
**Prefer a CIBG class over a token where one exists** — `.btn`, `.form-control`, `.card`,
`.stepper`, `.confirmation`, `.applications`, … are already themed by the vendored CSS (see
`public/cibg-huisstijl/`). Reach for a `--rhc-*` token only where CIBG has no ready-made class
(an `alert` surface, a skeleton loader, a status badge — see ADR-0003).
> Resolved values below are read live from the running theme via `getComputedStyle`, so they
> can't drift from what ships. All demos are wrapped in `.rhc-theme.lintblauw`, the same scope
> the app runs under.
> can't drift from what ships. `body.brand--cibg` (set in `index.html` and Storybook's
> `preview.ts`) activates CIBG's robijn/lintblauw palette; no extra wrapper class is needed.
export const Resolved = ({ token }) => {
const ref = useRef(null);
@@ -26,24 +32,23 @@ export const Resolved = ({ token }) => {
## When to use which token
- **CIBG class first** (see above) — a token is for the gaps a CIBG class doesn't cover.
- **Semantic first** — reach for a role token (`--rhc-color-foreground-default`,
`--rhc-color-border-default`, `--rhc-color-core`) before a raw palette step. Roles survive
a theme swap; palette steps don't.
`--rhc-color-border-default`, `--rhc-color-foreground-link`) before a raw palette step
(`--rhc-color-lintblauw-500`). Roles survive a theme swap; palette steps don't.
- **`--rhc-space-max-*`** for all spacing/gaps — never a raw `rem`.
- **`--app-*`** (in `src/styles.scss`) only for app measures RHC has no token for
(`--app-content-max`, `--app-form-max`). If you're tempted to add one, check RHC first.
<div className="rhc-theme lintblauw">
- **`--app-*`** (in `src/styles.scss`) only for app measures CIBG has no token for
(`--app-content-max`, `--app-form-narrow`). If you're tempted to add one, check CIBG first.
## Spacing scale — `--rhc-space-max-*`
<div style={{ display: 'grid', gap: '0.4rem', margin: '1rem 0' }}>
{['2xs','xs','sm','md','lg','xl','2xl','3xl','4xl','5xl'].map((step) => {
{['xs','sm','md','lg','xl','2xl','3xl','4xl','5xl'].map((step) => {
const token = `--rhc-space-max-${step}`;
return (
<div key={step} style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<code style={{ width: '12rem', fontSize: '0.78rem' }}>{token}</code>
<div style={{ height: '1rem', width: `var(${token})`, background: 'var(--rhc-color-core)', borderRadius: '2px' }} />
<div style={{ height: '1rem', width: `var(${token})`, background: 'var(--rhc-color-lintblauw-500)', borderRadius: '2px' }} />
<Resolved token={token} />
</div>
);
@@ -57,11 +62,12 @@ export const Resolved = ({ token }) => {
'--rhc-color-foreground-default',
'--rhc-color-foreground-subtle',
'--rhc-color-foreground-link',
'--rhc-color-core',
'--rhc-color-core-hover',
'--rhc-color-layout',
'--rhc-color-lintblauw-500',
'--rhc-color-lintblauw-700',
'--rhc-color-border-default',
'--rhc-color-border-strong',
'--rhc-color-bg-document',
'--rhc-color-cool-grey-100',
].map((token) => (
<div key={token} style={{ border: '1px solid #ddd', borderRadius: '6px', overflow: 'hidden' }}>
<div style={{ height: '3rem', background: `var(${token})` }} />
@@ -72,5 +78,3 @@ export const Resolved = ({ token }) => {
</div>
))}
</div>
</div>