← back to Screenprintedwallpaper

public/dw-header.js

131 lines

/* dw-header.js — universal DW fleet header (drop-in).
 *
 * Spec (Steve 2026-06-01): BOTH hamburgers upper-LEFT, site name CENTERED.
 *   - Public: ☰ (site nav) top-left · centered site name
 *   - Admin (logged in): a SECOND ☰ beside it (accent-tinted) for admin tools
 *
 * Drop-in:  <script src="/dw-header.js" defer></script>
 * Per-site override (set BEFORE the script loads):
 *   window.DwHeaderConfig = {
 *     siteName: 'Jute Wallpaper',           // default: derived from hostname
 *     accent:   '#9B7B4F',                  // admin-burger tint (default: --accent var or #9B7B4F)
 *     homeUrl:  '/',
 *     navLinks:   [{name:'Home',url:'/'}, {name:'About',url:'/about'}, ...],
 *     adminLinks: [{name:'Shopify admin', url:'https://admin.shopify.com', external:true}, ...],
 *   };
 * Admin reveal: ?admin=1 sets localStorage.dwAdmin=1 (stub until real auth wires in).
 *
 * Self-styled · idempotent · replaces inline ns-header + the upper-right corner-nav.
 */
(function () {
  'use strict';
  if (window.__dwHeaderLoaded) return;
  window.__dwHeaderLoaded = true;

  var CFG = window.DwHeaderConfig || {};
  var DOMAIN = location.hostname.replace(/^www\./, '');
  var STEM = DOMAIN.replace(/\..+$/, '');

  function titleCase(s) {
    return s.replace(/(wallcoverings?|wallpapers?|walls?|fabric|covering|paper|cover)/gi, ' $1')
            .replace(/\s+/g, ' ').trim()
            .replace(/\b\w/g, function (c) { return c.toUpperCase(); });
  }
  var SITE_NAME = CFG.siteName || titleCase(STEM);
  var HOME = CFG.homeUrl || '/';
  var ACCENT = CFG.accent || (getComputedStyle(document.documentElement).getPropertyValue('--accent').trim()) || '#9B7B4F';
  var NAV = CFG.navLinks || [
    { name: 'Home', url: '/' }, { name: 'About', url: '/about' },
    { name: 'Care', url: '/care.html' }, { name: 'History', url: '/history.html' },
  ];
  var ADMIN = CFG.adminLinks || [
    { name: 'Shopify admin', url: 'https://admin.shopify.com', external: true },
    { name: 'Edit hero', url: '#' }, { name: 'Analytics', url: '#' },
  ];

  // admin reveal (stub auth)
  var isAdmin = false;
  try {
    var p = new URLSearchParams(location.search);
    if (p.get('admin') === '1') localStorage.setItem('dwAdmin', '1');
    if (p.get('admin') === '0') localStorage.removeItem('dwAdmin');
    isAdmin = localStorage.getItem('dwAdmin') === '1';
  } catch (e) {}

  function esc(s) { return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;'); }
  function linksHTML(arr) {
    return arr.map(function (l) {
      if (l.onclick) return '<button type="button" data-act="' + esc(l.onclick) + '">' + esc(l.name) + '</button>';
      var ext = l.external ? ' target="_blank" rel="noopener noreferrer"' : '';
      return '<a href="' + esc(l.url) + '"' + ext + '>' + esc(l.name) + '</a>';
    }).join('');
  }

  // ---- styles ----
  var css = document.createElement('style');
  css.id = 'dw-header-styles';
  css.textContent = [
    '.dwh{position:sticky;top:0;z-index:200;display:flex;align-items:center;justify-content:space-between;padding:18px 28px;background:rgba(10,10,10,.92);backdrop-filter:blur(8px);border-bottom:1px solid rgba(245,241,232,.08)}',
    '.dwh-burgers{display:flex;align-items:center;gap:10px;flex:1 1 0}',
    '.dwh-spacer{flex:1 1 0}',
    '.dwh-name{flex:0 0 auto;text-align:center;font-family:"Cormorant Garamond","Recoleta",Georgia,serif;font-style:italic;font-weight:500;font-size:22px;letter-spacing:.01em;color:#f5f1e8;text-decoration:none;white-space:nowrap}',
    '.dwh-burger{display:flex;flex-direction:column;align-items:center;justify-content:center;width:34px;height:34px;gap:5px;background:transparent;border:0;cursor:pointer;padding:0}',
    '.dwh-burger span{display:block;width:22px;height:1.5px;background:#f5f1e8;transition:transform .2s,opacity .2s}',
    '.dwh-burger[aria-expanded="true"] span:nth-child(1){transform:translateY(6.5px) rotate(45deg)}',
    '.dwh-burger[aria-expanded="true"] span:nth-child(2){opacity:0}',
    '.dwh-burger[aria-expanded="true"] span:nth-child(3){transform:translateY(-6.5px) rotate(-45deg)}',
    '.dwh-admin span{background:' + ACCENT + '}',
    '.dwh-drawer{display:none;position:absolute;top:100%;left:18px;flex-direction:column;align-items:stretch;background:rgba(10,10,10,.97);backdrop-filter:blur(10px);border:1px solid rgba(245,241,232,.12);border-top:0;min-width:240px;padding:8px 0;margin:0;box-shadow:0 16px 40px rgba(0,0,0,.55);z-index:210}',
    '.dwh-drawer.open{display:flex}',
    '.dwh-drawer.dwh-admin-drawer{left:60px}',
    '.dwh-drawer a,.dwh-drawer button{font-family:-apple-system,system-ui,sans-serif;font-size:14px;color:#f5f1e8;text-decoration:none;background:none;border:0;cursor:pointer;padding:12px 22px;text-align:left;width:100%;border-bottom:1px solid rgba(245,241,232,.08);display:block}',
    '.dwh-drawer a:last-child,.dwh-drawer button:last-child{border-bottom:0}',
    '.dwh-drawer a:hover,.dwh-drawer button:hover{background:rgba(245,241,232,.06)}',
    '.dwh-head{padding:10px 22px 8px;font-size:10px;letter-spacing:.22em;text-transform:uppercase;color:' + ACCENT + '}',
  ].join('\n');
  document.head.appendChild(css);

  // ---- header markup ----
  var header = document.createElement('header');
  header.className = 'dwh';
  header.innerHTML =
    '<div class="dwh-burgers">' +
      '<button type="button" class="dwh-burger" id="dwhNav" aria-label="Menu" aria-controls="dwhNavDrawer" aria-expanded="false"><span></span><span></span><span></span></button>' +
      (isAdmin ? '<button type="button" class="dwh-burger dwh-admin" id="dwhAdmin" aria-label="Admin tools" aria-controls="dwhAdminDrawer" aria-expanded="false"><span></span><span></span><span></span></button>' : '') +
    '</div>' +
    '<a href="' + esc(HOME) + '" class="dwh-name">' + esc(SITE_NAME) + '</a>' +
    '<div class="dwh-spacer"></div>' +
    '<nav class="dwh-drawer" id="dwhNavDrawer" aria-label="Primary">' + linksHTML(NAV) + '</nav>' +
    (isAdmin ? '<nav class="dwh-drawer dwh-admin-drawer" id="dwhAdminDrawer" aria-label="Admin tools"><div class="dwh-head">Admin tools</div>' + linksHTML(ADMIN) + '<button type="button" data-act="logout">Sign out admin</button></nav>' : '');

  document.body.insertBefore(header, document.body.firstChild);

  // hide ANY pre-existing site header (ns-header, .header, etc.) so we never double up
  document.querySelectorAll('header').forEach(function (h) {
    if (h !== header && h.className.indexOf('dwh') === -1) h.style.display = 'none';
  });

  // ---- behaviour ----
  function drawer(id) { return document.getElementById(id); }
  function close(navId, btnId) { var d = drawer(navId), b = document.getElementById(btnId); if (d) d.classList.remove('open'); if (b) b.setAttribute('aria-expanded', 'false'); }
  function toggle(navId, btnId, otherNav, otherBtn) {
    close(otherNav, otherBtn);
    var d = drawer(navId), b = document.getElementById(btnId);
    if (!d || !b) return;
    var open = d.classList.toggle('open');
    b.setAttribute('aria-expanded', open ? 'true' : 'false');
  }
  var navBtn = document.getElementById('dwhNav');
  if (navBtn) navBtn.addEventListener('click', function () { toggle('dwhNavDrawer', 'dwhNav', 'dwhAdminDrawer', 'dwhAdmin'); });
  var admBtn = document.getElementById('dwhAdmin');
  if (admBtn) admBtn.addEventListener('click', function () { toggle('dwhAdminDrawer', 'dwhAdmin', 'dwhNavDrawer', 'dwhNav'); });

  header.addEventListener('click', function (e) {
    var act = e.target && e.target.getAttribute && e.target.getAttribute('data-act');
    if (!act) return;
    if (act === 'logout') { try { localStorage.removeItem('dwAdmin'); } catch (x) {} location.reload(); return; }
    if (typeof window[act] === 'function') { e.preventDefault(); window[act](); }
  });
  document.addEventListener('keydown', function (e) { if (e.key === 'Escape') { close('dwhNavDrawer', 'dwhNav'); close('dwhAdminDrawer', 'dwhAdmin'); } });
})();