@@ -39,6 +39,7 @@ Tailwind v4 compiles `bg-gray-950` → `background-color: var(--color-gray-950)`
3939## Task 1: Color math helpers
4040
4141** Files:**
42+
4243- Create: ` src/lib/settings/color.ts `
4344- Test: ` src/lib/settings/color.test.ts `
4445
@@ -113,12 +114,7 @@ export function parseHex(hex: string): RGB {
113114}
114115
115116export function toHex(rgb : RGB ): string {
116- return (
117- ' #' +
118- rgb
119- .map ((c ) => clampChannel (c ).toString (16 ).padStart (2 , ' 0' ))
120- .join (' ' )
121- );
117+ return ' #' + rgb .map ((c ) => clampChannel (c ).toString (16 ).padStart (2 , ' 0' )).join (' ' );
122118}
123119
124120/** Linear blend: t=0 returns a, t=1 returns b. */
@@ -151,6 +147,7 @@ git commit -m "feat(theme): add hex color math helpers"
151147## Task 2: Theme model, presets, and resolution
152148
153149** Files:**
150+
154151- Create: ` src/lib/settings/theme.ts `
155152- Test: ` src/lib/settings/theme.test.ts `
156153
@@ -418,6 +415,7 @@ git commit -m "feat(theme): add theme presets, token derivation, and active-them
418415## Task 3: Settings type, defaults, migration, and barrel export
419416
420417** Files:**
418+
421419- Modify: ` src/lib/settings/settings.ts ` (type ~ 133, defaults ~ 251, migration ~ 426, end of file)
422420- Test: ` src/lib/settings/settings.test.ts ` (create)
423421
@@ -426,9 +424,9 @@ git commit -m "feat(theme): add theme presets, token derivation, and active-them
426424In ` src/lib/settings/settings.ts ` , in the ` Settings ` type, immediately after the ` backgroundColor: string; ` line (currently line 133), add:
427425
428426``` ts
429- backgroundColor : string ;
430- theme : string ; // preset id ('dark' | 'eink' | 'paper' | 'sepia' | 'nord' | 'custom')
431- customTheme : import (' ./theme' ).CustomTheme ; // edited by the Custom theme mode
427+ backgroundColor : string ;
428+ theme : string ; // preset id ('dark' | 'eink' | 'paper' | 'sepia' | 'nord' | 'custom')
429+ customTheme : import (' ./theme' ).CustomTheme ; // edited by the Custom theme mode
432430```
433431
434432- [ ] ** Step 2: Add the defaults**
@@ -456,26 +454,26 @@ In `defaultSettings`, immediately after the `backgroundColor: '#030712',` line (
456454In ` migrateProfiles ` , after the ` migratedProfile.catalogSettings = { ... } ` block and before the ` // Add timestamp if missing ` comment (currently ~ line 430), add:
457455
458456``` ts
459- // Theme migration. The `...defaultSettings, ...profile` spread above already
460- // applies `theme`/`customTheme` defaults or carries existing values forward.
461- migratedProfile .customTheme = {
462- ... defaultSettings .customTheme ,
463- ... (profile .customTheme || {})
464- };
465- // Legacy: a profile predating themes that deliberately changed the reader
466- // background keeps its look via a seeded Custom theme.
467- if (
468- profile .theme === undefined &&
469- typeof profile .backgroundColor === ' string' &&
470- profile .backgroundColor !== defaultSettings .backgroundColor
471- ) {
472- migratedProfile .theme = ' custom' ;
473- migratedProfile .customTheme = {
474- ... migratedProfile .customTheme ,
475- base: ' dark' ,
476- background: profile .backgroundColor
477- };
478- }
457+ // Theme migration. The `...defaultSettings, ...profile` spread above already
458+ // applies `theme`/`customTheme` defaults or carries existing values forward.
459+ migratedProfile .customTheme = {
460+ ... defaultSettings .customTheme ,
461+ ... (profile .customTheme || {})
462+ };
463+ // Legacy: a profile predating themes that deliberately changed the reader
464+ // background keeps its look via a seeded Custom theme.
465+ if (
466+ profile .theme === undefined &&
467+ typeof profile .backgroundColor === ' string' &&
468+ profile .backgroundColor !== defaultSettings .backgroundColor
469+ ) {
470+ migratedProfile .theme = ' custom' ;
471+ migratedProfile .customTheme = {
472+ ... migratedProfile .customTheme ,
473+ base: ' dark' ,
474+ background: profile .backgroundColor
475+ };
476+ }
479477```
480478
481479- [ ] ** Step 4: Import theme helpers and define the active-theme store**
@@ -571,6 +569,7 @@ git commit -m "feat(theme): store theme per-profile with backgroundColor migrati
571569## Task 4: ThemeController and global CSS wiring
572570
573571** Files:**
572+
574573- Create: ` src/lib/components/ThemeController.svelte `
575574- Modify: ` src/app.css ` (after the ` @theme { ... } ` block)
576575- Modify: ` src/app.html ` (` <body> ` class, line 39)
@@ -650,13 +649,13 @@ body {
650649In ` src/app.html ` , change the ` <body> ` tag (line 39) from:
651650
652651``` html
653- <body data-sveltekit-preload-data =" hover" class =" bg-white dark:bg-gray-950 dark:text-white" >
652+ <body data-sveltekit-preload-data =" hover" class =" bg-white dark:bg-gray-950 dark:text-white" ></ body >
654653```
655654
656655to:
657656
658657``` html
659- <body data-sveltekit-preload-data =" hover" class =" dark:text-white" >
658+ <body data-sveltekit-preload-data =" hover" class =" dark:text-white" ></ body >
660659```
661660
662661(The ` bg-white dark:bg-gray-950 ` classes are removed because ` body { background-color: var(--app-bg) } ` now owns the canvas color across all themes.)
@@ -668,8 +667,8 @@ In `src/routes/+layout.svelte`:
668667Add the import alongside the other component imports (after the ` NightModeFilter ` import on line 18):
669668
670669``` ts
671- import NightModeFilter from ' $lib/components/NightModeFilter.svelte' ;
672- import ThemeController from ' $lib/components/ThemeController.svelte' ;
670+ import NightModeFilter from ' $lib/components/NightModeFilter.svelte' ;
671+ import ThemeController from ' $lib/components/ThemeController.svelte' ;
673672```
674673
675674Change the main content wrapper (line 117) from:
@@ -687,13 +686,14 @@ to:
687686Add the controller next to ` <NightModeFilter /> ` (line 123):
688687
689688``` svelte
690- <NightModeFilter />
691- <ThemeController />
689+ <NightModeFilter />
690+ <ThemeController />
692691```
693692
694693- [ ] ** Step 5: Manual verification**
695694
696695Run: ` npm run dev `
696+
697697- App still loads in Dark (default) with no visible change vs. before.
698698- In the browser console: ` document.documentElement.style.setProperty('--app-bg', '#ffffff'); document.documentElement.classList.remove('dark') ` → canvas turns white, text/components switch to light styling. Re-add ` dark ` and reset to confirm reversibility.
699699
@@ -709,6 +709,7 @@ git commit -m "feat(theme): apply themes via ThemeController and canvas backgrou
709709## Task 5: Reader viewport follows the theme
710710
711711** Files:**
712+
712713- Modify: ` src/lib/components/Reader/Reader.svelte:1386 `
713714- Modify: ` src/lib/components/Reader/VerticalScrollReader.svelte:573 `
714715- Modify: ` src/lib/components/Reader/HorizontalScrollReader.svelte:635 `
@@ -732,27 +733,27 @@ to:
732733Change line 573 from:
733734
734735``` svelte
735- style:background-color={$settings.backgroundColor}
736+ style:background-color={$settings.backgroundColor}
736737```
737738
738739to:
739740
740741``` svelte
741- style:background-color="var(--reader-bg)"
742+ style:background-color="var(--reader-bg)"
742743```
743744
744745- [ ] ** Step 3: HorizontalScrollReader.svelte**
745746
746747Change line 635 from:
747748
748749``` svelte
749- style:background-color={$settings.backgroundColor}
750+ style:background-color={$settings.backgroundColor}
750751```
751752
752753to:
753754
754755``` svelte
755- style:background-color="var(--reader-bg)"
756+ style:background-color="var(--reader-bg)"
756757```
757758
758759- [ ] ** Step 4: Verify**
@@ -771,25 +772,26 @@ git commit -m "feat(theme): drive reader viewport background from theme"
771772## Task 6: Remove the migrated background-color control
772773
773774** Files:**
775+
774776- Modify: ` src/lib/components/Settings/Reader/ReaderSelects.svelte ` (fn ~ 52, control ~ 95-96)
775777
776778- [ ] ** Step 1: Remove the control markup**
777779
778780Delete these two lines (currently 95-96):
779781
780782``` svelte
781- <Label class="text-gray-900 dark:text-white">Background color:</Label>
782- <Input type="color" onchange={onBackgroundColor} value={$settings.backgroundColor} />
783+ <Label class="text-gray-900 dark:text-white">Background color:</Label>
784+ <Input type="color" onchange={onBackgroundColor} value={$settings.backgroundColor} />
783785```
784786
785787- [ ] ** Step 2: Remove the now-unused handler**
786788
787789Delete the function (currently lines 52-54):
788790
789791``` ts
790- function onBackgroundColor(event : Event ) {
791- updateSetting (' backgroundColor' , (event .target as HTMLInputElement ).value );
792- }
792+ function onBackgroundColor(event : Event ) {
793+ updateSetting (' backgroundColor' , (event .target as HTMLInputElement ).value );
794+ }
793795```
794796
795797- [ ] ** Step 3: Check for unused imports**
@@ -809,6 +811,7 @@ git commit -m "feat(theme): drop standalone reader background-color control (mig
809811## Task 7: Appearance settings UI
810812
811813** Files:**
814+
812815- Create: ` src/lib/components/Settings/AppearanceSettings.svelte `
813816- Modify: ` src/lib/components/Settings/Settings.svelte ` (import + mount in the Accordion)
814817
@@ -928,16 +931,16 @@ git commit -m "feat(theme): drop standalone reader background-color control (mig
928931In ` src/lib/components/Settings/Settings.svelte ` , add the import after the ` QuickAccess ` import:
929932
930933``` ts
931- import QuickAccess from ' ./QuickAccess.svelte' ;
932- import AppearanceSettings from ' ./AppearanceSettings.svelte' ;
934+ import QuickAccess from ' ./QuickAccess.svelte' ;
935+ import AppearanceSettings from ' ./AppearanceSettings.svelte' ;
933936```
934937
935938Add the section to the ` <Accordion> ` after ` <CatalogSettings /> ` and before ` <Stats /> ` :
936939
937940``` svelte
938- <CatalogSettings />
939- <AppearanceSettings />
940- <Stats />
941+ <CatalogSettings />
942+ <AppearanceSettings />
943+ <Stats />
941944```
942945
943946- [ ] ** Step 3: Type-check**
@@ -948,6 +951,7 @@ Expected: No errors in `AppearanceSettings.svelte`.
948951- [ ] ** Step 4: Manual verification (the core of this feature)**
949952
950953Run: ` npm run dev ` . Open Settings → Appearance:
954+
951955- Click each preset (Dark, E-ink, Paper, Sepia, Nord) → entire UI re-colors live: navbar, drawer, accordion, buttons, toggles, range sliders, the catalog, and the reader viewport.
952956- Click ** Custom…** → editor appears seeded from the active preset; changing each of the six swatches and the base toggle updates the UI live.
953957- Switch profiles → theme follows the profile.
@@ -973,7 +977,7 @@ Light themes expose components that hardcode light-on-dark colors with **no ligh
973977Run:
974978
975979``` bash
976- grep -rn " text-white" src/lib/components src/routes | grep -v " dark:text-white" | grep -v " bg-primary\|bg-blue\|bg-green\|bg-red\|hover:"
980+ grep -rn " text-white" src/lib/components src/routes | grep -v " dark:text-white" | grep -v " bg-primary\|bg-blue\|bg-green\|bg-red\|hover:"
977981```
978982
979983and
@@ -1008,6 +1012,7 @@ git commit -m "fix(theme): add light-mode color counterparts where missing"
10081012## Task 9: Full verification and changelog
10091013
10101014** Files:**
1015+
10111016- Modify: changelog file (check repo for ` CHANGELOG.md ` or the pattern used by recent releases, e.g. commit ` 1a50b3d ` ).
10121017
10131018- [ ] ** Step 1: Test suite**
@@ -1033,6 +1038,7 @@ Expected: Builds successfully.
10331038- [ ] ** Step 5: Full manual matrix**
10341039
10351040` npm run preview ` , then for each preset (Dark, E-ink, Paper, Sepia, Nord) and one Custom config, with night mode both OFF and ON:
1041+
10361042- Catalog grid + list, series view, reader (page + continuous scroll modes), settings drawer, and at least one modal (e.g. Volume Editor) — confirm legible, correctly themed, buttons clickable.
10371043- Confirm a legacy profile with a custom ` backgroundColor ` (simulate by editing localStorage ` profiles ` to add ` backgroundColor: "#123456" ` and removing ` theme ` , then reload) migrates to a Custom theme preserving that background.
10381044
0 commit comments