Starter Angular 21 (standalone, signals) pour construire un design system 100% maison, sans dépendance à une librairie UI propriétaire. Composants headless (Angular CDK + signals natifs) stylés exclusivement via des design tokens.
C'est le volet Starter Angular de la stratégie Double-Moteur :
- La logique dépend de la stack : Angular CDK ici (Radix UI côté React).
- La couche partagée entre stacks = les design tokens (variables CSS).
- Le style des composants est co-localisé (
.scssscopé Angular) et consomme ces tokens.
| Couche | Techno |
|---|---|
| Framework | Angular 21 standalone, signals |
| Comportement | Composants maison + @angular/cdk |
| Style | Co-localisé par composant (.scss scopé) + CSS custom properties |
| Tokens | JSON (Token Flow Manager) → scripts/tokens.build.mjs → SCSS (src/styles/src/generated/) |
| Storybook | 10.x + addon-designs (Figma) |
| Grid | Gridaflex |
| Icons | FontAwesome Free |
npm install # installe + génère les tokens (postinstall → tokens:build)
npm start # app de démo → http://localhost:4200
npm run storybook # Storybook → http://localhost:6006
npm run tokens:build # régénère les variables CSS de tokens
npm run build && npm run build-storybook
npm run lint| Dimension | Attribut | Service |
|---|---|---|
| Marque | [data-brand='brand2'|'brand3'] (brand1 = défaut) |
BrandService (mappe le sous-domaine) |
| Clair/Sombre | [data-theme='dark'] (light = défaut) |
ThemeService |
Les semantics référencent les primitives (var(--primitives-*)) : changer marque ou mode
recompose tout sans duplication.
src/
├── app/
│ ├── core/service/ ← ThemeService ([data-theme]), BrandService ([data-brand])
│ ├── core/controlValueAccessor/← BaseControlValueAccessor (formulaires)
│ ├── shared/
│ │ ├── components/ui/ ← composants maison ui-* (ui-button, ui-icon…)
│ │ └── types/ ← types partagés (UiLevel, UiSize…)
│ └── pages/ ← démo (home, preview)
├── design-tokens/ ← SOURCE des tokens (JSON, export Token Flow Manager)
└── styles/
└── src/generated/ ← variables CSS GÉNÉRÉES — ne pas éditer
scripts/tokens.build.mjs ← resolver tokens (DTCG → SCSS vars)
storybook/ ← config + stories
src/styles/src/generated/est généré (gitignoré), reconstruit parnpm run tokens:build.
--primitives-* · --metrics-* · semantics sans préfixe (--actions-high-surface-default,
--global-*) · --fontfamily-* / --weight-* · --transition-*.
| Élément | Convention | SCSS |
|---|---|---|
| Racine | ui-{name} |
.ui-{name} |
| Sous-élément | ui-{name}-{part} |
&-{part} |
| Modifier | _{modifier} |
&._{modifier} |
.ui-button {
&-icon { … } // .ui-button-icon
&._small { … } // modifier
&._high { … } // modifier de niveau
&:hover { … } // états = pseudo-classes (jamais une classe modifier)
}On reproduit le patron ui-button (+ ui-icon). Exemple ui-input :
- Composant —
src/app/shared/components/ui/forms/ui-input/ui-input.ts:input()signals +computed()qui assemble la liste de classes.ui-input.html: HTML natif headless (+ CDK si overlay/a11y), accessible.ui-input.scss: style co-localisé, classes.ui-input/&-…/&._…, valeurs uniquement viavar(--…).
- Story & Documentation :
storybook/stories/ui/forms/ui-input.stories.mdx+storybook/stories/ui/forms/ui-input.stories.ts
Règles d'or : aucune valeur en dur (tout via token) · accessibilité (élément natif,
aria-label, :focus-visible, disabled).