Skip to content

Commit 125797d

Browse files
Merge pull request #30 from foundersandcoders/ui/dark-mode
feat(ui): Add dark mode support with light/dark/system themes
2 parents e028c3d + bc2a5a1 commit 125797d

31 files changed

+1807
-272
lines changed

docs/dev/features/dark-mode.md

Lines changed: 584 additions & 0 deletions
Large diffs are not rendered by default.

docs/dev/ref/palettes/README.md

Lines changed: 143 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@
22

33
## Overview
44

5-
The Rhea palette system provides workflow-specific colour schemes that automatically apply based on the current route. Each workflow (Metis, Themis, Tethys, Theia, and Rhea) has its own distinct palette.
5+
The Rhea palette system provides workflow-specific colour schemes that automatically apply based on the current route. Each workflow (Metis, Themis, Tethys, Theia, and Rhea) has its own distinct palette with both **light and dark theme variants**.
6+
7+
**Features:**
8+
- ✅ Workflow-based colour schemes
9+
- ✅ Light and dark theme support
10+
- ✅ System preference detection
11+
- ✅ Persisted user preference
12+
- ✅ Smooth theme transitions
613

714
## Architecture
815

@@ -11,24 +18,26 @@ The Rhea palette system provides workflow-specific colour schemes that automatic
1118
**Location:** `src/lib/config/palettes/`
1219

1320
All palette data is defined in TypeScript files in this directory:
14-
- `rheaPalette.ts` - Main application palette
15-
- `metisPalette.ts` - Module generator palette
16-
- `themisPalette.ts` - Course builder palette
17-
- `tethysPalette.ts` - Arc designer palette
18-
- `theiaPalette.ts` - Content manager palette
21+
- `rheaPalette.ts` / `rheaPalette.dark.ts` - Main application palette (light/dark)
22+
- `metisPalette.ts` / `metisPalette.dark.ts` - Module generator palette (light/dark)
23+
- `themisPalette.ts` / `themisPalette.dark.ts` - Course builder palette (light/dark)
24+
- `tethysPalette.ts` / `tethysPalette.dark.ts` - Arc designer palette (light/dark)
25+
- `theiaPalette.ts` / `theiaPalette.dark.ts` - Content manager palette (light/dark)
1926

2027
### Data Flow
2128

2229
```
23-
1. Palette Definitions (src/lib/config/palettes/*.ts)
30+
1. Palette Definitions (src/lib/config/palettes/*.ts + *.dark.ts)
31+
32+
2. Theme Store (src/lib/stores/themeStore.ts) - User preference & system detection
2433
25-
2. Palette Loader (src/lib/utils/palette/paletteLoader.ts)
34+
3. Palette Loader (src/lib/utils/palette/paletteLoader.ts) - Select light/dark variant
2635
27-
3. Palette Transformer (src/lib/utils/palette/paletteTransformer.ts)
36+
4. Palette Transformer (src/lib/utils/palette/paletteTransformer.ts)
2837
29-
4. CSS Variables (applied to DOM via data-palette attribute)
38+
5. CSS Variables (applied via data-palette + data-theme attributes)
3039
31-
5. Component Styles (reference CSS variables like --palette-primary)
40+
6. Component Styles (reference CSS variables like --palette-primary)
3241
```
3342

3443
## Palette Structure
@@ -104,68 +113,94 @@ The transformer converts the structured palette into flat CSS variables:
104113
| `colours.foreground.primary.midi` | `--palette-accent` | Mid-tone accent |
105114
| `colours.line.primary.colour` | `--palette-line` | Border/divider colour |
106115

116+
**Note:** These mappings apply to both light and dark theme variants. The system automatically selects the appropriate palette based on the user's theme preference.
117+
107118
## How to Add or Modify a Palette
108119

109120
### Modifying an Existing Palette
110121

111-
1. **Edit the palette file** in `src/lib/config/palettes/`
122+
1. **Edit the palette file(s)** in `src/lib/config/palettes/`
112123
```typescript
113-
// Example: Change Metis foreground colour
114-
// File: src/lib/config/palettes/metisPalette.ts
115-
124+
// Example: Change Metis foreground colour in BOTH light and dark variants
125+
126+
// File: src/lib/config/palettes/metisPalette.ts (LIGHT)
116127
export const metisPalette: MetisPalette = {
117-
// ... other properties
118128
colours: {
119-
// ... other colours
120129
foreground: {
121130
primary: {
122131
name: "teal",
123-
dark: "#096A78ff", // Change this hex value
132+
dark: "#096A78ff", // Light theme: darker shade
124133
midi: "#0E9191ff",
125134
lite: "#17B8C4ff"
126-
},
127-
// ...
135+
}
136+
}
137+
}
138+
}
139+
140+
// File: src/lib/config/palettes/metisPalette.dark.ts (DARK)
141+
export const metisPaletteDark: MetisPalette = {
142+
colours: {
143+
foreground: {
144+
primary: {
145+
name: "teal",
146+
dark: "#3FC4D8ff", // Dark theme: lighter shade for contrast
147+
midi: "#5FD4E6ff",
148+
lite: "#7FE0F0ff"
149+
}
128150
}
129151
}
130152
}
131153
```
132154

133-
2. **Update the reference CSS** in `src/lib/styles/palettes.css` (optional, for documentation)
155+
2. **Regenerate CSS** to include your changes:
156+
```bash
157+
npm run generate:palettes
158+
```
134159

135160
3. **Test the changes:**
136161
```bash
137162
npm run dev
138163
```
139-
Navigate to the relevant workflow route to see changes
164+
Navigate to the relevant workflow route and toggle between light/dark themes
140165

141166
### Adding a New Palette
142167

143-
1. **Create a new palette file:**
168+
1. **Create palette files (light and dark variants):**
144169
```typescript
145-
// File: src/lib/config/palettes/newWorkflowPalette.ts
146-
170+
// File: src/lib/config/palettes/newWorkflowPalette.ts (LIGHT)
147171
import type { PaletteStructure } from "$lib/utils/palette/paletteTypes";
148172

149173
export const newWorkflowPalette: PaletteStructure = {
150-
metadata: {
151-
requiredImprovements: []
152-
},
174+
metadata: { requiredImprovements: [] },
153175
colours: {
154-
// Define all required colour properties
155-
// See existing palettes for structure
176+
// Define all required colour properties for LIGHT theme
177+
// See existing light palettes for structure
178+
}
179+
};
180+
181+
// File: src/lib/config/palettes/newWorkflowPalette.dark.ts (DARK)
182+
export const newWorkflowPaletteDark: PaletteStructure = {
183+
metadata: { requiredImprovements: [] },
184+
colours: {
185+
// Define all required colour properties for DARK theme
186+
// Use lighter foregrounds, darker backgrounds
156187
}
157188
};
158189
```
159190

160191
2. **Register in paletteLoader.ts:**
161192
```typescript
162-
// Import the new palette
193+
// Import both variants
163194
import { newWorkflowPalette } from "$lib/config/palettes/newWorkflowPalette";
195+
import { newWorkflowPaletteDark } from "$lib/config/palettes/newWorkflowPalette.dark";
164196

165-
// Add to PALETTES object
166-
const PALETTES: Record<WorkflowName, PaletteStructure> = {
197+
// Add to PALETTES object with theme variants
198+
const PALETTES: Record<WorkflowName, Record<ThemeMode, PaletteStructure>> = {
167199
// ... existing palettes
168-
newWorkflow: newWorkflowPalette,
200+
newWorkflow: {
201+
light: newWorkflowPalette,
202+
dark: newWorkflowPaletteDark,
203+
},
169204
};
170205

171206
// Add route mapping in getWorkflowPaletteName()
@@ -179,9 +214,15 @@ The transformer converts the structured palette into flat CSS variables:
179214
export type WorkflowName = "metis" | "rhea" | "tethys" | "theia" | "themis" | "newWorkflow";
180215
```
181216

217+
4. **Update CSS generation script:**
218+
- Add palette parsing in `scripts/generatePaletteCss.js`
219+
- Include both light and dark selectors in generated CSS
220+
182221
## Using Palettes in Components
183222

184-
### Via CSS Variables
223+
### Via CSS Variables (Automatic Theme Support)
224+
225+
CSS variables automatically update when theme changes:
185226

186227
```svelte
187228
<style>
@@ -202,23 +243,80 @@ The transformer converts the structured palette into flat CSS variables:
202243
</style>
203244
```
204245

205-
### Programmatically
246+
### Programmatically (Theme-Aware)
206247

207248
```typescript
208249
import { getWorkflowPalette, generateCSSVariables } from "$lib/utils/palette/paletteLoader";
250+
import { effectiveTheme } from "$lib/stores/themeStore";
251+
import { get } from "svelte/store";
209252

210-
// Get structured palette data
211-
const metisPalette = getWorkflowPalette("metis");
253+
// Get structured palette data for current theme
254+
const theme = get(effectiveTheme); // 'light' or 'dark'
255+
const metisPalette = getWorkflowPalette("metis", theme);
212256

213-
// Generate CSS variables object
214-
const cssVars = generateCSSVariables("metis");
257+
// Generate CSS variables object for specific theme
258+
const cssVars = generateCSSVariables("metis", theme);
215259
// Returns: { '--palette-primary': '#00121Fff', ... }
216260

217261
// Generate CSS string for inline styles
218-
const cssString = generateCSSVariableString("metis");
262+
const cssString = generateCSSVariableString("metis", theme);
219263
// Returns: "--palette-primary: #00121Fff; --palette-foreground: #096A78ff; ..."
220264
```
221265

266+
## Dark Mode System
267+
268+
### Theme Store
269+
270+
The theme system provides three preference options:
271+
- **Light**: Always use light theme
272+
- **Dark**: Always use dark theme
273+
- **System**: Follow OS/browser preference (default)
274+
275+
```typescript
276+
import { themePreference, effectiveTheme } from "$lib/stores/themeStore";
277+
278+
// Set user preference
279+
themePreference.set("dark");
280+
281+
// Get effective theme (resolves "system" to actual light/dark)
282+
const theme = get(effectiveTheme); // 'light' or 'dark'
283+
```
284+
285+
### Theme Selector Component
286+
287+
The `ThemeSelector` component provides UI for theme switching:
288+
289+
```svelte
290+
<script>
291+
import ThemeSelector from "$lib/components/ui/ThemeSelector.svelte";
292+
</script>
293+
294+
<ThemeSelector />
295+
```
296+
297+
Located in navigation breadcrumb on all non-home pages.
298+
299+
### How Dark Mode Works
300+
301+
1. **User selects theme** via ThemeSelector (light/dark/system)
302+
2. **Preference persisted** to localStorage
303+
3. **System preference detected** via `matchMedia('prefers-color-scheme: dark')`
304+
4. **Effective theme computed** (resolves "system" to light/dark)
305+
5. **Layout applies attributes**: `data-palette="metis" data-theme="dark"`
306+
6. **CSS selectors match**: `[data-palette="metis"][data-theme="dark"]`
307+
7. **Variables update**: All `--palette-*` variables switch to dark variants
308+
8. **Components re-render** with new colours automatically
309+
310+
### Dark Palette Design Guidelines
311+
312+
When creating dark palette variants:
313+
- **Backgrounds**: Use darker tones (15-25% lightness)
314+
- **Foregrounds**: Use lighter, brighter colours (70-90% lightness)
315+
- **Contrast**: Maintain 4.5:1 minimum contrast ratio (WCAG AA)
316+
- **Subtle backgrounds**: 5-10% lighter than main background
317+
- **Lines/borders**: Use mid-tones visible on dark backgrounds
318+
- **Identity**: Preserve workflow colour identity (hue consistency)
319+
222320
## Reference Files
223321

224322
The `.ts` files in `docs/dev/ref/palettes/` are **historical references only** and are no longer used by the application. The source of truth is now `src/lib/config/palettes/`.
@@ -230,6 +328,8 @@ The `.ts` files in `docs/dev/ref/palettes/` are **historical references only** a
230328
3. **Consistent Mapping:** All palettes follow the same structure for predictable CSS variable generation
231329
4. **Type Safety:** TypeScript interfaces ensure palette definitions are complete and correct
232330
5. **Separation of Concerns:** Data (config) is separate from transformation logic (utils)
331+
6. **Theme Consistency:** Light and dark variants maintain workflow identity while optimizing for readability
332+
7. **Accessibility First:** All colour combinations meet WCAG contrast requirements
233333

234334
## Benefits of This Approach
235335

@@ -239,6 +339,8 @@ The `.ts` files in `docs/dev/ref/palettes/` are **historical references only** a
239339
-**Easy to modify:** Change colours in one place, see results everywhere
240340
-**Maintainable:** Clear separation between data and logic
241341
-**Documented:** Rich structure includes colour names and metadata
342+
-**Dark mode ready:** Full light/dark theme support with system preference detection
343+
-**User control:** Persisted theme preference with smooth transitions
242344

243345
## Common Tasks
244346

0 commit comments

Comments
 (0)