import { Meta, Canvas } from '@storybook/addon-docs/blocks';
import * as ButtonStories from '../src/ui/atoms/button/button.stories';
import * as FormFieldStories from '../src/ui/molecules/form-field/form-field.stories';
import * as PageShellStories from '../src/layout/page-shell/page-shell.stories';
import * as DocumentUploadStories from '../src/ui/organisms/upload/document-upload/document-upload.stories';
# Atomic design
Every screen in this app is built from a small set of layers, each composed **only from
the layer below it**. Read a screen top-down and you always land on the same handful of
atoms — that is the whole point: fewer things to understand, nothing bespoke per page.
{[
[
'Templates',
'shared/layout',
'shell, page-shell, wizard-shell — the page skeleton',
'#1e3a5f',
],
[
'Organisms',
'shared/ui/upload/document-upload …',
'self-contained sections that own a bit of behaviour',
'#2a5a8a',
],
['Molecules', 'shared/ui/form-field, async …', 'a label + control + error, grouped', '#3f7cb5'],
[
'Atoms',
'shared/ui/button, text-input …',
'thin wrappers over CIBG Huisstijl (Bootstrap) CSS classes',
'#6aa6d8',
],
].map(([name, where, why, bg], i) => (
))}
## The rule, enforced
**Each layer only uses layers below it, and dependencies point inward.** This is not a
convention you have to remember — `npm run dep:check` (dependency-cruiser) fails the build if
`domain/` imports Angular, or if a context imports "upward". `eslint.config.mjs` enforces a
different rule: the `any` ban, and a `max-lines` budget (250 lines, `skipBlankLines`,
`skipComments`) on every `{apps,libs}/**/*.{page,component,section,step}.ts` file — reachable,
not a style-guide default, since the dashboard page lands at 42 lines. See
[the FP-in-the-UI primer](?path=/docs/foundations-fp-in-the-ui--docs) for how the same
discipline shapes state and effects.
## The layer folders
| Layer | Where |
| --------- | --------------------------------------------------------------------------------------------------------------------------- |
| Atoms | `libs/shared/src/ui/atoms/` |
| Molecules | `libs/shared/src/ui/molecules/` |
| Organisms | `libs/shared/src/ui/organisms/` |
| Templates | `libs/shared/src/layout/` — deliberately holds several layers; its own organisms are chrome that only its own templates use |
## The step-component contract
A wizard step follows the same rule as `address-fields.component.ts`: values in, events
out, no internal state.
1. **Inputs down.** A step reads its data only from `input()`s the container passes it.
2. **One narrow output up.** A step emits one specific event, not the container's whole
`dispatch`.
3. **`dispatch` is never passed down.** The container owns the Model and decides what a
step's event means; a step never calls `dispatch` itself.
Corollary: a step gets **no** story of its own. The wizard's own story already mounts every
step, because it seeds the machine.
## A composition chain, live
Here is one real chain from atom → molecule → template. Each is a published Storybook
story below; click through to the sidebar entries to explore every variant.
### Atom — `button`
A thin wrapper: we own a typed `variant` input, the CIBG CSS owns the pixels.