Files
crumbs/docs/superpowers/plans/2026-03-20-dark-mode.md
T
Frederic CabassutandClaude Opus 4.6 57b9d89e2e docs: add dark mode implementation plan
10-task plan covering CSS variables, preferences, FOUC prevention,
reactive theme application, component updates, and testing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 08:47:20 +01:00

24 KiB

Dark Mode Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Add a dark parchment theme variant with system/light/dark toggle in Settings > Preferences.

Architecture: CSS [data-theme="dark"] attribute on <html> overrides CSS variables in app.css. A blocking inline script in app.html prevents FOUC. Theme preference uses the existing UserPreferences key-value system. A reactive isDarkMode rune in theme.svelte.ts drives note card color switching.

Tech Stack: SvelteKit, Svelte 5 runes, CSS custom properties, Tailwind CSS v4, Vitest, Playwright

Spec: docs/superpowers/specs/2026-03-20-dark-mode-design.md


File Map

Action File Responsibility
Modify src/app.css Add --grid-dot to :root, add [data-theme="dark"] block, update body::before
Modify src/app.html Add FOUC-prevention inline script
Modify src/lib/types/preferences.ts Add theme field to UserPreferences
Create src/lib/utils/theme.svelte.ts applyTheme(), reactive isDarkMode rune
Modify src/lib/utils/colors.ts Add NOTE_COLORS_DARK, export getNoteColor()
Modify src/routes/+layout.svelte Add theme $effect watcher
Modify src/routes/(app)/settings/preferences/+page.svelte Add theme segmented toggle
Modify src/lib/components/NoteCard.svelte Use getNoteColor() with isDarkMode
Modify src/lib/components/NoteEditor.svelte Use getNoteColor() with isDarkMode
Modify src/lib/components/ColorPicker.svelte Use dark color swatches when isDarkMode
Skip src/routes/(share)/s/[token]/+page.svelte Out of scope per spec (public share pages)
Modify .gitignore Add .superpowers/
Create tests/unit/theme.test.ts Unit tests for applyTheme and isDarkMode
Create tests/unit/colors.test.ts Unit tests for getNoteColor
Create tests/e2e/dark-mode.spec.ts E2E tests for theme toggle, persistence, system detection

Task 1: CSS Dark Theme Variables

Files:

  • Modify: src/app.css:6-23 (:root block) and src/app.css:36 (body::before)

  • Step 1: Add --grid-dot to :root and add [data-theme="dark"] block

In src/app.css, add --grid-dot: #1a1a2e; to the end of the :root block, then add the dark theme block after :root:

:root {
	--bg-base: #f0e6d3;
	--bg-surface: #faf5eb;
	--primary: #C8860A;
	--primary-hover: #E8A020;
	--text: #1a1a2e;
	--text-muted: #6b6272;
	--border: #1a1a2e;
	--border-subtle: #d4cabb;
	--destructive: #a63d2f;
	--error-bg: #f5e0db;
	--error-border: #d4a69e;
	--error-text: #8b2e1f;
	--success-bg: #dce8d4;
	--success-text: #3a5a2e;
	--card-shadow: 2px 2px 0px var(--border-subtle);
	--card-shadow-hover: 3px 3px 0px var(--primary);
	--grid-dot: #1a1a2e;
}

[data-theme="dark"] {
	--bg-base: #1a1715;
	--bg-surface: #2a2520;
	--text: #e8dcc8;
	--text-muted: #9a8e7e;
	--border: #e8dcc8;
	--border-subtle: #3a3530;
	--destructive: #d4604e;
	--error-bg: #3a2020;
	--error-border: #5a3030;
	--error-text: #e8a090;
	--success-bg: #203020;
	--success-text: #90c880;
	--card-shadow: 2px 2px 0px var(--border-subtle);
	--card-shadow-hover: 3px 3px 0px var(--primary);
	--grid-dot: #e8dcc8;
}
  • Step 2: Update body::before to use --grid-dot variable

Replace the hardcoded color in the body::before rule:

body::before {
	content: '';
	position: fixed;
	inset: 0;
	pointer-events: none;
	opacity: 0.03;
	background-image: repeating-conic-gradient(var(--grid-dot) 0% 25%, transparent 0% 50%);
	background-size: 4px 4px;
	z-index: 0;
}
  • Step 3: Verify by temporarily adding data-theme="dark" to app.html

In src/app.html, temporarily change <html lang="en"> to <html lang="en" data-theme="dark">. Run make dev and visually confirm dark background and light text. Then revert the change.

  • Step 4: Commit
git add src/app.css
git commit -m "feat: add dark theme CSS variables and grid-dot extraction"

Task 2: Theme Preference Type

Files:

  • Modify: src/lib/types/preferences.ts

  • Step 1: Add theme field to UserPreferences

export interface UserPreferences {
	theme: 'system' | 'light' | 'dark';
	defaultNoteMode: 'richtext' | 'markdown';
	defaultNoteColor: NoteColor;
	hideFooter: boolean;
	sidebarDefaultState: 'open' | 'collapsed';
	notifyOnShare: boolean;
	notifyOnCollabRemoved: boolean;
	notifyOnNoteDeleted: boolean;
}

export const DEFAULT_PREFERENCES: UserPreferences = {
	theme: 'system',
	defaultNoteMode: 'richtext',
	defaultNoteColor: 'default',
	hideFooter: false,
	sidebarDefaultState: 'open',
	notifyOnShare: true,
	notifyOnCollabRemoved: true,
	notifyOnNoteDeleted: true
};

Do NOT add theme to BOOLEAN_PREF_KEYS.

  • Step 2: Commit
git add src/lib/types/preferences.ts
git commit -m "feat: add theme preference type (system/light/dark)"

Task 3: Theme Utility — applyTheme and isDarkMode

Files:

  • Create: src/lib/utils/theme.svelte.ts

  • Test: tests/unit/theme.test.ts

  • Step 1: Write failing tests for theme utility

Note: theme.svelte.ts uses module-scope $state which requires the Svelte compiler. The Vitest node environment does not process .svelte.ts runes correctly, so we test only the DOM side-effects of applyTheme (attribute and meta tag changes). The reactive isDarkMode / getIsDarkMode() behavior is covered by E2E tests instead.

Create tests/unit/theme.test.ts:

import { describe, it, expect, beforeEach, vi } from 'vitest';

// Mock $app/environment
vi.mock('$app/environment', () => ({ browser: true }));

describe('applyTheme', () => {
	let applyTheme: (theme: 'system' | 'light' | 'dark') => void;

	beforeEach(async () => {
		// Reset DOM
		document.documentElement.removeAttribute('data-theme');
		const meta = document.querySelector('meta[name="theme-color"]');
		if (meta) meta.remove();
		const metaEl = document.createElement('meta');
		metaEl.setAttribute('name', 'theme-color');
		metaEl.setAttribute('content', '#f0e6d3');
		document.head.appendChild(metaEl);

		// Reset module state
		vi.resetModules();
		const mod = await import('$lib/utils/theme.svelte.js');
		applyTheme = mod.applyTheme;
	});

	it('sets data-theme="dark" for dark mode', () => {
		applyTheme('dark');
		expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
	});

	it('removes data-theme for light mode', () => {
		document.documentElement.setAttribute('data-theme', 'dark');
		applyTheme('light');
		expect(document.documentElement.hasAttribute('data-theme')).toBe(false);
	});

	it('updates meta theme-color for dark mode', () => {
		applyTheme('dark');
		const meta = document.querySelector('meta[name="theme-color"]');
		expect(meta?.getAttribute('content')).toBe('#1a1715');
	});

	it('updates meta theme-color for light mode', () => {
		applyTheme('dark');
		applyTheme('light');
		const meta = document.querySelector('meta[name="theme-color"]');
		expect(meta?.getAttribute('content')).toBe('#f0e6d3');
	});

	it('falls back to system for unknown values', () => {
		// matchMedia returns false by default in jsdom → resolves to light
		applyTheme('invalid' as 'system' | 'light' | 'dark');
		expect(document.documentElement.hasAttribute('data-theme')).toBe(false);
	});

	it('resolves system preference to dark when prefers-color-scheme is dark', () => {
		Object.defineProperty(window, 'matchMedia', {
			writable: true,
			value: vi.fn().mockImplementation((query: string) => ({
				matches: query === '(prefers-color-scheme: dark)',
				media: query,
				addEventListener: vi.fn(),
				removeEventListener: vi.fn(),
				dispatchEvent: vi.fn()
			}))
		});
		applyTheme('system');
		expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
	});
});
  • Step 2: Run tests to verify they fail

Run: pnpm vitest run tests/unit/theme.test.ts Expected: FAIL — module not found

  • Step 3: Implement theme.svelte.ts

Create src/lib/utils/theme.svelte.ts:

import { browser } from '$app/environment';

const LIGHT_THEME_COLOR = '#f0e6d3';
const DARK_THEME_COLOR = '#1a1715';

let darkMode = $state(false);

export function getIsDarkMode(): boolean {
	return darkMode;
}

export function applyTheme(theme: 'system' | 'light' | 'dark'): void {
	if (!browser) return;

	// Validate input
	if (theme !== 'system' && theme !== 'light' && theme !== 'dark') {
		theme = 'system';
	}

	// Resolve system preference
	let resolved = theme;
	if (theme === 'system') {
		resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
	}

	const isDark = resolved === 'dark';
	darkMode = isDark;

	if (isDark) {
		document.documentElement.setAttribute('data-theme', 'dark');
	} else {
		document.documentElement.removeAttribute('data-theme');
	}

	const meta = document.querySelector('meta[name="theme-color"]');
	if (meta) {
		meta.setAttribute('content', isDark ? DARK_THEME_COLOR : LIGHT_THEME_COLOR);
	}
}
  • Step 4: Run tests to verify they pass

Run: pnpm vitest run tests/unit/theme.test.ts Expected: All PASS

  • Step 5: Commit
git add src/lib/utils/theme.svelte.ts tests/unit/theme.test.ts
git commit -m "feat: add theme utility with applyTheme and isDarkMode"

Task 4: Note Card Dark Colors

Files:

  • Modify: src/lib/utils/colors.ts

  • Test: tests/unit/colors.test.ts

  • Step 1: Write failing tests for getNoteColor

Create tests/unit/colors.test.ts:

import { describe, it, expect } from 'vitest';
import { getNoteColor, NOTE_COLORS, NOTE_COLORS_DARK } from '$lib/utils/colors.js';

describe('getNoteColor', () => {
	it('returns light color when isDark is false', () => {
		expect(getNoteColor('default', false)).toBe('#faf5eb');
		expect(getNoteColor('coral', false)).toBe('#faafa8');
	});

	it('returns dark color when isDark is true', () => {
		expect(getNoteColor('default', true)).toBe('#2a2520');
		expect(getNoteColor('coral', true)).toBe('#4a2522');
	});

	it('returns default color for unknown color name', () => {
		expect(getNoteColor('unknown' as any, false)).toBe('#faf5eb');
		expect(getNoteColor('unknown' as any, true)).toBe('#2a2520');
	});

	it('has matching keys in light and dark color maps', () => {
		const lightKeys = Object.keys(NOTE_COLORS).sort();
		const darkKeys = Object.keys(NOTE_COLORS_DARK).sort();
		expect(lightKeys).toEqual(darkKeys);
	});
});
  • Step 2: Run tests to verify they fail

Run: pnpm vitest run tests/unit/colors.test.ts Expected: FAIL — getNoteColor and NOTE_COLORS_DARK not found

  • Step 3: Add dark colors and helper to colors.ts

Update src/lib/utils/colors.ts:

import type { NoteColor } from '$lib/types/index.js';

export const NOTE_COLORS: Record<NoteColor, { bg: string; label: string }> = {
	default: { bg: '#faf5eb', label: 'Default' },
	coral: { bg: '#faafa8', label: 'Coral' },
	peach: { bg: '#f39f76', label: 'Peach' },
	sand: { bg: '#fff8b8', label: 'Sand' },
	mint: { bg: '#e2f6d3', label: 'Mint' },
	sage: { bg: '#b4ddd3', label: 'Sage' },
	fog: { bg: '#d4e4ed', label: 'Fog' },
	storm: { bg: '#aeccdc', label: 'Storm' },
	dusk: { bg: '#d3bfdb', label: 'Dusk' },
	blossom: { bg: '#f6e2dd', label: 'Blossom' },
	clay: { bg: '#e9e3d4', label: 'Clay' },
	chalk: { bg: '#efeff1', label: 'Chalk' }
};

export const NOTE_COLORS_DARK: Record<NoteColor, { bg: string; label: string }> = {
	default: { bg: '#2a2520', label: 'Default' },
	coral: { bg: '#4a2522', label: 'Coral' },
	peach: { bg: '#4a3020', label: 'Peach' },
	sand: { bg: '#3a3520', label: 'Sand' },
	mint: { bg: '#2a3a22', label: 'Mint' },
	sage: { bg: '#223a32', label: 'Sage' },
	fog: { bg: '#222e3a', label: 'Fog' },
	storm: { bg: '#1e2a35', label: 'Storm' },
	dusk: { bg: '#352540', label: 'Dusk' },
	blossom: { bg: '#3a2830', label: 'Blossom' },
	clay: { bg: '#302e28', label: 'Clay' },
	chalk: { bg: '#2e2e30', label: 'Chalk' }
};

export function getNoteColor(color: NoteColor, isDark: boolean): string {
	const map = isDark ? NOTE_COLORS_DARK : NOTE_COLORS;
	return (map[color] ?? map.default).bg;
}

export const COLOR_OPTIONS = Object.entries(NOTE_COLORS).map(([value, { label }]) => ({
	value: value as NoteColor,
	label
}));
  • Step 4: Run tests to verify they pass

Run: pnpm vitest run tests/unit/colors.test.ts Expected: All PASS

  • Step 5: Commit
git add src/lib/utils/colors.ts tests/unit/colors.test.ts
git commit -m "feat: add dark note card colors and getNoteColor helper"

Task 5: FOUC Prevention Script

Files:

  • Modify: src/app.html

  • Step 1: Add inline script to app.html

Insert after the <meta name="theme-color"> tag (line 6) and before %sveltekit.head% (line 15):

    <script>
      (function() {
        try {
          var p = JSON.parse(localStorage.getItem('crumbs-preferences') || '{}');
          var t = p.theme || 'system';
          if (t !== 'light' && t !== 'dark') t = 'system';
          if (t === 'system') t = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
          if (t === 'dark') {
            document.documentElement.setAttribute('data-theme', 'dark');
            var m = document.querySelector('meta[name="theme-color"]');
            if (m) m.content = '#1a1715';
          }
        } catch(e) {}
      })();
    </script>
  • Step 2: Commit
git add src/app.html
git commit -m "feat: add FOUC-prevention script for dark mode"

Task 6: Reactive Theme in Root Layout

Files:

  • Modify: src/routes/+layout.svelte

  • Step 1: Add theme $effect to root layout

The root layout currently imports CSS and registers the PWA service worker. Add the theme $effect here so both (app) and (auth) layouts get the theme applied. Important: Keep initPreferences() in (app)/+layout.svelte — do NOT move it here. The (app) layout reads sidebarDefaultState immediately after init, and moving init to the root layout would create a race condition. The root layout only watches the preference reactively via $effect, which fires after preferences are loaded.

On auth pages (login/setup), initPreferences() hasn't been called, so prefs.theme will be 'system' (the default). The FOUC script already handles the initial theme from localStorage, and the $effect will apply the correct theme once it runs.

<script lang="ts">
	import '../app.css';
	import { onMount, onDestroy } from 'svelte';
	import { pwaInfo } from 'virtual:pwa-info';
	import { browser } from '$app/environment';
	import { getPreferences } from '$lib/stores/preferences.svelte.js';
	import { applyTheme } from '$lib/utils/theme.svelte.js';

	let { children } = $props();
	let mediaQuery: MediaQueryList | undefined;
	let systemListener: ((e: MediaQueryListEvent) => void) | undefined;

	const prefs = $derived(getPreferences());

	onMount(async () => {
		if (pwaInfo) {
			const { registerSW } = await import('virtual:pwa-register');
			registerSW({
				immediate: true,
				onRegistered(r: ServiceWorkerRegistration | undefined) {
					console.log('SW Registered:', r);
				},
				onRegisterError(error: Error) {
					console.log('SW registration error', error);
				}
			});
		}
	});

	$effect(() => {
		if (!browser) return;
		const theme = prefs.theme ?? 'system';
		applyTheme(theme);

		// Listen for system preference changes when in system mode
		if (systemListener && mediaQuery) {
			mediaQuery.removeEventListener('change', systemListener);
		}
		if (theme === 'system') {
			mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
			systemListener = () => applyTheme('system');
			mediaQuery.addEventListener('change', systemListener);
		}
	});

	onDestroy(() => {
		if (systemListener && mediaQuery) {
			mediaQuery.removeEventListener('change', systemListener);
		}
	});

	const webManifest = $derived(pwaInfo ? pwaInfo.webManifest.linkTag : '');
</script>

<svelte:head>
	{@html webManifest}
</svelte:head>

{@render children()}

Note: initPreferences() stays in (app)/+layout.svelte. The root layout only adds the theme $effect and does not call initPreferences().

  • Step 2: Verify dev server works

Run make dev, open the app, go to Settings > Preferences. The theme toggle doesn't exist yet, but the app should render without errors. Check browser console for any issues.

  • Step 4: Commit
git add src/routes/+layout.svelte src/routes/\(app\)/+layout.svelte
git commit -m "feat: add reactive theme watcher in root layout"

Task 7: Theme Toggle in Settings

Files:

  • Modify: src/routes/(app)/settings/preferences/+page.svelte

  • Step 1: Add theme segmented toggle

Add as the first preference section (before "Default note mode"):

<!-- Theme -->
<div class="space-y-2">
	<span class="block text-sm font-medium text-[var(--text)]">Theme</span>
	<div class="flex gap-1" role="group" aria-label="Theme">
		{#each [['system', 'System'], ['light', 'Light'], ['dark', 'Dark']] as [value, label]}
			<button
				onclick={() => updatePreference('theme', value)}
				class="rounded-sm border px-4 py-2 text-sm transition-colors {prefs.theme === value ? 'border-[var(--primary)] bg-[var(--primary)]/15 text-[var(--primary)] font-medium' : 'border-[var(--border-subtle)] text-[var(--text)] hover:border-[var(--primary)]'}"
				data-testid="pref-theme-{value}"
			>
				{label}
			</button>
		{/each}
	</div>
</div>
  • Step 2: Manually test

Run make dev. Go to Settings > Preferences. Click System / Light / Dark and confirm:

  • Clicking "Dark" immediately applies dark theme

  • Clicking "Light" reverts to light theme

  • Clicking "System" follows OS preference

  • Refreshing the page persists the choice (no FOUC)

  • Step 3: Commit

git add src/routes/\(app\)/settings/preferences/+page.svelte
git commit -m "feat: add theme toggle to preferences settings"

Task 8: Update Components to Use Dark Note Colors

Files:

  • Modify: src/lib/components/NoteCard.svelte:2,29-32
  • Modify: src/lib/components/NoteEditor.svelte:12,194-197
  • Modify: src/lib/components/ColorPicker.svelte

Important: getIsDarkMode() must be called inside $effect or $derived blocks for reactivity. Calling it as a top-level const would capture the value once and never update when the theme changes. Each component already has an $effect block where it computes card/bg styles — add getIsDarkMode() inside those existing blocks.

Public share page (src/routes/(share)/s/[token]/+page.svelte) is out of scope per the spec.

  • Step 1: Update NoteCard.svelte

Replace NOTE_COLORS import with getNoteColor and getIsDarkMode:

<!-- Change import at top of script -->
import { getNoteColor } from '$lib/utils/colors.js';
import { getIsDarkMode } from '$lib/utils/theme.svelte.js';

<!-- Inside the existing $effect block where cardStyle is computed (around lines 29-32): -->
$effect(() => {
	// ... existing logic ...
	const bg = getNoteColor(note.color, getIsDarkMode());
	cardStyle = `background-color: ${bg}`;
});

Remove the NOTE_COLORS import. getIsDarkMode() is called inside the $effect so it re-runs when the theme changes.

  • Step 2: Update NoteEditor.svelte

Same pattern — inside the existing $effect where bgStyle is computed:

import { getNoteColor } from '$lib/utils/colors.js';
import { getIsDarkMode } from '$lib/utils/theme.svelte.js';

<!-- Inside the existing $effect (around lines 194-197): -->
bgStyle = `background-color: ${getNoteColor(color, getIsDarkMode())}`;

Remove the NOTE_COLORS import.

  • Step 3: Update ColorPicker.svelte

The ColorPicker shows circular swatches. Use $derived for the dark mode check so swatches update reactively:

import { COLOR_OPTIONS, getNoteColor } from '$lib/utils/colors.js';
import { getIsDarkMode } from '$lib/utils/theme.svelte.js';

<!-- Use getIsDarkMode() in the template directly (it's reactive in Svelte 5 template expressions): -->
style="background-color: {getNoteColor(option.value, getIsDarkMode())}"

Remove NOTE_COLORS import if no longer needed.

  • Step 4: Verify all components render correctly in both themes

Run make dev. Toggle between light and dark in Settings > Preferences. Check:

  • Note cards show correct dark/light background colors

  • Color picker shows dark/light swatches

  • Note editor uses correct background color

  • Step 5: Commit

git add src/lib/components/NoteCard.svelte src/lib/components/NoteEditor.svelte src/lib/components/ColorPicker.svelte
git commit -m "feat: use dark note card colors in all components"

Task 9: E2E Tests

Files:

  • Create: tests/e2e/dark-mode.spec.ts

  • Step 1: Write E2E tests

Create tests/e2e/dark-mode.spec.ts:

import { test, expect } from './helpers/fixtures.js';

test.describe('Dark mode', () => {
	test('Scenario: Theme toggle persists selection across page reload', async ({
		authenticatedPage: page
	}) => {
		// Given the user is on the preferences page
		await page.goto('/settings/preferences');

		// When the user selects dark theme
		await page.getByTestId('pref-theme-dark').click();

		// Then the page applies dark theme
		await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');

		// When the page is reloaded
		await page.reload();

		// Then the dark theme persists without flash
		await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
		await expect(page.getByTestId('pref-theme-dark')).toHaveClass(/font-medium/);
	});

	test('Scenario: Light mode removes dark theme attribute', async ({
		authenticatedPage: page
	}) => {
		// Given dark theme is active
		await page.goto('/settings/preferences');
		await page.getByTestId('pref-theme-dark').click();
		await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');

		// When the user selects light theme
		await page.getByTestId('pref-theme-light').click();

		// Then the dark theme attribute is removed
		await expect(page.locator('html')).not.toHaveAttribute('data-theme');
	});

	test('Scenario: System mode follows OS dark preference', async ({
		authenticatedPage: page
	}) => {
		// Given the OS prefers dark mode
		await page.emulateMedia({ colorScheme: 'dark' });

		// When the user selects system theme
		await page.goto('/settings/preferences');
		await page.getByTestId('pref-theme-system').click();

		// Then the page applies dark theme from system preference
		// Use toHaveAttribute with timeout to handle async $effect execution
		await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark', { timeout: 5000 });
	});

	test('Scenario: Note cards use dark colors in dark mode', async ({
		authenticatedPage: page
	}) => {
		// Given dark theme is active
		await page.goto('/settings/preferences');
		await page.getByTestId('pref-theme-dark').click();

		// When viewing the notes page
		await page.goto('/');

		// Then note cards should have dark background colors
		const noteCard = page.getByTestId('note-card').first();
		if (await noteCard.isVisible()) {
			const bg = await noteCard.evaluate((el) => getComputedStyle(el).backgroundColor);
			// Dark default color is #2a2520 = rgb(42, 37, 32)
			expect(bg).not.toBe('rgb(250, 245, 235)'); // Not the light default
		}
	});
});
  • Step 2: Run E2E tests

Run: pnpm test:e2e tests/e2e/dark-mode.spec.ts Expected: All PASS

  • Step 3: Commit
git add tests/e2e/dark-mode.spec.ts
git commit -m "test: add E2E tests for dark mode toggle and persistence"

Task 10: Housekeeping and Type Check

Files:

  • Modify: .gitignore

  • Step 1: Add .superpowers/ to .gitignore

Append to .gitignore:

# Superpowers brainstorm artifacts
.superpowers/
  • Step 2: Run type check

Run: pnpm check Expected: No errors. If there are type errors, fix them.

  • Step 3: Run full test suite

Run: make test Expected: All unit and E2E tests pass.

  • Step 4: Commit
git add .gitignore
git commit -m "chore: add .superpowers/ to gitignore"