Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions design/prototype/assets/theme-fouc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// FOUC prevention — resolve theme synchronously before paint.
(function () {
try {
var choice = localStorage.getItem('label-suite-theme') || 'system';
var resolved = (choice === 'dark') ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', resolved);
} catch (e) {
document.documentElement.setAttribute('data-theme', 'light');
}
})();
59 changes: 59 additions & 0 deletions design/prototype/assets/theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* global window, document */
(function bootstrapLabelSuiteTheme(windowObj, documentObj) {
const STORAGE_KEY = 'label-suite-theme';
const VALID_CHOICES = ['system', 'light', 'dark'];
const DEFAULT_CHOICE = 'system';
const listeners = new Set();

function getStoredChoice() {
try {
const value = windowObj.localStorage.getItem(STORAGE_KEY);
return VALID_CHOICES.indexOf(value) >= 0 ? value : DEFAULT_CHOICE;
} catch (_err) {
return DEFAULT_CHOICE;
}
}

function resolveChoice(choice) {
return choice === 'dark' ? 'dark' : 'light';
}

function applyResolved(resolved) {
documentObj.documentElement.setAttribute('data-theme', resolved);
}

function setChoice(choice) {
if (VALID_CHOICES.indexOf(choice) < 0) return;
try {
windowObj.localStorage.setItem(STORAGE_KEY, choice);
} catch (_err) {
// localStorage unavailable (private mode etc) — apply for this session only
}
const resolved = resolveChoice(choice);
applyResolved(resolved);
listeners.forEach((cb) => {
try { cb({ choice: choice, resolved: resolved }); } catch (_err) { /* ignore */ }
});
}

function onChange(callback) {
if (typeof callback === 'function') {
listeners.add(callback);
return function unsubscribe() { listeners.delete(callback); };
}
return function noop() {};
}

// Apply on script load (the inline <head> snippet has already set
// data-theme to prevent FOUC; this is a no-op if values already match).
applyResolved(resolveChoice(getStoredChoice()));

windowObj.LabelSuiteTheme = {
STORAGE_KEY: STORAGE_KEY,
VALID_CHOICES: VALID_CHOICES.slice(),
getStoredChoice: getStoredChoice,
getResolved: function () { return resolveChoice(getStoredChoice()); },
setChoice: setChoice,
onChange: onChange,
};
})(window, document);
74 changes: 74 additions & 0 deletions design/prototype/assets/tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
--color-white: #FFFFFF;
--color-ink: #1E1B4B; /* Indigo 950 — body text */
--color-primary-soft-bg: #EEF2FF;
--color-primary-border: #C7D2FE; /* Indigo 200 */

/* ── Supporting ───────────────────────────────────────────── */
--color-border: #E2E8F0; /* slate-200 */
Expand Down Expand Up @@ -118,8 +119,81 @@
--fg-3: var(--color-text-muted);
--bg-page: var(--color-surface);
--bg-surface: var(--color-white);

/* Nav-active background — overridden in dark mode so the active pill
reads as "raised" (lighter than navbar) instead of fading into bg. */
--nav-active-bg: var(--color-surface);
}

/* ═══════════════════════════════════════════════════════════════
Dark Theme — applied via <html data-theme="dark">
Strategy: re-map the same token names so every page that already
uses var(--color-*) flips automatically. No HTML changes needed.

Specificity note: existing prototype pages redefine these tokens
inside inline <style>{ :root { ... } } blocks. `:root` has
specificity (0,1,0); `html[data-theme="dark"]` has (0,1,1), so the
dark overrides win regardless of source order.
═══════════════════════════════════════════════════════════════ */
html[data-theme="dark"] {
/* ── Core palette ─────────────────────────────────────────── */
--color-primary: #818CF8; /* indigo-400, brighter on dark */
--color-secondary: #A5B4FC; /* indigo-300 */
--color-cta: #34D399; /* emerald-400 */
--color-cta-hover: #10B981;
--color-surface: #0B0B12; /* page bg, near-black */
--color-white: #16161F; /* card / sidebar bg, raised 1 step */
--color-ink: #E2E8F0; /* slate-200, primary text */
--color-primary-soft-bg: #1E1B4B; /* indigo-950 */
--color-primary-border: #3730A3; /* Indigo 800 */

/* ── Supporting ───────────────────────────────────────────── */
--color-border: #2A2A35;
--color-border-muted: #1F1F28;
--color-text-muted: #9CA3AF; /* slate-400 — raised from zinc-500 for WCAG AA */
--color-text-soft: #A1A1AA; /* zinc-400 */
--color-slate-50: #1F1F28; /* hover bg in dark */

/* Aliases */
--color-ink-muted: #9CA3AF; /* = --color-text-muted */

/* ── Semantic state — desaturated for dark backgrounds ────── */
--color-error: #F87171;
--color-error-bg: #2A1414;
--color-error-border: #5B2222;
--color-error-soft-bg: #2A1414;
--color-error-soft-border:#5B2222;

--color-success: #4ADE80;
--color-success-bg: #0F2A18;
--color-success-border: #1F5132;
--color-success-soft-bg: #0F2A18;
--color-success-soft-border: #1F5132;

--color-warning: #FACC15;
--color-warning-bg: #2A2210;
--color-warning-border: #5C4A1A;
--color-warning-soft-bg: #2A2210;
--color-warning-soft-border: #5C4A1A;

--color-info: #60A5FA;
--color-info-bg: #0F1F33;
--color-info-border: #1E3A66;

/* ── Shadows — much subtler on dark, mostly ring-style ────── */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.40);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.45);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.50);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.55);
--shadow-card: 0 0 0 1px rgba(129, 140, 248, 0.10), 0 4px 24px rgba(0, 0, 0, 0.40);

/* Nav-active pill: lighter than --color-white (#16161F) so it reads as raised */
--nav-active-bg: #2A2A35;
}

/* `data-theme` is always resolved to "light" or "dark" by theme.js.
The "system" choice is resolved client-side using matchMedia. */

/* ── Semantic type rules ──────────────────────────────────── */
html[lang="zh-TW"] body,
html[lang="zh"] body { line-height: var(--leading-body-zh); }
Expand Down
27 changes: 27 additions & 0 deletions design/prototype/pages/account/forgot-password.html
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,33 @@
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ── Dark mode overrides ──────────────────────────────────── */
html[data-theme="dark"] {
--color-background: #0B0B12;
--color-surface: #16161F;
--color-border: #2A2A35;
--color-border-focus: #818CF8;
--color-text: #E2E8F0;
--color-text-muted: #9CA3AF;
--color-link: #818CF8;
--color-primary: #818CF8;
--color-primary-light: #1E1B4B;
--color-error: #F87171;
--color-error-bg: #2A1414;
--color-error-border: #5B2222;
--color-success: #4ADE80;
--color-success-bg: #0F2A18;
--color-success-border:#1F5132;
--shadow-card: 0 0 0 1px rgba(129,140,248,0.10), 0 4px 24px rgba(0,0,0,0.40);

/* CTA button */
.submit-btn { color: #064E3B; background: #34D399; }
.submit-btn:hover { background: #10B981; }

/* Lang toggle hover */
.lang-toggle:hover { background: #1E1B4B; }
}
</style>
</head>
<body>
Expand Down
28 changes: 28 additions & 0 deletions design/prototype/pages/account/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,34 @@
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ── Dark mode overrides ──────────────────────────────────── */
html[data-theme="dark"] {
/* Re-map local token names — these pages do not import tokens.css */
--color-background: #0B0B12;
--color-surface: #16161F;
--color-border: #2A2A35;
--color-border-focus: #818CF8;
--color-text: #E2E8F0;
--color-text-muted: #9CA3AF;
--color-link: #818CF8;
--color-primary: #818CF8;
--color-primary-light: #1E1B4B;
--color-error: #F87171;
--color-error-bg: #2A1414;
--color-error-border: #5B2222;
--shadow-card: 0 0 0 1px rgba(129,140,248,0.10), 0 4px 24px rgba(0,0,0,0.40);

/* CTA button — dark bg, dark text on bright emerald for contrast */
.login-btn { color: #064E3B; background: #34D399; }
.login-btn:hover { background: #10B981; }

/* SSO button hover */
.sso-btn:hover { background: #1E1B4B; border-color: #818CF8; }

/* Lang toggle hover */
.lang-toggle:hover { background: #1E1B4B; }
}
</style>
</head>
<body>
Expand Down
Loading
Loading