This commit is contained in:
27
src/app.css
27
src/app.css
@@ -19,6 +19,33 @@
|
|||||||
--teal: #007d79;
|
--teal: #007d79;
|
||||||
--gray-tag: #e0e0e0;
|
--gray-tag: #e0e0e0;
|
||||||
--focus: #0f62fe;
|
--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;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
|
|||||||
12
src/app.html
12
src/app.html
@@ -4,6 +4,18 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<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%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
|||||||
73
src/lib/components/ThemeToggle.svelte
Normal file
73
src/lib/components/ThemeToggle.svelte
Normal 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>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { TOOLS } from '$lib/defaults';
|
import { TOOLS } from '$lib/defaults';
|
||||||
|
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head><title>Project SDE | Meeting Toolkit</title></svelte:head>
|
<svelte:head><title>Project SDE | Meeting Toolkit</title></svelte:head>
|
||||||
@@ -7,6 +8,7 @@
|
|||||||
<header>
|
<header>
|
||||||
<div class="brand"><strong>Project SDE</strong> <span>| Meeting Toolkit</span></div>
|
<div class="brand"><strong>Project SDE</strong> <span>| Meeting Toolkit</span></div>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
|
<ThemeToggle />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
@@ -34,7 +36,7 @@
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 30;
|
z-index: 30;
|
||||||
background: var(--text);
|
background: var(--header-bg);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { toast } from '$lib/toast.svelte';
|
import { toast } from '$lib/toast.svelte';
|
||||||
import Toast from '$lib/components/Toast.svelte';
|
import Toast from '$lib/components/Toast.svelte';
|
||||||
import SyncBadge from '$lib/components/SyncBadge.svelte';
|
import SyncBadge from '$lib/components/SyncBadge.svelte';
|
||||||
|
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
|
||||||
import {
|
import {
|
||||||
DECISION_OWNERS,
|
DECISION_OWNERS,
|
||||||
DECISION_STATUSES,
|
DECISION_STATUSES,
|
||||||
@@ -223,6 +224,7 @@
|
|||||||
<SyncBadge status={ws.status} updatedAt={ws.updatedAt} />
|
<SyncBadge status={ws.status} updatedAt={ws.updatedAt} />
|
||||||
<button class="hdr-btn" onclick={reset}>Reset all</button>
|
<button class="hdr-btn" onclick={reset}>Reset all</button>
|
||||||
<button class="hdr-btn primary" onclick={exportSummary}>Export meeting summary</button>
|
<button class="hdr-btn primary" onclick={exportSummary}>Export meeting summary</button>
|
||||||
|
<ThemeToggle />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
@@ -407,7 +409,7 @@
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 30;
|
z-index: 30;
|
||||||
background: var(--text);
|
background: var(--header-bg);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { toast } from '$lib/toast.svelte';
|
import { toast } from '$lib/toast.svelte';
|
||||||
import Toast from '$lib/components/Toast.svelte';
|
import Toast from '$lib/components/Toast.svelte';
|
||||||
import SyncBadge from '$lib/components/SyncBadge.svelte';
|
import SyncBadge from '$lib/components/SyncBadge.svelte';
|
||||||
|
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
|
||||||
import { PEOPLE, SUGGESTIONS } from '$lib/defaults/workshop';
|
import { PEOPLE, SUGGESTIONS } from '$lib/defaults/workshop';
|
||||||
import { suggestBreadcrumb, DOMAIN_OPTIONS } from '$lib/breadcrumbs';
|
import { suggestBreadcrumb, DOMAIN_OPTIONS } from '$lib/breadcrumbs';
|
||||||
import type { Breadcrumb, Cluster, Problem, WorkshopState } from '$lib/types';
|
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={saveJson}>Save JSON</button>
|
||||||
<button class="hdr-btn" onclick={reset}>Reset</button>
|
<button class="hdr-btn" onclick={reset}>Reset</button>
|
||||||
<button class="hdr-btn primary" onclick={exportSummary}>Export summary</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" />
|
<input type="file" accept=".json,application/json" bind:this={fileInput} onchange={loadJson} style="display:none" />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -605,7 +607,7 @@
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 30;
|
z-index: 30;
|
||||||
background: var(--text);
|
background: var(--header-bg);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user