Design System Consolidation — Decision Log¶
Purpose: Records the 10 key decisions made during plan development (v1–v14) with rationale and alternatives considered. Delete this file after decisions are incorporated into implementation PRs.
Plan file: docs/features/design-system-consolidation.md (v23)
Q1: Cleanup first or M3 migration first?¶
Decision: Cleanup first, M3 later.
Rationale: The codebase has ~80 hardcoded colors, ~42 !important hacks, ~61 inline styles, 3 legacy Bootstrap CSS files, and ~20 dead MDC selectors. Attempting M3 migration on top of this debt would compound risk — each hardcoded value becomes a potential regression. The M3 migration plan (docs/features/material-3-migration-plan.md) explicitly lists these as prerequisites.
Alternatives considered: Big-bang M3 migration (rejected — too much risk on a codebase with this much tech debt).
Q2: What to do with the custom app-select component?¶
Decision: Delete it — confirmed dead code.
Rationale: The select/ directory (10 files) under shared/form-controls/ has only 2 imports, both dead:
- annotation-experiment-question.component.ts:38 imports SelectComponent as a @ViewChild type, but no <app-select> exists in the template.
- annotation-form.service.ts:74 imports Option class but never uses it.
No template references <app-select> anywhere in the codebase.
Alternatives considered: Refactoring to use as a thin wrapper (rejected — no consumers exist).
Q3: What to do with FlexLayout (~450+ directive usages)?¶
Decision: Defer migration — document scope only.
Rationale: @ngbracket/ngx-layout v20 works on Angular 21. The ~450+ fx*/gd* directive usages across ~120 files represent a large migration that is not blocking M3 or design token adoption. The consolidation plan documents the scope in Phase 9 (docs/features/flexlayout-migration-scope.md) for a separate multi-PR initiative.
Alternatives considered: Include in this plan (rejected — would double the plan scope without blocking benefit).
Q4: Merge the 2 confirmation dialogs or keep separate?¶
Decision: Merge into one unified ConfirmationDialog.
Rationale: ConfirmationDialog and InfoConfirmationDialog share ~80% of their API surface. The differences are: (a) InfoConfirmation adds text, listItems, informationBox content modes and an actionText field; (b) cancel returns null vs false. The unified dialog accepts both okText and actionText (resolves as actionText ?? okText ?? 'OK'), supports all content modes, and standardizes cancel to return null. The 10 InfoConfirmation consumers use truthiness checks, so null (falsy) works identically to false (falsy). Three strict-switch consumers must add disableClose: true to prevent undefined from backdrop/Escape.
Alternatives considered: Keep separate (rejected — maintaining two nearly-identical dialog components with divergent close semantics is a maintenance hazard).
Q5: Refactor annotation controls or leave as-is?¶
Decision: Document the architecture, don't refactor.
Rationale: The annotation controls use an Adapter/Wrapper pattern (AbstractAnnotationControl → per-type wrappers → Material components). Investigation showed this architecture is sound — it handles focus management, type coercion, and form integration correctly. Refactoring would risk breaking annotation workflows with no design-system benefit.
Alternatives considered: Flatten wrappers directly to Material components (rejected — wrappers provide necessary abstraction for annotation-specific behavior).
Q6: CSS custom properties or SCSS variables for design tokens?¶
Decision: SCSS variables for now.
Rationale: The M2 theme API uses Sass mixins and mat.m2-get-color-from-palette(), which operate at compile time. CSS custom properties would require runtime resolution, creating a mismatch. When we migrate to M3 (which uses CSS custom properties natively via --mat-sys-*), the design tokens can be updated to reference M3 properties (Phase 4 of the M3 migration plan).
Alternatives considered: CSS custom properties now (rejected — would fight the M2 compile-time theme system; better to align with M3 migration).
Q7: Invest in dark theme improvements?¶
Decision: Preserve current dark theme, don't invest.
Rationale: Dark theme exists (.global-dark-theme class toggle) and works with the current M2 system. Several component themes already handle it correctly. Investing in dark theme improvements now would be wasted effort — M3 auto-generates dark theme from the same tonal palette, so improvements would need to be redone during M3 migration.
Alternatives considered: Fix dark theme gaps now (rejected — M3 migration will supersede all dark theme work).
Q8: Storybook scope and Chromatic tier?¶
Decision: Tiered approach — ~25 hand-crafted Tier 1 stories, ~170 AI-generated Tier 2 stories. Chromatic free tier (5,000 snapshots/month).
Rationale: Tier 1 stories (dialogs, form controls, shared UI components) are crafted to cover edge cases and interaction states — these provide the visual regression safety net for Bootstrap removal and token adoption. Tier 2 stories (domain components) are shallow render-only stories generated with AI assistance to expand coverage. Chromatic free tier provides 5,000 snapshots/month, which covers both tiers with headroom.
Alternatives considered: Full hand-crafted stories for all ~198 components (rejected — diminishing returns; Tier 2 shallow stories catch layout regressions adequately). No Storybook (rejected — manual visual testing for ~14 PRs of style changes is error-prone).
Q9: How to replace hardcoded pixel values?¶
Decision: AI-assisted comprehensive pixel replacement in batched PRs.
Rationale: The codebase has hundreds of hardcoded px values in margin, padding, gap, font-size, border-radius properties. Manual replacement is tedious but mechanical. Splitting into 4 PRs (colors → spacing shared/core → spacing domain → inline styles + !important) keeps each PR reviewable. The 4px grid snap approach (round to nearest token) keeps the process systematic.
Alternatives considered: Automated codemod (rejected — determining semantic intent of each value requires context that a codemod can't reliably infer). Leave off-grid values as-is (rejected — would leave partial adoption).
Q10: What to do with dead MDC selectors?¶
Decision: Remove them in a dedicated PR.
Rationale: ~20 selectors in styles.scss target pre-MDC internal classes (.mat-form-field-wrapper, .mat-form-field-flex, .mat-form-field-infix, .mat-form-field-underline, .mat-form-field-ripple, .mat-form-field-subscript-wrapper, .mat-form-field-disabled, .mat-form-field-invalid, .mat-input-wrapper, .mat-input-underline, .mat-button-wrapper, .mat-checkbox-layout). These classes no longer exist in Angular Material v21's MDC-based DOM — the overrides are silently inert. Removing them reduces noise and prevents confusion about what actually affects rendering.
Alternatives considered: Replace with MDC equivalents (rejected — these were global overrides that shouldn't be needed if components use proper theming). Leave as-is (rejected — dead code obscures the actual styling).