feat(ms-ai-architect): playground v3 theme toggle with localStorage persistence [skip-docs]
Step 13 (Wave 5). Adds light/dark theme toggle to v3 playground. - Inline <script> in <head> reads ms-ai-architect-theme from localStorage and sets <html data-theme="..."> BEFORE stylesheets parse (avoids FOUC). - New .theme-toggle button in topbar (vendored design-system class). - ACTIONS['toggle-theme'] flips data-theme, persists to localStorage, and syncs all [data-theme-label] elements + aria-label in-place (no re-render). - Default behavior (no localStorage value or unsupported value) keeps existing data-theme="dark" hard-coded on <html>.
This commit is contained in:
parent
997acb190f
commit
bebe070236
1 changed files with 40 additions and 0 deletions
|
|
@ -5,6 +5,21 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ms-ai-architect — Playground v3</title>
|
||||
|
||||
<!-- Theme bootstrap (Step 13). Må kjøre før stylesheets parses for å unngå
|
||||
flash-of-wrong-theme (FOUC). Leser ms-ai-architect-theme fra localStorage
|
||||
og overstyrer <html data-theme="..."> før .css evaluerer.
|
||||
Wrappes i try/catch — file:// + privatmodus kan blokkere localStorage. -->
|
||||
<script>
|
||||
(function () {
|
||||
try {
|
||||
var saved = localStorage.getItem('ms-ai-architect-theme');
|
||||
if (saved === 'light' || saved === 'dark') {
|
||||
document.documentElement.setAttribute('data-theme', saved);
|
||||
}
|
||||
} catch (e) { /* localStorage utilgjengelig — behold default fra HTML-attributtet */ }
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- Vendored design-system. Kilden er shared/playground-design-system/ — synces via
|
||||
scripts/sync-design-system.mjs ved marketplace-rot. Aldri rediger filer under
|
||||
playground/vendor/ direkte; endringer går i shared/ + re-sync. -->
|
||||
|
|
@ -1456,6 +1471,9 @@
|
|||
const crumbHtml = (orgName || crumb)
|
||||
? '<span class="topbar__crumb">' + (orgName ? escapeHtml(orgName) : '') + (orgName && crumb ? ' · ' : '') + (crumb || '') + '</span>'
|
||||
: '';
|
||||
const currentTheme = document.documentElement.getAttribute('data-theme') === 'light' ? 'light' : 'dark';
|
||||
const themeLabel = currentTheme === 'light' ? 'Lys' : 'Mørk';
|
||||
const themeNext = currentTheme === 'light' ? 'mørk' : 'lys';
|
||||
return (
|
||||
'<header class="topbar">' +
|
||||
'<div class="topbar__brand">' +
|
||||
|
|
@ -1470,6 +1488,9 @@
|
|||
'<button type="button" class="btn btn--secondary btn--sm" data-action="export-state" aria-label="Eksporter state til JSON">Eksporter</button>' +
|
||||
'<button type="button" class="btn btn--secondary btn--sm" data-action="import-state" aria-label="Importer state fra JSON">Importer</button>' +
|
||||
'<input type="file" accept="application/json,.json" data-import-input hidden>' +
|
||||
'<button type="button" class="theme-toggle" data-action="toggle-theme" aria-label="Bytt til ' + themeNext + ' modus">' +
|
||||
'<span data-theme-label>' + themeLabel + '</span>' +
|
||||
'</button>' +
|
||||
'</nav>' +
|
||||
'</header>'
|
||||
);
|
||||
|
|
@ -3625,6 +3646,25 @@
|
|||
ACTIONS['goto-catalog'] = function () { navigate('catalog'); };
|
||||
ACTIONS['goto-onboarding'] = function () { navigate('onboarding'); };
|
||||
|
||||
// Theme toggle (Step 13). Veksler data-theme på <html>, persisterer i
|
||||
// localStorage('ms-ai-architect-theme'). Tar høyde for begrensning fra
|
||||
// file:// + privatmodus. Re-renderer ikke surfaces — endrer kun attributt
|
||||
// og synkroniserer alle [data-theme-label]-elementer in-place.
|
||||
ACTIONS['toggle-theme'] = function () {
|
||||
const current = document.documentElement.getAttribute('data-theme') === 'light' ? 'light' : 'dark';
|
||||
const next = current === 'dark' ? 'light' : 'dark';
|
||||
document.documentElement.setAttribute('data-theme', next);
|
||||
try { localStorage.setItem('ms-ai-architect-theme', next); } catch (e) { /* ignore */ }
|
||||
const labels = document.querySelectorAll('[data-theme-label]');
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
labels[i].textContent = next === 'dark' ? 'Mørk' : 'Lys';
|
||||
}
|
||||
const buttons = document.querySelectorAll('[data-action="toggle-theme"]');
|
||||
for (let j = 0; j < buttons.length; j++) {
|
||||
buttons[j].setAttribute('aria-label', 'Bytt til ' + (next === 'dark' ? 'lys' : 'mørk') + ' modus');
|
||||
}
|
||||
};
|
||||
|
||||
ACTIONS['open-project'] = function (ev, el) {
|
||||
const id = el.dataset.projectId;
|
||||
if (!id) return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue