← back to Wallpaperdefinitions
fleet rebuild (header-only): dw-header (hamburgers UL + centered name) + accent — local branch
05ae265b48197aa2f8b0184d144d0a2ca94c0878 · 2026-06-01 13:47:37 -0700 · Steve Abrams
Files touched
A public/dw-header.jsM public/index.html
Diff
commit 05ae265b48197aa2f8b0184d144d0a2ca94c0878
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jun 1 13:47:37 2026 -0700
fleet rebuild (header-only): dw-header (hamburgers UL + centered name) + accent — local branch
---
public/dw-header.js | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++
public/index.html | 4 +-
2 files changed, 132 insertions(+), 1 deletion(-)
diff --git a/public/dw-header.js b/public/dw-header.js
new file mode 100644
index 0000000..0f10a0b
--- /dev/null
+++ b/public/dw-header.js
@@ -0,0 +1,129 @@
+/* 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, '&').replace(/</g, '<').replace(/"/g, '"'); }
+ 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>' : '');
+
+ // remove any pre-existing inline header so we don't double up
+ var old = document.querySelector('header.ns-header, header.dwh:not(:first-of-type)');
+ if (old && old.className.indexOf('dwh') === -1) old.style.display = 'none';
+
+ document.body.insertBefore(header, document.body.firstChild);
+
+ // ---- 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'); } });
+})();
diff --git a/public/index.html b/public/index.html
index 57deb46..824135e 100644
--- a/public/index.html
+++ b/public/index.html
@@ -299,7 +299,7 @@ body {
document.documentElement.setAttribute('data-theme', t);
})();
</script>
- <script src="/corner-nav.js" defer></script>
+ <!-- corner-nav disabled (UL-hamburger rebuild) -->
<style id="ns-modal-css">
/* ── Contact modal (.ns-modal) — added by fanout-12 ── */
.ns-modal {
@@ -833,5 +833,7 @@ toggle.addEventListener('click', () => {
window.nsContactClose = function () { var m = document.getElementById('nsContactModal'); if (m) m.classList.remove('show'); };
}
</script>
+<script>window.DwHeaderConfig={siteName:"Wallpaperdefinitions",accent:"#9B7B4F",navLinks:[{name:'Home',url:'/'},{name:'Collections',url:'#shop'},{name:'About',url:'/about'},{name:'Care',url:'/care.html'},{name:'History',url:'/history.html'}],adminLinks:[{name:'Shopify admin',url:'https://admin.shopify.com',external:true},{name:'Edit hero',url:'#'},{name:'Analytics',url:'#'}]};</script>
+<script src="/dw-header.js" defer></script>
</body>
</html>
← fc91905 Remove Big Red widget — never UX-worked, collided with Help
·
back to Wallpaperdefinitions
·
Revert "Remove Big Red widget — never UX-worked, collided wi e978f81 →