refactor(libs): graduate Icon / LayoutStateService / brand tokens to libs/shared/* #99

Merged
julien merged 1 commits from feat/portal-shell/libs-graduation into main 2026-05-12 01:17:31 +02:00
Owner

Summary

Per ADR-0020 §"Shared-libs graduation": the three primitives that portal-shell (today) and portal-admin (next) will both share move out of apps/portal-shell/src/ and into the workspace libs. Mechanical move — no behaviour change, prepares the ground for the admin-app PR.

Graduated

Primitive Old location New location
Icon component (+ IconName type) apps/portal-shell/src/app/components/icon/ libs/shared/ui/src/lib/icon/
LayoutStateService (+ ThemeMode type) apps/portal-shell/src/app/state/ libs/shared/state/src/lib/
Brand-palette @theme block apps/portal-shell/src/styles.css libs/shared/tokens/src/brand-tokens.css

Notable changes

  • Icon selector renamed app-iconlib-icon. Required by the lib's ESLint @angular-eslint/component-selector rule (prefix lib for shared libs). All ~20 usages in portal-shell templates updated in one sweep.
  • New shared-state library generated via nx g @nx/angular:library libs/shared/state. Mirrors the existing feature-auth / shared-ui shape (vite + Angular + spec setup). tsconfig.base.json path alias shared-state added by the generator.
  • Lib tags rebalanced to satisfy the module-boundary lint rule:
    • shared-state: new lib → scope:shared, type:shared.
    • shared-ui: was scope:portal-shell (scaffold default) → scope:shared, type:shared.
  • shared-tokens is now CSS-only. The placeholder TS function is gone; index.ts is an empty export {} with a TODO note. Vitest config flips passWithNoTests: true so the test target stops failing on the empty lib.
  • portal-shell's styles.css drops the inline @theme {} block and instead @imports ../../../libs/shared/tokens/src/brand-tokens.css. Both apps will read the same source.
  • Placeholder scaffolded files in shared-ui/src/lib/shared-ui/ removed.

Verification

  • nx run-many -t lint test build across portal-shell, shared-ui, shared-state, shared-tokens — green.
  • Tests redistributed: portal-shell 28, shared-state 9, shared-ui 3, shared-tokens 0 = 40 total (same as before the move).
  • Production build: 128.7 KB gzip initial per locale (was 128.5 KB on main — noise-level delta).
  • Brand-color CSS variables (--color-brand-primary-500: #12546c, …) still inlined in the produced styles-*.css.
  • dist/.../browser/{en,fr}/ emitted as expected.

What this PR explicitly does NOT do

  • Move other components (Header, Sidebar, Footer, ThemeSwitcher, LocaleSwitcher). Those are app-specific shell concerns; if portal-admin ends up needing them, they graduate in their own PR.
  • Set up portal-admin. That's the next PR on the admin track — and now imports from shared-ui / shared-state / shared-tokens will Just Work.
  • Refactor feature-auth's tagging. It still says scope:portal-shell; revisit when the auth implementation actually lands and the dual-audience design (per ADR-0008) makes the right scope obvious.

Test plan

  • Per-project run-many — green.
  • Production build emits both locales with brand tokens applied.
  • Manual: pnpm exec nx serve portal-shell — the dev experience is unchanged.
  • Manual: serve-static + locale switcher / theme switcher / sidebar collapse / navigation — all the behaviours the moved primitives drive still work in production.
## Summary Per ADR-0020 §"Shared-libs graduation": the three primitives that `portal-shell` (today) and `portal-admin` (next) will both share move out of `apps/portal-shell/src/` and into the workspace libs. Mechanical move — no behaviour change, prepares the ground for the admin-app PR. ## Graduated | Primitive | Old location | New location | |---|---|---| | `Icon` component (+ `IconName` type) | `apps/portal-shell/src/app/components/icon/` | [`libs/shared/ui/src/lib/icon/`](libs/shared/ui/src/lib/icon/) | | `LayoutStateService` (+ `ThemeMode` type) | `apps/portal-shell/src/app/state/` | [`libs/shared/state/src/lib/`](libs/shared/state/src/lib/) | | Brand-palette `@theme` block | `apps/portal-shell/src/styles.css` | [`libs/shared/tokens/src/brand-tokens.css`](libs/shared/tokens/src/brand-tokens.css) | ## Notable changes - **Icon selector renamed `app-icon` → `lib-icon`.** Required by the lib's ESLint `@angular-eslint/component-selector` rule (prefix `lib` for shared libs). All ~20 usages in `portal-shell` templates updated in one sweep. - **New `shared-state` library** generated via `nx g @nx/angular:library libs/shared/state`. Mirrors the existing `feature-auth` / `shared-ui` shape (vite + Angular + spec setup). `tsconfig.base.json` path alias `shared-state` added by the generator. - **Lib tags rebalanced** to satisfy the module-boundary lint rule: - `shared-state`: new lib → `scope:shared, type:shared`. - `shared-ui`: was `scope:portal-shell` (scaffold default) → `scope:shared, type:shared`. - **`shared-tokens` is now CSS-only.** The placeholder TS function is gone; `index.ts` is an empty `export {}` with a TODO note. Vitest config flips `passWithNoTests: true` so the test target stops failing on the empty lib. - **`portal-shell`'s `styles.css`** drops the inline `@theme {}` block and instead `@import`s `../../../libs/shared/tokens/src/brand-tokens.css`. Both apps will read the same source. - Placeholder scaffolded files in `shared-ui/src/lib/shared-ui/` removed. ## Verification - `nx run-many -t lint test build` across `portal-shell, shared-ui, shared-state, shared-tokens` — green. - Tests redistributed: **portal-shell 28**, **shared-state 9**, **shared-ui 3**, **shared-tokens 0** = 40 total (same as before the move). - Production build: **128.7 KB gzip** initial per locale (was 128.5 KB on `main` — noise-level delta). - Brand-color CSS variables (`--color-brand-primary-500: #12546c`, …) still inlined in the produced `styles-*.css`. - `dist/.../browser/{en,fr}/` emitted as expected. ## What this PR explicitly does NOT do - Move other components (Header, Sidebar, Footer, ThemeSwitcher, LocaleSwitcher). Those are app-specific shell concerns; if `portal-admin` ends up needing them, they graduate in their own PR. - Set up `portal-admin`. That's the next PR on the admin track — and now imports from `shared-ui` / `shared-state` / `shared-tokens` will Just Work. - Refactor `feature-auth`'s tagging. It still says `scope:portal-shell`; revisit when the auth implementation actually lands and the dual-audience design (per ADR-0008) makes the right scope obvious. ## Test plan - [x] Per-project run-many — green. - [x] Production build emits both locales with brand tokens applied. - [ ] Manual: `pnpm exec nx serve portal-shell` — the dev experience is unchanged. - [ ] Manual: serve-static + locale switcher / theme switcher / sidebar collapse / navigation — all the behaviours the moved primitives drive still work in production.
julien added 1 commit 2026-05-12 01:17:15 +02:00
refactor(libs): graduate Icon / LayoutStateService / brand tokens to libs/shared/*
CI / commits (pull_request) Successful in 3m9s
CI / scan (pull_request) Successful in 3m25s
CI / check (pull_request) Successful in 3m58s
CI / a11y (pull_request) Successful in 1m59s
CI / perf (pull_request) Successful in 5m11s
f7c3ba24d7
Per ADR-0020 §"Shared-libs graduation": the three primitives that
both `portal-shell` (today) and `portal-admin` (next) will share
move out of `apps/portal-shell/src/` and into the workspace libs.
Mechanical move — no behaviour change.

Graduated:

- `Icon` component → `libs/shared/ui/src/lib/icon/`. Selector
  renamed `app-icon` → `lib-icon` (consumed via `<lib-icon>`),
  `IconName` re-exported from `shared-ui`.
- `LayoutStateService` (+ `ThemeMode` type) →
  `libs/shared/state/src/lib/`. New library generated via
  `nx g @nx/angular:library` to match the existing
  `feature-auth` / `shared-ui` shape.
- Brand-palette `@theme` block →
  `libs/shared/tokens/src/brand-tokens.css`. `portal-shell`'s
  `styles.css` imports it by relative path.

Lib tags updated so the module-boundary lint rule lets both apps
consume them:

- `shared-state`: new lib → `scope:shared, type:shared`.
- `shared-ui`: was `scope:portal-shell` (stopgap from the initial
  scaffold) → `scope:shared, type:shared`.

Side-edits:

- `tsconfig.base.json` gains the `shared-state` path alias
  (generator-applied).
- `shared-tokens` is now CSS-only — its placeholder TS function is
  removed and its vitest config flips `passWithNoTests: true` since
  there is nothing to test there yet.
- Placeholder `shared-ui/shared-ui.{ts,html,css,spec.ts}` files
  removed.

Verification:

- Lint / test / build run-many across the four projects — green.
- portal-shell: 28 tests, shared-state: 9, shared-ui: 3 (= the same
  40 specs as before, redistributed across the new libs).
- Production build: 128.7 KB gzip initial per locale (was 128.5 KB
  on main — noise-level delta).
- Brand tokens still inlined in the produced `styles-*.css`.
- Both `dist/.../browser/{en,fr}/` emitted as expected.

No public-API breakage. The migration to `portal-admin` is now a
clean `import { Icon } from 'shared-ui'` + `LayoutStateService`
inject + `@import` of `brand-tokens.css` on admin's `styles.css` —
no duplication.
julien merged commit 8329fa133d into main 2026-05-12 01:17:31 +02:00
julien deleted branch feat/portal-shell/libs-graduation 2026-05-12 01:17:33 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: julien/apf_portal#99