dark mode
Some checks failed
build-image / build-and-push (push) Has been cancelled

This commit is contained in:
C-West8
2026-07-22 16:10:21 -05:00
parent a0e6a32b5f
commit 352f60fad4
6 changed files with 121 additions and 3 deletions

View File

@@ -19,6 +19,33 @@
--teal: #007d79;
--gray-tag: #e0e0e0;
--focus: #0f62fe;
/* The top bar stays dark in both themes; a dedicated token keeps it
independent of --text, which flips between light and dark modes. */
--header-bg: #161616;
color-scheme: light;
}
:root[data-theme='dark'] {
--bg: #161616;
--layer: #262626;
--layer-hover: #333333;
--border: #393939;
--border-strong: #6f6f6f;
--text: #f4f4f4;
--text-secondary: #c6c6c6;
--text-helper: #a8a8a8;
--interactive: #4589ff;
--interactive-hover: #6ea6ff;
--danger: #fa4d56;
--success: #42be65;
--warning-bg: #302915;
--warning: #f1c21b;
--purple: #a56eff;
--teal: #3ddbd9;
--gray-tag: #393939;
--focus: #4589ff;
--header-bg: #0b0b0b;
color-scheme: dark;
}
* {

View File

@@ -4,6 +4,18 @@
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script>
// Apply the saved (or system) theme before first paint to avoid a flash.
(function () {
try {
var t = localStorage.getItem('theme');
if (t !== 'light' && t !== 'dark') {
t = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
document.documentElement.dataset.theme = t;
} catch (e) {}
})();
</script>
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">

View File

@@ -0,0 +1,73 @@
<script lang="ts">
import { onMount } from 'svelte';
type Theme = 'light' | 'dark';
let theme = $state<Theme>('light');
onMount(() => {
theme = (document.documentElement.dataset.theme as Theme) === 'dark' ? 'dark' : 'light';
});
function toggle() {
theme = theme === 'dark' ? 'light' : 'dark';
document.documentElement.dataset.theme = theme;
try {
localStorage.setItem('theme', theme);
} catch {
// storage may be unavailable (private mode); toggle still works for the session
}
}
</script>
<button
class="theme-toggle"
type="button"
onclick={toggle}
aria-label={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
title={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
>
{#if theme === 'dark'}
<!-- Sun: shown in dark mode to switch to light -->
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
<circle cx="12" cy="12" r="4.2" fill="currentColor" />
<g stroke="currentColor" stroke-width="1.6" stroke-linecap="round">
<line x1="12" y1="2" x2="12" y2="4.6" />
<line x1="12" y1="19.4" x2="12" y2="22" />
<line x1="2" y1="12" x2="4.6" y2="12" />
<line x1="19.4" y1="12" x2="22" y2="12" />
<line x1="4.9" y1="4.9" x2="6.7" y2="6.7" />
<line x1="17.3" y1="17.3" x2="19.1" y2="19.1" />
<line x1="4.9" y1="19.1" x2="6.7" y2="17.3" />
<line x1="17.3" y1="6.7" x2="19.1" y2="4.9" />
</g>
</svg>
{:else}
<!-- Moon: shown in light mode to switch to dark -->
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
<path
d="M20.5 14.2A8.2 8.2 0 0 1 9.8 3.5a8.2 8.2 0 1 0 10.7 10.7Z"
fill="currentColor"
/>
</svg>
{/if}
</button>
<style>
.theme-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: transparent;
color: #fff;
border: 1px solid #6f6f6f;
padding: 0;
}
.theme-toggle:hover {
background: #353535;
}
.theme-toggle svg {
display: block;
}
</style>

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import { TOOLS } from '$lib/defaults';
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
</script>
<svelte:head><title>Project SDE | Meeting Toolkit</title></svelte:head>
@@ -7,6 +8,7 @@
<header>
<div class="brand"><strong>Project SDE</strong> <span>| Meeting Toolkit</span></div>
<div class="spacer"></div>
<ThemeToggle />
</header>
<main>
@@ -34,7 +36,7 @@
position: sticky;
top: 0;
z-index: 30;
background: var(--text);
background: var(--header-bg);
color: #fff;
padding: 0 24px;
display: flex;

View File

@@ -4,6 +4,7 @@
import { toast } from '$lib/toast.svelte';
import Toast from '$lib/components/Toast.svelte';
import SyncBadge from '$lib/components/SyncBadge.svelte';
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
import {
DECISION_OWNERS,
DECISION_STATUSES,
@@ -223,6 +224,7 @@
<SyncBadge status={ws.status} updatedAt={ws.updatedAt} />
<button class="hdr-btn" onclick={reset}>Reset all</button>
<button class="hdr-btn primary" onclick={exportSummary}>Export meeting summary</button>
<ThemeToggle />
</header>
<nav>
@@ -407,7 +409,7 @@
position: sticky;
top: 0;
z-index: 30;
background: var(--text);
background: var(--header-bg);
color: #fff;
padding: 0 24px;
display: flex;

View File

@@ -4,6 +4,7 @@
import { toast } from '$lib/toast.svelte';
import Toast from '$lib/components/Toast.svelte';
import SyncBadge from '$lib/components/SyncBadge.svelte';
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
import { PEOPLE, SUGGESTIONS } from '$lib/defaults/workshop';
import { suggestBreadcrumb, DOMAIN_OPTIONS } from '$lib/breadcrumbs';
import type { Breadcrumb, Cluster, Problem, WorkshopState } from '$lib/types';
@@ -359,6 +360,7 @@
<button class="hdr-btn" onclick={saveJson}>Save JSON</button>
<button class="hdr-btn" onclick={reset}>Reset</button>
<button class="hdr-btn primary" onclick={exportSummary}>Export summary</button>
<ThemeToggle />
<input type="file" accept=".json,application/json" bind:this={fileInput} onchange={loadJson} style="display:none" />
</header>
@@ -605,7 +607,7 @@
position: sticky;
top: 0;
z-index: 30;
background: var(--text);
background: var(--header-bg);
color: #fff;
padding: 0 24px;
display: flex;