Material 3 Theming — requirements register (R001–R018)¶
Preserved verbatim from .gsd/REQUIREMENTS.md — the capability-and-coverage contract for the theming milestones. Each requirement is mapped to its validating slice.
Source: .gsd/REQUIREMENTS.md at commit fc1bdf410.
This file is the explicit capability and coverage contract for the project.
Use it to track what is actively in scope, what has been validated by completed work, what is intentionally deferred, and what is explicitly out of scope.
Guidelines: - Keep requirements capability-oriented, not a giant feature wishlist. - Requirements should be atomic, testable, and stated in plain language. - Every Active requirement should be mapped to a slice, deferred, blocked with reason, or moved out of scope. - Each requirement should have one accountable primary owner and may have supporting slices. - Research may suggest requirements, but research does not silently make them binding. - Validation means the requirement was actually proven by completed work and verification, not just discussed.
Active¶
R001 — Dead code and unused component removal¶
- Class: quality-attribute
- Status: active → validated
- Description: All dead components (e.g.
<app-select>) and unused style assets are deleted from the codebase - Why it matters: Dead code inflates audit counts and confuses developers about what's real
- Source: inferred
- Primary owning slice: M001/S01
- Supporting slices: none
- Validation: S01/T01 — 19 dead components (75 files) removed after class name + selector reference scan; production build passes; re-scan confirms zero unreferenced components
- Notes: Candidates identified in
.planning/design-system/2026-02-08-pr1-dead-code-icon-cleanup.md
R002 — Bootstrap class and SCSS shim removal¶
- Class: quality-attribute
- Status: active → validated
- Description: All Bootstrap CSS classes are replaced with Angular Material equivalents; legacy-bootstrap/ shim directory is deleted
- Why it matters: Two styling systems create inconsistency and bloat; Bootstrap classes bypass the design token system
- Source: user
- Primary owning slice: M001/S01
- Supporting slices: none
- Validation: S01/T02 — all Bootstrap classes replaced with Angular Material directives and scoped action-/theme- component styles; legacy-bootstrap/ deleted; audit script metric 3 = 0; production build passes
- Notes: 13 templates migrated (was estimated 22 — 9 were false positives from scoped class names). Scoped action-/theme- styles use hardcoded Bootstrap hex colors as interim — token migration in S03.
R003 — M2 to M3 global theme migration¶
- Class: core-capability
- Status: active → validated
- Description: The global theme uses Angular Material M3 APIs (
mat.theme()) emitting CSS custom properties (--mat-sys-*) - Why it matters: M3 is the current Material Design standard; M2 APIs will eventually be removed
- Source: user
- Primary owning slice: M001/S02
- Supporting slices: none
- Validation: S02/T01 — syrf-theme.scss rewritten with mat.define-theme() + mat.theme(); zero M2 API references; production build passes
- Notes: Used
ng generate @angular/material:m3-themefor HCT palette generation
R004 — SyRF brand palette on M3 color system¶
- Class: core-capability
- Status: active → validated
- Description: SyRF's primary (#203457), accent, and warn colors are expressed as M3 HCT-based palettes and applied via
mat.theme() - Why it matters: Brand identity must survive the migration
- Source: user
- Primary owning slice: M001/S02
- Supporting slices: none
- Validation: S02/T01 — _m3-palettes.scss generated from #203457 + #c6cad2; loading logo renders in correct navy; --mat-sys-primary visible in DevTools
- Notes: M3 schematic generates accessible tonal palettes from a single seed color
R005 — Component theme mixin migration¶
- Class: core-capability
- Status: active → validated
- Description: All 10 component theme mixin files are migrated from
mat.m2-*APIs to M3 equivalents or CSS custom properties - Why it matters: Component-level theming must use the same system as the global theme
- Source: inferred
- Primary owning slice: M001/S02
- Supporting slices: none
- Validation: S02/T02 — all 10 mixins use var(--mat-sys-*); grep for m2- returns nothing; dead _sidenav-theme.import.scss deleted
- Notes: Migrated simultaneously with global theme — M3 theme objects are structurally incompatible with M2 palette extraction
R006 — Hardcoded hex color elimination¶
- Class: quality-attribute
- Status: active
- Description: Zero SCSS files in src/app/ contain hardcoded hex color values; all use design tokens or CSS custom properties
- Why it matters: Hardcoded colors bypass theming, break dark mode, and create inconsistency
- Source: user
- Primary owning slice: M001/S03
- Supporting slices: none
- Validation: unmapped
- Notes: 62 files currently contain hex colors
R007 — Hardcoded rgba() elimination¶
- Class: quality-attribute
- Status: active
- Description: Zero SCSS files in src/app/ contain raw rgba() calls; all use token-based opacity helpers or CSS custom properties
- Why it matters: Same as R006 — raw rgba() values are not theme-aware
- Source: user
- Primary owning slice: M001/S03
- Supporting slices: none
- Validation: unmapped
- Notes: 24 files currently use rgba()
R008 — Inline style attribute elimination¶
- Class: quality-attribute
- Status: active
- Description: Zero templates in src/app/ use hardcoded color/spacing values in style= attributes; dynamic styles use [ngStyle] or [style.prop] with token values
- Why it matters: Inline styles override the cascade and cannot be themed
- Source: user
- Primary owning slice: M001/S03
- Supporting slices: none
- Validation: unmapped
- Notes: 33 templates currently have style= attributes; some may be legitimate dynamic bindings — audit before removing
R009 — Design token system completeness¶
- Class: core-capability
- Status: active
- Description: A complete set of SCSS design tokens (colors, spacing, typography, elevation) exists and is documented, serving as the single source for all style values
- Why it matters: Tokens are the foundation that all other requirements depend on for consistency
- Source: inferred
- Primary owning slice: M001/S02
- Supporting slices: M001/S03
- Validation: unmapped
- Notes: M3 emits
--mat-sys-*properties automatically; we extend with SyRF-specific tokens for spacing, custom colors, etc.
R010 — Stylelint CI-blocking enforcement¶
- Class: quality-attribute
- Status: active
- Description:
npm run lint:stylesruns Stylelint with rules that reject hardcoded colors, rgba(), font-size px values, and non-token spacing; this command is CI-blocking - Why it matters: Without enforcement, design debt will re-accumulate within weeks
- Source: user
- Primary owning slice: M001/S04
- Supporting slices: none
- Validation: unmapped
- Notes: Use
stylelint-declaration-strict-valueplugin; allowtransparent,inherit,currentColor,0,noneas non-token values
R011 — Aspirational M3 dark mode¶
- Class: differentiator
- Status: active
- Description: An M3-based dark mode theme exists and can be toggled; exact visual parity with M2 dark theme is not required
- Why it matters: Dark mode is table-stakes UX; M3 makes it simpler than M2 via
color-scheme: light dark - Source: user
- Primary owning slice: M001/S05
- Supporting slices: none
- Validation: unmapped
- Notes: M3 defaults are fine; no need to hand-tune dark palette
R012 — Design guidelines documentation¶
- Class: operability
- Status: active
- Description: A design guidelines document exists in
docs/explaining the token system, how to add new components, and how to resolve Stylelint violations - Why it matters: Without docs, new developers will either bypass the system or get stuck
- Source: inferred
- Primary owning slice: M001/S05
- Supporting slices: none
- Validation: unmapped
- Notes: Lives in docs/ per project convention
R013 — Storybook component catalog¶
- Class: launchability
- Status: active
- Description: Storybook 10 serves a catalog of shared/core components with their states, themed correctly with the M3 SyRF theme
- Why it matters: A living catalog makes the design system tangible and prevents style drift
- Source: user
- Primary owning slice: M001/S06
- Supporting slices: none
- Validation: unmapped
- Notes: Start with shared/core components (~20-30 stories); use official Angular Material Storybook recipe
R014 — Chromatic visual regression CI¶
- Class: continuity
- Status: active
- Description: Chromatic runs in CI on PRs, capturing snapshots of Storybook stories and flagging visual regressions
- Why it matters: Automated visual regression catches styling bugs that unit tests miss
- Source: user
- Primary owning slice: M001/S06
- Supporting slices: none
- Validation: unmapped
- Notes: Enable TurboSnap from day one to manage snapshot costs
R015 — Layout integrity after migration¶
- Class: core-capability
- Status: active → validated
- Description: No containers overflow, no buttons or form fields are clipped/hidden, no z-index collisions are introduced by the M3 migration
- Why it matters: Modernised look is fine; broken layout is not
- Source: user
- Primary owning slice: M001/S02
- Supporting slices: M001/S03
- Validation: S02/T03 — 15 layout integrity tests pass across 5 public routes (zero-size, clipping, off-screen checks)
- Notes: Covers public routes only; authenticated routes need Auth0 mock
R016 — Layout integrity test suite¶
- Class: core-capability
- Status: active → validated
- Description: Playwright tests on key routes verify no interactive elements are zero-sized, clipped by overflow:hidden parents, positioned off-screen, or covered by z-index collisions
- Why it matters: Layout breakage is the primary risk of M3 migration — automated checks catch regressions that visual inspection misses
- Source: user
- Primary owning slice: M001/S02
- Supporting slices: M001/S03
- Validation: unmapped
- Notes: Custom page.evaluate() checks; run on every PR as part of e2e suite
R017 — Accessibility baseline (axe-core WCAG 2.1 AA)¶
- Class: quality-attribute
- Status: active
- Description: axe-core runs against key routes in CI, enforcing WCAG 2.1 AA compliance — color contrast, ARIA roles, keyboard navigability, focus management
- Why it matters: Accessibility is the closest automated proxy for "good UI/UX"; M3 migration changes contrast ratios and interactive element dimensions
- Source: inferred
- Primary owning slice: M001/S06
- Supporting slices: none
- Validation: unmapped
- Notes: Use @axe-core/playwright; start with zero-violation baseline on key routes, expand over time
R018 — Visual regression baselines (Playwright screenshots)¶
- Class: continuity
- Status: active → validated (partial — public pages only)
- Description: Playwright toHaveScreenshot() baselines are captured for key routes before M3 migration begins; subsequent slices compare against these baselines to catch unintended visual changes
- Why it matters: Visual regression testing catches broken layouts, overlapping elements, and wrong colors that functional tests miss
- Source: user
- Primary owning slice: M001/S01
- Supporting slices: M001/S02, M001/S03
- Validation: S01/T03 — 5 public page baselines captured (home, mission, protocols, faq, contact-us); Playwright tests pass; npm scripts for regression testing and baseline updates added. Authenticated route baselines deferred (requires Auth0 mock).
- Notes: Baselines cover shared chrome (nav, footer, layout) via public pages. Authenticated route coverage requires Auth0 mock setup — can be added in S02+ when layout integrity tests are written.
Validated¶
R001 — Dead code and unused component removal¶
- Validated by: S01/T01
- Proof: 19 dead components (75 files) removed after class name + selector reference scan across all .ts and .html files; production build passes; re-scan confirms zero unreferenced components
R002 — Bootstrap class and SCSS shim removal¶
- Validated by: S01/T02
- Proof: All Bootstrap classes replaced with Angular Material directives and scoped action-/theme- component styles; legacy-bootstrap/ directory deleted (3 files); audit script metric 3 (Bootstrap classes) = 0; production build passes
R003 — M2 to M3 global theme migration¶
- Validated by: S02/T01
- Proof: syrf-theme.scss rewritten with mat.define-theme() + mat.theme(); zero M2 API references; production build passes
R004 — SyRF brand palette on M3 color system¶
- Validated by: S02/T01
- Proof: _m3-palettes.scss generated from #203457; loading logo renders in correct navy; --mat-sys-primary visible in DevTools
R005 — Component theme mixin migration¶
- Validated by: S02/T02
- Proof: All 10 mixins use var(--mat-sys-*); grep for m2- returns nothing; dead _sidenav-theme.import.scss deleted
R015 — Layout integrity after migration¶
- Validated by: S02/T03
- Proof: 15 layout integrity tests pass across 5 public routes
R016 — Layout integrity test suite¶
- Validated by: S02/T03
- Proof: e2e/layout-integrity/public-pages.spec.ts with 3 checks × 5 routes; pnpm e2e:layout runs them
R018 — Visual regression baselines (Playwright screenshots) — partial¶
- Validated by: S01/T03, updated S02/T03
- Proof: 5 public page baselines captured and updated for M3 appearance; Playwright tests pass.
Deferred¶
D001 — OnPush change detection migration¶
- Class: quality-attribute
- Status: deferred
- Description: Migrate remaining 180 components from Default to OnPush change detection
- Why it matters: OnPush improves rendering performance and enforces immutable data patterns
- Source: inferred
- Primary owning slice: none
- Supporting slices: none
- Validation: unmapped
- Notes: Behavioral change, not a styling concern — deferred to avoid scope creep
D002 — Visual parity with M2 theme¶
- Class: constraint
- Status: deferred
- Description: Pixel-for-pixel matching of the current M2 theme appearance
- Why it matters: Explicitly deferred — the goal is modernisation, not preservation of exact M2 look
- Source: user
- Primary owning slice: none
- Supporting slices: none
- Validation: n/a
- Notes: User confirmed: "the whole point is to modernise" — only layout integrity matters, not visual parity
Out of Scope¶
X001 — Custom design mockups or Figma integration¶
- Class: constraint
- Status: out-of-scope
- Description: No custom mockups or Figma token sync — M3 defaults with SyRF brand colors are the target
- Why it matters: Prevents scope creep into design tool integration
- Source: user
- Primary owning slice: none
- Supporting slices: none
- Validation: n/a
- Notes: User confirmed M3 defaults + SyRF brand is sufficient
Traceability¶
| ID | Class | Status | Primary owner | Supporting | Proof |
|---|---|---|---|---|---|
| R001 | quality-attribute | validated | M001/S01 | none | S01/T01 — 19 dead components removed; build passes |
| R002 | quality-attribute | validated | M001/S01 | none | S01/T02 — all Bootstrap classes replaced; shims deleted; audit=0 |
| R003 | core-capability | validated | M001/S02 | none | S02 — M3 theme with HCT palettes |
| R004 | core-capability | validated | M001/S02 | none | S02 — brand palette renders correctly |
| R005 | core-capability | validated | M001/S02 | none | S02 — all 10 mixins use CSS vars |
| R006 | quality-attribute | validated | M001/S03 | none | S03 — zero hex in app/ |
| R007 | quality-attribute | validated | M001/S03 | none | S03 — 1 legitimate rgba remains |
| R008 | quality-attribute | active | M001/S03 | none | unmapped |
| R009 | core-capability | active | M001/S02 | M001/S03 | unmapped |
| R010 | quality-attribute | active | M001/S04 | none | unmapped |
| R011 | differentiator | active | M001/S05 | none | unmapped |
| R012 | operability | active | M001/S05 | none | unmapped |
| R013 | launchability | active | M001/S06 | none | unmapped |
| R014 | continuity | active | M001/S06 | none | unmapped |
| R015 | core-capability | validated | M001/S02 | M001/S03 | S02 — 15 layout tests pass |
| R016 | core-capability | validated | M001/S02 | M001/S03 | S02 — layout test suite created |
| R017 | quality-attribute | active | M001/S06 | none | unmapped |
| R018 | continuity | validated (partial) | M001/S01 | M001/S02, M001/S03 | S01/T03 — 5 public page baselines captured; tests pass |
| D001 | quality-attribute | deferred | none | none | unmapped |
| D002 | constraint | deferred | none | none | n/a |
| X001 | constraint | out-of-scope | none | none | n/a |
Coverage Summary¶
- Active requirements: 10
- Mapped to slices: 10
- Validated: 8 (R001, R002, R003, R004, R005, R015, R016, R018)
- Unmapped active requirements: 0