All files / js theme-toggle.js

0% Statements 0/27
0% Branches 0/15
0% Functions 0/4
0% Lines 0/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78                                                                                                                                                           
/**
 * Theme toggle functionality for Riksdagsmonitor news articles.
 *
 * Reads/writes the user's preference under the same storage key
 * (`riksdagsmonitor-theme`) used by the anti-flash head snippet so that
 * the initial state, the toggle, and the CSS selector (`[data-theme]` on
 * `<html>`) are all kept in sync.
 *
 * @module js/theme-toggle
 */
(function () {
  var STORAGE_KEY = 'riksdagsmonitor-theme';
  var DARK = 'dark';
  var LIGHT = 'light';
 
  var button = document.getElementById('theme-toggle');
  if (!button) {
    return;
  }
 
  /**
   * Apply the given theme to the document and update the toggle button state.
   * Receives the button element explicitly to avoid relying on the outer-scope variable.
   *
   * @param {string} theme - 'dark' or 'light'
   * @param {HTMLElement} btn - The theme-toggle button element
   */
  function applyTheme(theme, btn) {
    document.documentElement.setAttribute('data-theme', theme);
 
    var isDark = theme === DARK;
    btn.setAttribute('aria-pressed', String(isDark));
    var label = isDark
      ? btn.getAttribute('data-label-dark')
      : btn.getAttribute('data-label-light');
    if (label) {
      btn.setAttribute('aria-label', label);
      btn.setAttribute('title', label);
    }
  }
 
  function getStoredTheme() {
    try {
      var stored = window.localStorage.getItem(STORAGE_KEY);
      if (stored === DARK || stored === LIGHT) {
        return stored;
      }
    } catch (_e) {
      // Ignore storage errors and fall back to the attribute set by the anti-flash snippet
    }
    return null;
  }
 
  // Resolve the initial theme in priority order:
  //  1. `data-theme` attribute on <html> — already set by the anti-flash head
  //     snippet before first paint; respecting it avoids a second flash.
  //  2. `localStorage` value — respects the user's explicit last choice when
  //     the anti-flash snippet could not set the attribute (e.g. JS disabled).
  //  3. Default to LIGHT if neither source provides a valid value.
  var initial = document.documentElement.getAttribute('data-theme');
  var currentTheme =
    (initial === DARK || initial === LIGHT ? initial : null) ||
    getStoredTheme() ||
    LIGHT;
 
  applyTheme(currentTheme, button);
 
  button.addEventListener('click', function () {
    currentTheme = currentTheme === DARK ? LIGHT : DARK;
    applyTheme(currentTheme, button);
    try {
      window.localStorage.setItem(STORAGE_KEY, currentTheme);
    } catch (_e) {
      // Ignore storage errors
    }
  });
}());