/* ============================================================ VYNDR 2.0 — preferences store (§9 i18n + §10 a11y). Plain CommonJS so the modal/applier import it (allowJs) AND Jest tests the apply/load/save logic with a fake element + fake storage. The CSS layer already exists (Session 33 added the overrides to globals.css). This module SETS those attributes. ============================================================ */ const PREFS_KEY = 'vyndr_prefs'; const DEFAULTS = { lang: 'en', region: 'US', odds: 'american', currency: 'USD', motion: 'on', // 'on' | 'reduced' contrast: 'off', // 'off' | 'high' text: 'base', // 'sm' | 'base' | 'lg' | 'xl' cb: 'off', // 'off' | 'on' (colorblind-safe) font: 'default', // 'default' | 'readable' }; /** * Apply preferences to a document element by setting/removing the data-* * attributes the CSS layer keys off. `el` defaults to document.documentElement; * tests pass a fake element exposing setAttribute/removeAttribute. */ function applyPrefs(prefs, el) { const target = el || (typeof document !== 'undefined' ? document.documentElement : null); if (!target) return; const p = { ...DEFAULTS, ...(prefs || {}) }; const setOrRemove = (attr, value) => { if (value == null) target.removeAttribute(attr); else target.setAttribute(attr, value); }; setOrRemove('data-motion', p.motion === 'reduced' ? 'reduced' : null); setOrRemove('data-contrast', p.contrast === 'high' ? 'high' : null); setOrRemove('data-text', p.text && p.text !== 'base' ? p.text : null); setOrRemove('data-cb', p.cb === 'on' || p.cb === '1' ? '1' : null); setOrRemove('data-font', p.font === 'readable' ? 'readable' : null); } /** Read persisted prefs merged over defaults. `storage` defaults to localStorage. */ function loadPrefs(storage) { const store = storage || (typeof localStorage !== 'undefined' ? localStorage : null); if (!store) return { ...DEFAULTS }; try { const raw = store.getItem(PREFS_KEY); if (!raw) return { ...DEFAULTS }; return { ...DEFAULTS, ...JSON.parse(raw) }; } catch { return { ...DEFAULTS }; } } /** Persist prefs (merged over defaults). Returns the merged object. */ function savePrefs(prefs, storage) { const store = storage || (typeof localStorage !== 'undefined' ? localStorage : null); const merged = { ...DEFAULTS, ...(prefs || {}) }; if (store) { try { store.setItem(PREFS_KEY, JSON.stringify(merged)); } catch { /* ignore quota / privacy-mode failures */ } } return merged; } module.exports = { PREFS_KEY, DEFAULTS, applyPrefs, loadPrefs, savePrefs };