Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,13 @@ $focus-ring-width: utils.$form-focus-ring-width; // ← replace the value here t
3. Create the story + the `.mdx` **co-located** in `projects/ui-kit/<category>/ui-<name>/` (outside `src/`; global doc only → `storybook/docs/`) — pick `<category>` from `storybook/docs/Overview.mdx`'s sections, or ask if the component doesn't fit an existing one
4. Check off the component in `components-index.md`

> File **placement** does not drive the Storybook sidebar tree — the `title` does
> (`<Meta title="…">`, or the story's `title:`). Placement only decides what ships: a
> co-located `.stories.ts`/`.mdx` sits outside the entry point's `src/`, which is where
> `ng-packagr` starts from, so it never reaches `@4sh/ui-kit`. It does travel in
> `@4sh/ui-kit-schematics` (FSHSP-125), which copies it next to the component so a
> starter project documents its own copies.

### Create a business component (domain)

Like a `ui-*`, but: project prefix, `domain/` folder, and **composition of `ui-*` instances** (never style copying).
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le pr
Les imports suivent les copies, comme pour les sources — mais les extraits de code des pages ne sont pas réécrits : ce sont des exemples destinés au lecteur, pas des imports à résoudre. Les liens `parameters.design` vers notre fichier Figma sont retirés à la copie : le consommateur n'y a pas accès. Les pages transverses embarquées se limitent à *Foundations*, *Spécifications* et *Configuration* ; `Introduction`, `NpmPackage` et `Overview` parlent du kit lui-même et restent ici.

Désactivé par défaut : sans Storybook, story et MDX sont deux fichiers qui importent des packages absents. Le choix est retenu dans `ui-kit.json` et `update` s'y tient.
- **Les README du kit (EN + FR) présentent les deux modes de consommation** (FSHSP-116) : section « Deux modes de consommation » avant l'installation — tableau comparatif *dépendance* / *starter* (ce qui arrive dans le dépôt, imports, styles, documentation, personnalisation, mise à jour), qui renvoie au package compagnon pour la voie starter. FSHSP-109 avait livré le mode starter sans qu'il apparaisse nulle part sur la page npm, où le README est la seule documentation.
- **`@4sh/ui-kit-schematics` embarque son `LICENSE` et ses README** (EN + FR). Le package déclarait `Apache-2.0` sans en fournir le texte, alors que la licence (§4a) demande qu'une redistribution en joigne une copie — d'autant plus ici que ce package existe pour que son contenu soit recopié ailleurs. Sa page npmjs, jusque-là vide, redirige maintenant vers `@4sh/ui-kit` : le compagnon ne s'installe jamais directement.

### Fixed
Expand Down
214 changes: 54 additions & 160 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,197 +10,89 @@ This is the **Starter Angular** side of the *Dual-Engine* strategy:
- The layer shared across stacks = the **design tokens** (CSS variables).
- The **component styling is co-located** (Angular scoped `.scss`) and consumes these tokens.

The `ui-*` kit is published as the **[`@4sh/ui-kit`](https://www.npmjs.com/package/@4sh/ui-kit)**
npm package (one secondary entry point per component) — see
[`projects/ui-kit/README.md`](projects/ui-kit/README.md). This repo holds both the
package and the demo application that consumes it.
This repo holds the **[`@4sh/ui-kit`](https://www.npmjs.com/package/@4sh/ui-kit)** package
(53 secondary entry points / 54 `ui-*` components) and the demo application that consumes it.

Both are published to GitHub Pages on every push to `main`
([`deploy-pages.yml`](.github/workflows/deploy-pages.yml)):
| | |
|---|---|
| **Storybook** — component catalogue and foundations | <https://4sh.github.io/starter-angular/?path=/docs/introduction--docs> |
| **Demo application** — the kit in a real app | <https://4sh.github.io/starter-angular/demo/> |
| **Package** | [npmjs.com/package/@4sh/ui-kit](https://www.npmjs.com/package/@4sh/ui-kit) |

- **Storybook** (published catalogue):
<https://4sh.github.io/starter-angular/?path=/docs/introduction--docs>
- **Demo application** (the kit in a real app):
<https://4sh.github.io/starter-angular/demo/>
Both sites are redeployed on every push to `main`
([`deploy-pages.yml`](.github/workflows/deploy-pages.yml)).

---

## Stack

| Layer | Tech |
|--------------|---|
| Framework | Angular 22 standalone, signals, zoneless |
| Behavior | Components + `@angular/cdk` |
| Style | Co-located per component (scoped `.scss`) + CSS custom properties |
| Tokens | JSON (Token Flow Manager) → `scripts/tokens.build.mjs` → SCSS (`projects/ui-kit/styles/generated/`) |
| Tokens | JSON (Token Flow Manager) → `scripts/tokens.build.mjs` → SCSS |
| Storybook | 10.x + addon-designs (Figma) |
| Grid | Gridaflex |
| Icons | FontAwesome Free |


---

## Getting started

```bash
npm install # install + postinstall: tokens:build, ui-kit:build, docs:config
npm start # Storybook → http://localhost:6006
npm run serve # demo application → http://localhost:4200
npm run tokens:build # regenerate the token CSS variables
npm run ui-kit:build # build the @4sh/ui-kit package into dist/ui-kit
npm run docs:config # regenerate the Theming tables of the Storybook docs
npm run build && npm run build-storybook
npm run lint
```

> `npm start`, `npm run serve` and the two builds all run `ui-kit:build` first: the
> demo app consumes the kit through its **built** output (`dist/ui-kit`, mapped by the
> `@4sh/ui-kit/*` `paths` of `tsconfig.json`), exactly like an external consumer would.

### Themes & modes (runtime, via attributes on `<html>`)

| Dimension | Attribute | Service |
|---|---|---|
| Brand | `[data-brand='brand2'\|'brand3']` (brand1 = default) | `BrandService` (maps the subdomain) |
| Light/Dark | `[data-theme='dark']` (light = default) | `ThemeService` |

The semantics reference the primitives (`var(--primitives-*)`): switching brand or mode
recomposes everything without duplication.

---

## Structure

```
projects/ui-kit/ ← the @4sh/ui-kit PACKAGE (published)
├── ui-button/ … ui-tooltip/ ← 53 entry points / 54 ui-* components
│ ├── src/lib/ui-x.{ts,html,scss} ← the component, co-located
│ └── ui-x.{stories.ts,mdx} ← its doc, outside src/ (not packaged)
├── forms/ ← BaseControlValueAccessor, BaseFieldControl, BaseFormField…
├── theming/ ← ThemeService ([data-theme]), BrandService ([data-brand])
├── motion/ overlay/ types/ ← UiMotion · closeOnNavigation · UiLevel, UiSize…
└── styles/ ← SCSS foundation shipped with the package
├── generated/ ← GENERATED CSS variables — do not edit
├── base/ settings/ utils/ ← base layer, settings, utils API (no CSS emitted)
└── index.scss ← compiled to dist/ui-kit/styles.css

src/ ← DEMO application only
├── app/shared/components/domain/ ← business components (project prefix), compose ui-*
├── design-tokens/ ← token SOURCE (JSON, Token Flow Manager export)
└── styles/ ← app-side global styles (Gridaflex, layout, vendors)

scripts/tokens.build.mjs ← token resolver (DTCG → SCSS vars)
scripts/docs.config.mjs ← SCSS roles → storybook/generated/ui-config.json
storybook/ ← config + global documentation
docs/ ← VERSIONING.md · PUBLISHING.md · Figma runbooks
```

> `projects/ui-kit/styles/generated/` is **generated** (gitignored), rebuilt by
> `npm run tokens:build`. Same for `storybook/generated/` (`npm run docs:config`).
## Using the kit in an application

### CSS variable naming
**You do not need this repo.** The kit is published on the public npm registry and
consumed in one of two modes:

`--primitives-*` · `--metrics-*` · semantics without prefix (`--actions-high-surface-default`,
`--global-*`) · `--fontfamily-*` / `--weight-*` · `--transition-*`.

---

## CSS class conventions (no BEM)

| Element | Convention | SCSS |
|---|---|---|
| Root | `ui-{name}` | `.ui-{name}` |
| Sub-element | `ui-{name}-{part}` | `&-{part}` |
| Modifier | `_{modifier}` | `&._{modifier}` |

```scss
.ui-button {
&-icon { … } // .ui-button-icon
&._small { … } // modifier
&._high { … } // level modifier
&:hover { … } // states = pseudo-classes (never a modifier class)
}
```bash
npm install @4sh/ui-kit # dependency — compiled components, updated by version bump
ng add @4sh/ui-kit-schematics # starter — component sources copied into your project, yours to edit
```

---
Starter mode goes through the companion package, and deliberately leaves
`@4sh/ui-kit` out of `node_modules`: the one command lays the foundation and
copies the components you pick, `--with-storybook` brings their documentation
along, and `ng generate @4sh/ui-kit-schematics:update` replays their diff after a
kit release.

## Adding a new component (recipe)
Everything a consumer needs — choosing between the two modes, the list of entry
points, the stylesheet to load, the providers expected by `ui-image` and
`BrandService`, building your own field on `@4sh/ui-kit/forms` — is documented
**once**, in the README shipped with the package (which is also what the npmjs
page displays):

Replicate the `actions/ui-button` (+ `base/ui-icon`) reference pattern. One folder
per component under `projects/ui-kit/<category>/` (grouped by category — see
`storybook/docs/Overview.mdx` for the mapping), which **is** the secondary entry
point — example for `@4sh/ui-kit/forms/ui-input`:
**[`projects/ui-kit/README.md`](projects/ui-kit/README.md)** ·
[Français](projects/ui-kit/README.fr.md)

```
projects/ui-kit/forms/ui-input/
├── ng-package.json ← declares the entry point (entryFile + styleIncludePaths)
├── src/
│ ├── public-api.ts ← what the entry point exports
│ └── lib/ui-input.{ts,html,scss}
├── ui-input.stories.ts ← OUTSIDE src/: Storybook only, never packaged
└── ui-input.mdx
```

1. **Component** — in `src/lib/`:
- `ui-input.ts`: `input()` signals + `computed()` that assembles the class list.
- `ui-input.html`: headless native HTML (+ CDK if overlay/a11y), accessible.
- `ui-input.scss`: **co-located style**, classes `.ui-input` / `&-…` / `&._…`,
values only via `var(--…)`. Document the public roles with `///` comments —
they feed the `## Theming` section of the MDX (see below).
2. **Entry point** — copy the `ng-package.json` of a neighbouring component and export
the class from `src/public-api.ts`.
3. **Story & doc** — `ui-input.stories.ts` + `ui-input.mdx` at the folder root.

⚠️ Importing from **another entry point** must use the real package name
(`@4sh/ui-kit/base/ui-icon`), never a relative path: `ng-packagr` only records the
dependency in that form — otherwise the build order is undefined and fails
intermittently.

Golden rules: **no** hardcoded values (everything via tokens) · **accessibility** (native element,
`aria-label`, `:focus-visible`, `disabled`).
The API of each component, the tokens, the themes and the responsive rules are in
the [Storybook](https://4sh.github.io/starter-angular/?path=/docs/introduction--docs).

---

## Storybook — file organization

Three locations, one simple rule:
## Working in this repo

- **Component → co-location.** Each component carries its `*.stories.ts` and `*.mdx`
files **in its own folder**, at the entry point root (outside `src/`, so they never
ship in the package): `projects/ui-kit/<category>/ui-x/ui-x.stories.ts` + `ui-x.mdx`.
- **Global documentation** (foundations, guidelines, design system, overview) → **`storybook/docs/`**
(subfolders `foundations/`, `specifications/`). Never put component-specific doc there.
Only needed to build the design system itself: add or modify a `ui-*` component,
edit the tokens, cut a release.

The `storybook/main.js` config targets all three sources:

```js
stories: [
'./docs/**/*.mdx', // global doc
'../projects/ui-kit/**/*.mdx', // co-located kit doc
'../projects/ui-kit/**/*.stories.@(js|jsx|mjs|ts|tsx)',
'../src/app/shared/components/**/*.mdx', // app-side domain/ components
'../src/app/shared/components/**/*.stories.@(js|jsx|mjs|ts|tsx)',
]
```bash
npm install # install + postinstall: tokens:build, ui-kit:build, docs:config
npm start # Storybook → http://localhost:6006
npm run serve # demo application → http://localhost:4200
npm run tokens:build # regenerate the token CSS variables
npm run ui-kit:build # build the package into dist/ui-kit
npm run lint # ESLint --fix
```

> File **placement** does not affect the sidebar tree: it is driven by the
> `title` (`<Meta title="…">` or the story's `title:`). Co-located `.stories.ts` files
> never reach the published package: they sit outside the entry point's `src/`, which
> is where `ng-packagr` starts from.

### The `## Theming` section is generated, never written by hand

`npm run docs:config` (`scripts/docs.config.mjs`) reads the `///` comments of each
component `.scss` and produces `storybook/generated/ui-config.json`, rendered by
`<ConfigTable of="ui-x" />`. So a role is documented **where it is declared**:

```scss
$card-padding: var(--units-lg); /// Inset du corps.
$card-radius: var(--radius-md); /// Rayon des coins.
```
> `npm start`, `npm run serve` and the builds all run `ui-kit:build` first: the demo app
> consumes the kit through its **built** output (`dist/ui-kit`, mapped by the
> `@4sh/ui-kit/*` `paths` of `tsconfig.json`), exactly like an external consumer would.
>
> `projects/ui-kit/styles/generated/` and `storybook/generated/` are **generated**
> (gitignored), rebuilt by `tokens:build` and `docs:config`.

Never write a resolved value (`(12px)`) in a role: the doc measures it at runtime, in
the active theme, brand and viewport. `npm run docs:config:check` fails if the manifest
is stale — it runs on `postinstall` and before `storybook` / `build-storybook`.
Conventions to follow before writing code — repo layout, naming, CSS/SCSS rules
(no BEM), component creation recipe, Storybook file organization, token workflow —
are all in **[`AGENTS.md`](AGENTS.md)**. It is the single source of truth for
them, for humans as much as for AI agents.

---

Expand All @@ -209,7 +101,9 @@ is stale — it runs on `postinstall` and before `storybook` / `build-storybook`
| Topic | Where |
|---|---|
| Consuming the package | [`projects/ui-kit/README.md`](projects/ui-kit/README.md) ([FR](projects/ui-kit/README.fr.md)) |
| Coding conventions in this repo | [`AGENTS.md`](AGENTS.md) · [`CLAUDE.md`](CLAUDE.md) |
| Versioning & releases | [`docs/VERSIONING.md`](docs/VERSIONING.md) · [`CHANGELOG.md`](CHANGELOG.md) |
| Publishing to npm | [`docs/PUBLISHING.md`](docs/PUBLISHING.md) |
| Conventions for AI agents | [`AGENTS.md`](AGENTS.md) · [`CLAUDE.md`](CLAUDE.md) |
| Schematics companion package | [`projects/ui-kit-schematics/README.md`](projects/ui-kit-schematics/README.md) |
| Figma ↔ code workflow | [`CLAUDE.md`](CLAUDE.md) · [`docs/figma-migration-global.md`](docs/figma-migration-global.md) |
| Security policy | [`SECURITY.md`](SECURITY.md) |
26 changes: 26 additions & 0 deletions projects/ui-kit/README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@ natif (signals + Angular CDK), style piloté par les design tokens.

---

## Deux modes de consommation

À choisir **avant** d'installer : la décision conditionne toute la suite.

| | **dépendance** | **starter** |
|---|---|---|
| Installation | `npm install @4sh/ui-kit` | `ng add @4sh/ui-kit-schematics` |
| Ce qui arrive dans votre dépôt | rien — les composants compilés restent dans `node_modules` | les *sources* des composants, dans `src/app/shared/` |
| Imports | `@4sh/ui-kit/actions/ui-button` | votre propre chemin (`./shared/components/ui/actions/ui-button`) |
| Styles | `node_modules/@4sh/ui-kit/styles.css`, chargée globalement | copiés dans `src/styles/`, avec la chaîne de génération des tokens (`npm run tokens:build`) |
| Documentation | le Storybook lié ci-dessus | la vôtre, sur vos copies (`--with-storybook`) |
| Personnalisation | inputs + variables CSS | modifier le code lui-même |
| Mise à jour | bump de version | `ng generate @4sh/ui-kit-schematics:update` — diff par composant, appliquer ou ignorer |

**dépendance** est le mode par défaut : rien à maintenir, une seule version à
suivre, et la garantie que tous les projets affichent le même kit. **starter** —
l'approche de shadcn/ui ou spartan-ng — échange cette garantie contre la
possession du code : à choisir quand le projet doit s'écarter du Design System,
en assumant que les mises à jour deviennent semi-manuelles.

Les deux modes décrivent les mêmes composants. **La suite de cette page décrit le
mode `dépendance`** ; la voie starter a son propre package et son propre README
(voir [plus bas](#ou-copier-les-sources-à-la-place)).

---

## Installation

```bash
Expand Down
26 changes: 26 additions & 0 deletions projects/ui-kit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@ built in natively (signals + Angular CDK), styling driven by design tokens.

---

## Two ways to consume the kit

Choose **before** installing: the decision shapes everything that follows.

| | **dependency** | **starter** |
|---|---|---|
| Install | `npm install @4sh/ui-kit` | `ng add @4sh/ui-kit-schematics` |
| What lands in your repo | nothing — compiled components stay in `node_modules` | the component *sources*, in `src/app/shared/` |
| Imports | `@4sh/ui-kit/actions/ui-button` | your own path (`./shared/components/ui/actions/ui-button`) |
| Styles | `node_modules/@4sh/ui-kit/styles.css`, loaded globally | copied into `src/styles/`, with the token generation chain (`npm run tokens:build`) |
| Documentation | the Storybook linked above | yours, on your own copies (`--with-storybook`) |
| Customization | inputs + CSS variables | edit the code itself |
| Updating | bump the version | `ng generate @4sh/ui-kit-schematics:update` — per-component diff, accept or skip |

**dependency** is the default: nothing to maintain, one version to follow, and a
guarantee that every project renders the same kit. **starter** — the shadcn/ui or
spartan-ng approach — trades that guarantee for ownership of the code: pick it
when the project needs to diverge from the Design System, and accept that
updates are then semi-manual.

Both modes describe the same components. **The rest of this page describes the
`dependency` mode**; the starter path has its own package and its own README (see
[below](#or-copy-the-sources-instead)).

---

## Installation

```bash
Expand Down
22 changes: 22 additions & 0 deletions storybook/docs/NpmPackage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ composant, dans la barre latérale, donne l'import exact à recopier.

---

## Ou : posséder les sources (mode *starter*)

`npm install` livre les composants **compilés**. Le second mode de consommation
les installe en **sources**, dans votre dépôt, libres d'être modifiées :

```bash
ng add @4sh/ui-kit-schematics # fondation + composants, en une commande
ng add @4sh/ui-kit-schematics --with-storybook # avec la doc des composants copiés
ng generate @4sh/ui-kit-schematics:update # diff par composant après une release du kit
```

Le parcours passe par le package compagnon, et `@4sh/ui-kit` n'entre jamais dans
`node_modules` : absent, rien ne peut importer son code compilé à la place de vos
copies.

Le choix entre les deux — ce qui arrive dans le dépôt, les imports, les styles, la
personnalisation, les mises à jour — est comparé dans le README du package,
ci-dessous. La documentation des composants, elle, est la même dans les deux cas :
c'est ce Storybook.

---

## Le mode d'emploi complet vit dans le README du package

Installation, liste des entry points, feuille de styles à charger, providers
Expand Down
Loading