mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
ce1fa68c84
Adds a Special Themes row to Settings > Appearance > Theme. Halloween is a scheme on the existing data-scheme axis that is always dark: the applier and the boot script force .dark while it is on, and the color mode control is dimmed with a hint. - Palette for the whole app (surfaces, dashboard, Vacay glass, Journey, mobile chrome) in theme/special/halloween.tokens.css, loaded with index.css. - Decor as a lazy chunk mounted by SpecialThemeLayer while the scheme is on: fog, embers, bat flocks, a passing ghost, corner cobwebs, spiders that drop on a thread and flee when poked, a pumpkin patch and an ember running along the navbar. Figures are pre-rendered 3D sprite sheets. - Nothing is drawn over a map: the Atlas gets no decor, the trip planner only dresses its side panels. Marker clusters turn into lanterns. - The 3D mascot plays its own Halloween clips in every scene (witch hat, a spider on the brim, a lit pumpkin beside it); the SVG fallback wears the same costume. - Reduced motion stills everything, transparency off drops the fog, shared pages never show it. - Navbar, bottom nav, notifications, Atlas and Collections read the dark flag off the .dark class instead of settings.dark_mode, so a forced dark theme reaches them.
61 lines
2.5 KiB
JavaScript
61 lines
2.5 KiB
JavaScript
/*
|
|
* Pre-paint appearance boot — kills the flash of default/wrong theme (FOUC).
|
|
*
|
|
* Loaded as an external, render-blocking CLASSIC script in <head> (NOT a module)
|
|
* so it runs before first paint AND complies with the production CSP
|
|
* (script-src 'self'; inline scripts are blocked). It reads the compact snapshot
|
|
* written by client/src/theme/applyAppearance.ts and applies it verbatim. Keep
|
|
* this in sync with that module's snapshot shape + apply logic.
|
|
*
|
|
* It must never throw — any failure silently falls back to the default look.
|
|
*/
|
|
(function () {
|
|
try {
|
|
var raw = localStorage.getItem('trek_appearance');
|
|
if (!raw) return;
|
|
var s = JSON.parse(raw);
|
|
if (!s || s.v !== 1) return;
|
|
|
|
var root = document.documentElement;
|
|
var path = location.pathname;
|
|
var isShared = path.indexOf('/shared/') === 0 || path.indexOf('/public/') === 0;
|
|
|
|
var dark;
|
|
if (isShared) dark = false;
|
|
else if (s.darkMode === 'auto') dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
else dark = s.darkMode === true || s.darkMode === 'dark';
|
|
// Special themes (APPEARANCE_SPECIAL_SCHEMES in @trek/shared) are dark-only.
|
|
if (!isShared && s.scheme === 'halloween') dark = true;
|
|
root.classList.toggle('dark', dark);
|
|
|
|
var scheme = isShared ? 'default' : s.scheme;
|
|
if (scheme && scheme !== 'default') root.setAttribute('data-scheme', scheme);
|
|
if (!isShared && s.noTransparency) root.setAttribute('data-no-transparency', '');
|
|
if (s.density === 'compact') root.setAttribute('data-density', 'compact');
|
|
if (s.reduceMotion) root.setAttribute('data-reduce-motion', '');
|
|
|
|
if (!isShared && scheme === 'custom' && s.accent) {
|
|
root.style.setProperty('--accent-custom-light', s.accent.light);
|
|
root.style.setProperty('--accent-custom-dark', s.accent.dark);
|
|
if (s.accentText) {
|
|
root.style.setProperty('--accent-custom-text-light', s.accentText.light);
|
|
root.style.setProperty('--accent-custom-text-dark', s.accentText.dark);
|
|
}
|
|
}
|
|
|
|
var ts = s.typeScale || {};
|
|
var fs = typeof s.fontScale === 'number' ? s.fontScale : 1;
|
|
setScale('--fs-scale-title', fs * (ts.title || 1));
|
|
setScale('--fs-scale-subtitle', fs * (ts.subtitle || 1));
|
|
setScale('--fs-scale-body', fs * (ts.body || 1));
|
|
setScale('--fs-scale-caption', fs * (ts.caption || 1));
|
|
if (fs !== 1) root.style.fontSize = fs * 100 + '%';
|
|
|
|
function setScale(name, v) {
|
|
if (typeof v === 'number' && v !== 1) root.style.setProperty(name, String(v));
|
|
}
|
|
} catch (e) {
|
|
/* never block boot */
|
|
}
|
|
})();
|