Skip to content

Commit 89e1d41

Browse files
authored
feat: add robust theme switcher
(#41) * feat: add robust runtime theme switching * Implement feature X to enhance user experience and optimize performance * pr review * updates from pr revew * fix: address theme switcher review comments * fix: resolve blocking issue with remote Bootswatch metadata enrichment and add regression tests
1 parent 0723537 commit 89e1d41

73 files changed

Lines changed: 3123 additions & 118 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Specification Quality Checklist: Robust Theme Switcher
2+
3+
**Purpose**: Validate specification completeness and quality before proceeding to planning
4+
**Created**: 2026-04-13
5+
**Feature**: [spec.md](../spec.md)
6+
7+
## Content Quality
8+
9+
- [x] No implementation details (languages, frameworks, APIs)
10+
- [x] Focused on user value and business needs
11+
- [x] Written for non-technical stakeholders
12+
- [x] All mandatory sections completed
13+
14+
## Requirement Completeness
15+
16+
- [x] No [NEEDS CLARIFICATION] markers remain
17+
- [x] Requirements are testable and unambiguous
18+
- [x] Success criteria are measurable
19+
- [x] Success criteria are technology-agnostic (no implementation details)
20+
- [x] All acceptance scenarios are defined
21+
- [x] Edge cases are identified
22+
- [x] Scope is clearly bounded
23+
- [x] Dependencies and assumptions identified
24+
25+
## Feature Readiness
26+
27+
- [x] All functional requirements have clear acceptance criteria
28+
- [x] User scenarios cover primary flows
29+
- [x] Feature meets measurable outcomes defined in Success Criteria
30+
- [x] No implementation details leak into specification
31+
32+
## Notes
33+
34+
- Validation passed on first iteration. The spec keeps external catalog behavior at the outcome level and avoids prescribing a specific implementation approach.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Contract: Theme Catalog and Selection
2+
3+
## Purpose
4+
5+
Define the contract between the theme selector UI, theme runtime state, local curated catalog, and optional Bootswatch metadata mapping.
6+
7+
## Internal Catalog Contract
8+
9+
Each supported theme must be normalized into the following shape before the selector or `ThemeContext` consumes it.
10+
11+
```ts
12+
type ThemeCatalogContract = {
13+
version: string;
14+
themes: ThemeOptionContract[];
15+
};
16+
17+
type ThemeOptionContract = {
18+
id: string;
19+
name: string;
20+
description: string;
21+
source: "first-party" | "bootswatch";
22+
isDefault: boolean;
23+
isSupported: boolean;
24+
stylesheetHref: string;
25+
previewUrl?: string;
26+
thumbnailUrl?: string;
27+
colorModeHint: "light" | "dark" | "mixed";
28+
order: number;
29+
};
30+
```
31+
32+
## Bootswatch API Mapping Contract
33+
34+
When Bootswatch API data is consumed during curation or refresh, only the fields required by the app contract are mapped forward.
35+
36+
| Bootswatch API field | Internal field | Notes |
37+
| ----- | ----- | ----- |
38+
| `name` | `name` | Display name preserved |
39+
| derived slug | `id` | Normalized stable identifier |
40+
| `description` | `description` | Shown in selector cards |
41+
| `thumbnail` | `thumbnailUrl` | Optional selector thumbnail |
42+
| `preview` | `previewUrl` | Optional external preview reference |
43+
| curated stylesheet path | `stylesheetHref` | Resolved to the app's hosted static asset, not the remote API URL |
44+
45+
The following Bootswatch fields are not required for runtime correctness in the first implementation: `css`, `cssMin`, `cssCdn`, `scss`, `scssVariables`, RTL variants.
46+
47+
## Theme Selection Contract
48+
49+
### Input
50+
51+
- `themeId: string`
52+
53+
### Output
54+
55+
- Active stylesheet target updated
56+
- Persisted `ThemePreference.themeId`
57+
- Active-theme state updated for UI affordances
58+
59+
### Rules
60+
61+
1. Selecting a valid supported theme must apply and persist in the same interaction.
62+
2. Selecting an invalid or unsupported theme ID must resolve to the default theme.
63+
3. Failure to enrich metadata must not block selection of supported themes.
64+
4. Failure to resolve a non-default theme asset must recover to the default theme and expose a user-safe error state.
65+
66+
## UI Contract
67+
68+
The dedicated selector must receive enough data to:
69+
70+
- display the active theme clearly,
71+
- distinguish first-party and external themes,
72+
- render optional preview metadata when available,
73+
- present only supported themes as selectable,
74+
- remain usable on mobile and desktop layouts.
75+
76+
Minimum selector view model:
77+
78+
```ts
79+
type ThemeSelectorViewModel = {
80+
activeThemeId: string;
81+
themes: ThemeOptionContract[];
82+
status: "loading" | "ready" | "fallback" | "error";
83+
errorMessage?: string;
84+
};
85+
```
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Data Model: Robust Theme Switcher
2+
3+
## ThemeOption
4+
5+
Represents one officially supported theme in the selector and runtime theme system.
6+
7+
### Fields
8+
9+
| Field | Type | Required | Description |
10+
| ----- | ---- | -------- | ----------- |
11+
| `id` | string | Yes | Stable internal identifier used for persistence, routing state, and stylesheet lookup |
12+
| `name` | string | Yes | User-facing display name |
13+
| `description` | string | Yes | Short summary shown in the selector |
14+
| `source` | `first-party` or `bootswatch` | Yes | Indicates whether the theme is maintained by BootstrapSpark or mapped from Bootswatch |
15+
| `isDefault` | boolean | Yes | Marks the canonical BootstrapSpark fallback theme |
16+
| `isSupported` | boolean | Yes | Marks whether the theme is selectable in production |
17+
| `stylesheetHref` | string | Yes | Path to the static CSS asset that should be applied when the theme is active |
18+
| `previewUrl` | string | No | Optional preview reference derived from Bootswatch metadata |
19+
| `thumbnailUrl` | string | No | Optional thumbnail image used in selector cards |
20+
| `colorModeHint` | `light` or `dark` or `mixed` | Yes | Helps the selector group themes and choose fallback affordances |
21+
| `order` | number | Yes | Stable display ordering in curated lists |
22+
23+
### Validation Rules
24+
25+
- `id` must be unique within the supported catalog.
26+
- Exactly one `ThemeOption` must have `isDefault = true`.
27+
- All selectable themes must have `isSupported = true` and a valid `stylesheetHref`.
28+
- Unsupported or experimental themes may exist in source metadata but must not be exposed in the selector.
29+
30+
## ThemePreference
31+
32+
Represents the user's persisted selection.
33+
34+
### Fields
35+
36+
| Field | Type | Required | Description |
37+
| ----- | ---- | -------- | ----------- |
38+
| `themeId` | string | Yes | Selected `ThemeOption.id` |
39+
| `savedAt` | string | No | ISO timestamp of the last persisted update |
40+
| `catalogVersion` | string | No | Optional version marker used to detect stale selections against a changed catalog |
41+
42+
### Validation Rules
43+
44+
- `themeId` must resolve to a current supported theme before activation.
45+
- Invalid or stale preferences fall back to the single default theme.
46+
47+
## ThemeCatalog
48+
49+
Represents the validated collection of `ThemeOption` records used by the application.
50+
51+
### Fields
52+
53+
| Field | Type | Required | Description |
54+
| ----- | ---- | -------- | ----------- |
55+
| `version` | string | Yes | Version or revision identifier for the curated catalog |
56+
| `themes` | ThemeOption[] | Yes | Ordered supported theme list |
57+
| `generatedFrom` | string | No | Notes whether metadata originated from Bootswatch API mapping, manual curation, or mixed sources |
58+
59+
### Relationships
60+
61+
- One `ThemeCatalog` contains many `ThemeOption` records.
62+
- One `ThemePreference` references exactly one valid `ThemeOption` by `themeId`.
63+
64+
## ThemeLoadState
65+
66+
Represents the active runtime state managed by `ThemeContext`.
67+
68+
### Fields
69+
70+
| Field | Type | Required | Description |
71+
| ----- | ---- | -------- | ----------- |
72+
| `activeTheme` | ThemeOption | Yes | Currently applied theme |
73+
| `catalog` | ThemeCatalog | Yes | Current validated supported list |
74+
| `status` | `idle` or `loading` or `ready` or `fallback` or `error` | Yes | State for startup and failure handling |
75+
| `errorMessage` | string | No | User-safe message when enrichment or asset loading fails |
76+
77+
### State Transitions
78+
79+
1. `idle``loading` when the app initializes theme data.
80+
2. `loading``ready` when the catalog validates and the active theme asset is applied.
81+
3. `loading``fallback` when optional enrichment fails but the local catalog remains usable.
82+
4. `loading``error` only if both catalog resolution and default-theme recovery fail.
83+
5. `ready`/`fallback``loading` when a user selects a different theme.
84+
6. `loading``ready` when the new theme asset applies and the preference is persisted.
85+
86+
## ThemeSupportMatrix
87+
88+
Represents the checklist of application surfaces that must remain valid under each supported theme.
89+
90+
### Coverage Dimensions
91+
92+
- Shared shell: header, footer, skip link, navigation, update notification
93+
- Primary routes: home, about, projects, articles, joke, weather, community
94+
- Showcase routes: components, advanced-components, data-tables
95+
- Site demos: SaaS dashboard, team collaboration, product catalog
96+
- UI states: hover, focus, active, disabled, alerts, cards, forms, dropdowns, tables
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
gate: analyze
3+
status: pass
4+
blocking: false
5+
severity: info
6+
summary: "Artifacts are aligned after task-plan mitigation updates; requirements, plan, and tasks now consistently cover fallback, rollback, responsive validation, and performance evidence."
7+
---
8+
9+
# Specification Analysis Report
10+
11+
No blocking consistency issues remain after the artifact updates.
12+
13+
## Coverage Summary Table
14+
15+
| Requirement Key | Has Task? | Task IDs | Notes |
16+
| --- | --- | --- | --- |
17+
| dedicated-theme-switching-experience | Yes | T020, T022, T023, T024 | Core selector workflow is covered. |
18+
| safe-default-theme-experience | Yes | T005, T007, T008, T010, T013 | Default recovery is covered. |
19+
| bootstrapspark-first-class-option | Yes | T005, T006, T020 | Covered in catalog and selector tasks. |
20+
| identifying-information-per-theme | Yes | T005, T007, T018, T020, T024 | Covered through catalog metadata and selector presentation. |
21+
| immediate-theme-activation | Yes | T008, T011, T013, T015, T020 | Covered by runtime and integration tasks. |
22+
| apply-and-persist-same-interaction | Yes | T010, T011, T012, T013, T014, T015 | Covered by provider and preference tasks. |
23+
| recover-invalid-saved-preferences | Yes | T007, T008, T010, T012, T013, T014 | Covered by fallback and validation tasks. |
24+
| local-catalog-when-external-metadata-unavailable | Yes | T007, T012, T024 | Functional and automated validation coverage exist. |
25+
| preserve-readability-usability-across-supported-themes | Yes | T025, T026, T028, T029, T030 | Covered by audit and regression tasks. |
26+
| preserve-clear-interactive-states | Yes | T017, T025, T026, T028, T029, T030 | Covered through regression and remediation phases. |
27+
| accessible-active-theme-and-switching-affordance | Yes | T016, T017, T020, T023, T024 | Covered by header, selector UI, and tests. |
28+
| mobile-desktop-theme-browsing-support | Yes | T019, T021, T024, T031 | Explicit responsive coverage exists. |
29+
| bounded-curated-supported-theme-list | Yes | T005, T007, T018 | Covered by catalog and contract tasks. |
30+
| review-and-correct-theme-related-markup-styling | Yes | T028, T029, T030 | Covered by route and shell remediation tasks. |
31+
| supported-theme-set-covers-shell-primary-showcase-demo-surfaces | Yes | T025, T026, T028, T029, T030, T031 | Covered by verification and review tasks. |
32+
| stylesheet-load-failure-recovery | Yes | T008, T012, T024 | Explicit rollback coverage exists. |
33+
| pre-release-fallback-responsive-validation | Yes | T012, T019, T027, T031 | Release validation coverage exists. |
34+
| capture-switch-latency-evidence | Yes | T027, T031, T035 | Performance evidence capture exists. |
35+
36+
**Constitution Alignment Issues:**
37+
38+
- No constitution alignment issues remain in the current artifact set.
39+
40+
**Unmapped Tasks:**
41+
42+
- None that appear accidental. T001-T003 and T029-T031 are legitimate setup/polish tasks that support multiple requirements rather than mapping to exactly one requirement.
43+
44+
**Metrics:**
45+
46+
- Total Requirements: 18
47+
- Total Tasks: 35
48+
- Coverage % (requirements with >=1 task): 100%
49+
- Ambiguity Count: 0
50+
- Duplication Count: 0
51+
- Critical Issues Count: 0
52+
53+
## Next Actions
54+
55+
- Proceed with implementation against the updated task plan.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
gate: critic
3+
status: pass
4+
blocking: false
5+
severity: info
6+
summary: "The design remains viable and the strengthened task plan now explicitly covers failure-path resilience, stylesheet rollback, responsive selector validation, and measurable switch-latency evidence."
7+
---
8+
9+
## Technical Risk Assessment
10+
11+
**Analysis Date:** 2026-04-13
12+
**Risk Posture:** YELLOW
13+
**Detected Stack:** TypeScript + React 19 + Vite 7 + React Router 7 + Bootstrap 5.3/Bootswatch + browser localStorage
14+
15+
### Executive Summary
16+
17+
The overall architecture remains reasonable for the stated scope: local catalog ownership, precompiled theme assets, and optional Bootswatch enrichment are the right high-level decisions. The previously identified planning risks have been mitigated in the updated spec, plan, and tasks, so implementation can proceed without known blocking design risks.
18+
19+
### Showstopper Risks (Must Fix Before Implementation)
20+
21+
| ID | Category | Location | Risk Description | Likely Impact | Mitigation Required |
22+
| --- | --- | --- | --- | --- | --- |
23+
| None | None | N/A | No constitution-level showstoppers were identified in the current artifacts. | N/A | N/A |
24+
25+
### Critical Risks (High Probability of Costly Issues)
26+
27+
| ID | Category | Location | Risk Description | Likely Impact | Recommended Action |
28+
| --- | --- | --- | --- | --- | --- |
29+
| None | None | N/A | No critical pre-implementation risks remain after the artifact updates. | N/A | N/A |
30+
31+
### High-Priority Concerns
32+
33+
| ID | Category | Location | Issue | Impact | Suggestion |
34+
| --- | --- | --- | --- | --- | --- |
35+
| HP1 | Accessibility | spec.md (FR-011), tasks.md (T017, T024) | Accessibility validation is now planned through keyboard-navigation testing and active-theme status messaging, but practical implementation still needs careful attention. | The selector could still meet task coverage while missing some assistive-technology nuance. | Verify screen-reader wording and focus visibility during implementation. |
36+
37+
### Framework-Specific Red Flags
38+
39+
Node.js + React/Vite web checklist:
40+
41+
- [x] TypeScript strict mode is planned and constitutionally required.
42+
- [x] No new server-side connection pool or async I/O hazards are introduced by this feature.
43+
- [x] Theme asset load failure handling is explicitly planned.
44+
- [x] Responsive selector validation is explicitly planned.
45+
- [x] Measurable performance validation for theme switching is explicitly planned.
46+
- [x] Accessibility validation for the new selector interaction is explicitly planned.
47+
48+
### Architecture Red Flags
49+
50+
- [ ] Over-engineered for stated requirements
51+
- [ ] Under-engineered for implied validation burden
52+
- [ ] Single point of failure without redundancy
53+
- [ ] Missing standard patterns for problem domain
54+
- [ ] Inadequate async/concurrency handling
55+
56+
Notes:
57+
58+
- The stylesheet-swap path still deserves careful implementation, but rollback behavior and validation are now explicitly part of the plan.
59+
60+
### Missing Critical Tasks
61+
62+
- **Observability:** Theme-switch timing evidence is now captured in planned validation work.
63+
- **Operations:** Packaged theme asset validation is now planned.
64+
- **Testing:** Failure-path, responsive, performance, and accessibility validation tasks are now planned.
65+
- **Documentation:** Quickstart validation notes are planned; no additional permanent documentation need is known yet.
66+
- **Security:** No additional security gaps were identified beyond the existing requirement to keep CSP synchronized if runtime origins change.
67+
68+
### Questionable Assumptions
69+
70+
1. **"Rollback behavior will work without dedicated implementation care."** → Why this can still fail: stylesheet load events are easy to mishandle if link replacement and status updates are not sequenced correctly.
71+
2. **"Keyboard coverage alone guarantees accessibility quality."** → Why this can still fail: visible focus treatment and status phrasing still need implementation-level review.
72+
73+
### Dependencies Risk Assessment
74+
75+
| Dependency | Concern | Alternative to Consider |
76+
| --- | --- | --- |
77+
| Bootswatch 5.3 package assets | Asset path drift or packaging mismatch can break runtime stylesheet lookup. | Lock exact package version and validate built asset presence in CI or quickstart checks. |
78+
| Bootswatch API metadata | Optional enrichment can still introduce schema drift or partial metadata gaps. | Keep strict Zod mapping and prefer local catalog fields when metadata is absent or invalid. |
79+
| localStorage | Invalid or stale persisted values can create confusing startup behavior. | Version stored preference objects and test stale/unknown theme IDs explicitly. |
80+
81+
### Estimated Technical Debt at Launch
82+
83+
- **Code Debt:** Selector/runtime fallback logic may need refactoring once real failure cases are exercised.
84+
- **Operational Debt:** Deployment-time asset verification is currently manual.
85+
- **Documentation Debt:** Quickstart can hold execution notes, but no permanent documentation update is yet planned if theming becomes a maintained subsystem.
86+
- **Testing Debt:** Failure-path, responsive, accessibility, and measurable performance coverage are all lighter than the spec implies.
87+
88+
### Metrics
89+
90+
- Showstopper Count: 0
91+
- Critical Risk Count: 0
92+
- Missing Operational Tasks: 0
93+
- Underspecified Security Requirements: 0
94+
- Scale Bottlenecks Identified: 1
95+
96+
**GO/NO-GO RECOMMENDATION:**
97+
98+
```text
99+
[ ] STOP - Showstoppers present, cannot proceed to implementation
100+
[ ] CONDITIONAL - Fix critical risks first, then reassess
101+
[x] PROCEED WITH CAUTION - Document acknowledged risks, add mitigation tasks
102+
```
103+
104+
**Required Actions Before Implementation:**
105+
106+
1. Implement the planned fallback, rollback, responsive, and performance validation tasks carefully.
107+
2. Confirm the selector's accessibility wording and focus treatment during implementation.

0 commit comments

Comments
 (0)