Files
ehoandClaude Sonnet 5 e7156c5132 feat(WP-67): merge behandelportal into this repo as a monorepo
Restructures into apps/ssp + apps/behandelportal (two Angular projects)
plus libs/shared + libs/beheer (cross-app libraries), replacing WP-61's
separate sibling repo. That split had already produced real drift: a
hand-vendored copy of the backend's OpenAPI doc, a shared/ui+layout tree
forked and silently diverging (7 files), and beheer + the styles.scss
token bridge duplicated byte-for-byte across both repos.

- git mv the SSP's src/app/* into apps/ssp/; fold shared/, beheer/,
  environments/, the Storybook docs/*.mdx, and styles.scss into
  libs/shared + libs/beheer (all confirmed identical between the two
  repos before merging). auth stays deliberately duplicated per
  ADR-0002 (actor-specific, expected to diverge) - amended there.
- One generated API client (libs/shared), no more vendored swagger.json.
- .dependency-cruiser split into a base factory + one config per app,
  and Storybook into .storybook-ssp/.storybook-behandelportal - both
  forced by the @auth/* alias resolving to different directories per app.
- SiteHeaderComponent/ShellComponent gained HEADER_NAV_ITEMS/
  HEADER_ADMIN_LINKS/DEBUG_PANEL injection tokens so each app supplies
  its own nav/admin-links/dev-panel instead of one being hardcoded.
- CLAUDE.md, ARCHITECTURE.md, dependencies.md, and ADR-0002 updated;
  WP-67 backlog entry documents the full decision trail.

npm run ci green (lint, dep:check x2, 360 tests across ssp/
behandelportal/shared/beheer, both localized builds, backend tests,
snippet + api-client drift); both dev servers, both Storybook
instances, and docker compose verified working.

The old sibling repo (/home/eho/repos/behandelportal) is left
untouched, not deleted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-02 21:01:57 +02:00

101 lines
3.8 KiB
Plaintext

import { Meta } from '@storybook/addon-docs/blocks';
import { useState, useLayoutEffect, useRef } from 'react';
<Meta title="Foundations/Design Tokens" />
# Design tokens
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. `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);
const [val, setVal] = useState('');
useLayoutEffect(() => {
if (ref.current) setVal(getComputedStyle(ref.current).getPropertyValue(token).trim());
}, [token]);
return (
<span ref={ref} style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#666' }}>
{val || '…'}
</span>
);
};
## 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-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 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' }}>
{['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-lintblauw-500)',
borderRadius: '2px',
}}
/>
<Resolved token={token} />
</div>
);
})}
</div>
## Semantic colours
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(14rem, 1fr))',
gap: '0.75rem',
margin: '1rem 0',
}}
>
{[
'--rhc-color-foreground-default',
'--rhc-color-foreground-subtle',
'--rhc-color-foreground-link',
'--rhc-color-layout',
'--rhc-color-lintblauw-500',
'--rhc-color-lintblauw-700',
'--rhc-color-border-default',
'--rhc-color-border-strong',
'--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})` }} />
<div style={{ padding: '0.4rem 0.5rem' }}>
<div style={{ fontFamily: 'monospace', fontSize: '0.72rem', wordBreak: 'break-all' }}>
{token}
</div>
<Resolved token={token} />
</div>
</div>
))}
</div>