← back to Designer Wallcoverings
Add DW design-language style guide: tokens.css + servable /style-guide page
2744dd362b741734126c58cf08448fe6b3c5b5e9 · 2026-06-23 16:28:54 -0700 · Steve
Distilled the approved cream-background / Lora-Inter vendor-landing look
(quadrille-house PDP) into a canonical token contract (style-guide/tokens.css,
33 custom props) plus a live reference page (style-guide/index.html) that
renders palette swatches, the type scale, and the DW component set straight
from the tokens. Served from the flagship via brand-router (/style-guide).
Files touched
M brand-router/server.jsA style-guide/index.htmlA style-guide/tokens.css
Diff
commit 2744dd362b741734126c58cf08448fe6b3c5b5e9
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Jun 23 16:28:54 2026 -0700
Add DW design-language style guide: tokens.css + servable /style-guide page
Distilled the approved cream-background / Lora-Inter vendor-landing look
(quadrille-house PDP) into a canonical token contract (style-guide/tokens.css,
33 custom props) plus a live reference page (style-guide/index.html) that
renders palette swatches, the type scale, and the DW component set straight
from the tokens. Served from the flagship via brand-router (/style-guide).
---
brand-router/server.js | 4 +
style-guide/index.html | 269 +++++++++++++++++++++++++++++++++++++++++++++++++
style-guide/tokens.css | 98 ++++++++++++++++++
3 files changed, 371 insertions(+)
diff --git a/brand-router/server.js b/brand-router/server.js
index 968b0e7c..65c143b0 100644
--- a/brand-router/server.js
+++ b/brand-router/server.js
@@ -40,6 +40,10 @@ app.use(basicAuth);
app.use(express.json({ limit: '10mb' }));
app.use(express.static(path.join(__dirname, 'public')));
+// DW design-language style guide — canonical tokens.css + servable reference page.
+// Lives at repo root /style-guide; served here so the look has one home + one URL.
+app.use('/style-guide', express.static(path.join(__dirname, '..', 'style-guide')));
+
// Load brand data
const BRANDS_FILE = path.join(__dirname, 'brands-data.json');
function loadBrands() {
diff --git a/style-guide/index.html b/style-guide/index.html
new file mode 100644
index 00000000..e87aecd7
--- /dev/null
+++ b/style-guide/index.html
@@ -0,0 +1,269 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<title>Style Guide · Designer Wallcoverings</title>
+<meta name="description" content="The Designer Wallcoverings vendor-landing design language — tokens, type, and components.">
+<link rel="preconnect" href="https://fonts.googleapis.com">
+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+<link href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
+
+<!-- THE CONTRACT: every color / font / radius / shadow comes from here -->
+<link rel="stylesheet" href="./tokens.css">
+
+<style>
+ /* ---- style-guide chrome (page-local; demonstrates building on the tokens) ---- */
+ .wrap{ max-width:var(--maxw); margin:0 auto; padding:0 var(--gutter); }
+ header.sg{
+ border-bottom:1px solid var(--line); background:var(--paper);
+ padding:48px var(--gutter) 40px;
+ }
+ header.sg .eyebrow{ font-size:var(--fs-eyebrow); letter-spacing:var(--track-eyebrow);
+ text-transform:uppercase; color:var(--gold); }
+ header.sg h1{ font-family:var(--serif); font-weight:500; font-size:var(--fs-h2);
+ margin:10px 0 8px; letter-spacing:.01em; }
+ header.sg p{ color:var(--ink-soft); margin:0; max-width:620px; font-size:14.5px; }
+ header.sg .wm{ font-family:var(--serif); font-weight:600; letter-spacing:var(--track-wordmark);
+ text-transform:uppercase; font-size:15px; }
+ header.sg .wm .dot{ color:var(--gold); }
+
+ section.sg{ padding:54px 0 8px; border-bottom:1px solid var(--line); }
+ section.sg:last-of-type{ border-bottom:0; }
+ .sg-label{ font-size:var(--fs-eyebrow); letter-spacing:var(--track-eyebrow);
+ text-transform:uppercase; color:var(--ink-soft); margin-bottom:22px; }
+ .sg-label b{ color:var(--ink); font-weight:600; }
+ .note{ color:var(--taupe); font-size:12.5px; margin:6px 0 26px; max-width:640px; line-height:1.6; }
+ code.tok{ font-family:ui-monospace,Menlo,Consolas,monospace; font-size:12px;
+ background:#efe9df; color:var(--accent); padding:1.5px 6px; border-radius:5px; }
+
+ /* palette swatches */
+ .swatches{ display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:16px; }
+ .sw{ border:1px solid var(--line); border-radius:var(--r-card); overflow:hidden; background:var(--paper); }
+ .sw .chip{ height:96px; }
+ .sw .meta{ padding:11px 12px 13px; }
+ .sw .nm{ font-size:12.5px; color:var(--ink); font-weight:600; }
+ .sw .var{ font-family:ui-monospace,Menlo,monospace; font-size:11px; color:var(--ink-soft); margin-top:3px; }
+ .sw .hex{ font-family:ui-monospace,Menlo,monospace; font-size:11px; color:var(--taupe); text-transform:uppercase; }
+
+ /* type specimens */
+ .type-row{ display:flex; align-items:baseline; gap:18px; padding:14px 0; border-bottom:1px solid var(--line); flex-wrap:wrap; }
+ .type-row:last-child{ border-bottom:0; }
+ .type-row .demo{ flex:1 1 360px; min-width:0; }
+ .type-row .spec{ font-family:ui-monospace,Menlo,monospace; font-size:11px; color:var(--ink-soft); white-space:nowrap; }
+ .t-hero{ font-family:var(--serif); font-weight:500; font-size:clamp(40px,7vw,72px); line-height:.98; }
+ .t-h1{ font-family:var(--serif); font-weight:500; font-size:var(--fs-h1); line-height:1.02; }
+ .t-h2{ font-family:var(--serif); font-weight:500; font-size:var(--fs-h2); }
+ .t-h3{ font-family:var(--serif); font-weight:500; font-size:var(--fs-h3); }
+ .t-sub{ font-family:var(--serif); font-style:italic; font-size:24px; color:var(--ink); }
+ .t-body{ font-family:var(--sans); font-size:var(--fs-body); line-height:var(--lh-body); color:var(--ink-soft); max-width:560px; }
+ .t-eyebrow{ font-size:var(--fs-eyebrow); letter-spacing:var(--track-eyebrow); text-transform:uppercase; color:var(--gold); }
+
+ .demo-grid{ display:grid; gap:34px; }
+ .demo-stack{ display:flex; flex-direction:column; gap:14px; align-items:flex-start; }
+ .swatch-band{ display:flex; gap:10px; flex-wrap:wrap; align-items:center; }
+
+ /* ---- DW component library (the reusable patterns, lifted from vendor-landing) ---- */
+ .cta{ display:inline-block; text-align:center; padding:15px 26px; border-radius:var(--r-pill);
+ background:var(--ink); color:#fff; font-size:11px; letter-spacing:var(--track-cta);
+ text-transform:uppercase; cursor:pointer; border:0; transition:background var(--t-fast); }
+ .cta:hover{ background:var(--accent); }
+ .cta.ghost{ background:transparent; color:var(--ink); border:1px solid var(--line); }
+ .cta.ghost:hover{ border-color:var(--ink); }
+
+ .chip{ border:1px solid var(--line); background:var(--paper); border-radius:var(--r-pill);
+ padding:9px 18px; font-size:12px; letter-spacing:.04em; cursor:pointer; transition:var(--t-fast); color:var(--ink-soft); }
+ .chip:hover{ border-color:var(--taupe); color:var(--ink); }
+ .chip.active{ background:var(--ink); color:#fff; border-color:var(--ink); }
+ .chip .n{ opacity:.55; margin-left:6px; font-variant-numeric:tabular-nums; }
+
+ .cdot{ display:inline-flex; align-items:center; gap:7px; border:1px solid var(--line); background:var(--paper);
+ border-radius:var(--r-pill); padding:6px 13px 6px 8px; font-size:11px; letter-spacing:.04em;
+ text-transform:capitalize; color:var(--ink-soft); cursor:pointer; transition:.18s; }
+ .cdot span{ width:14px; height:14px; border-radius:50%; border:1px solid rgba(0,0,0,.15); }
+ .cdot:hover{ border-color:var(--taupe); color:var(--ink); }
+ .cdot.active{ background:var(--ink); color:#fff; border-color:var(--ink); }
+
+ /* product card */
+ .cards{ display:grid; grid-template-columns:repeat(var(--cols),1fr); gap:22px; }
+ .card{ background:var(--paper); border:1px solid var(--line); border-radius:var(--r-card); overflow:hidden;
+ cursor:pointer; transition:transform var(--t-med) ease,box-shadow var(--t-med) ease; display:flex; flex-direction:column; }
+ .card:hover{ transform:translateY(-3px); box-shadow:var(--shadow-card); }
+ .card .ph{ aspect-ratio:1/1; width:100%; object-fit:cover; display:block; transition:transform .6s var(--ease); }
+ .card:hover .ph{ transform:scale(1.055); }
+ .card .body{ padding:13px 14px 15px; }
+ .card .ser{ font-size:10px; letter-spacing:.18em; text-transform:uppercase; color:var(--gold); }
+ .card .nm{ font-family:var(--serif); font-size:var(--fs-card); line-height:1.1; margin:3px 0 2px; }
+ .card .col{ font-size:12px; color:var(--ink-soft); display:inline-flex; align-items:center; gap:6px; margin-top:7px; }
+ .card .col .dot{ width:9px; height:9px; border-radius:50%; border:1px solid rgba(0,0,0,.12); }
+
+ /* spec table */
+ .spec{ border-top:1px solid var(--line); max-width:440px; }
+ .spec .row{ display:flex; justify-content:space-between; padding:11px 0; border-bottom:1px solid var(--line); font-size:13px; }
+ .spec .row .k{ color:var(--ink-soft); letter-spacing:.04em; }
+ .spec .row .v{ color:var(--ink); text-align:right; }
+
+ /* share buttons */
+ .share{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
+ .share-lbl{ font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--ink-soft); }
+ .sbtn{ display:inline-flex; align-items:center; justify-content:center; width:38px; height:38px; border-radius:50%;
+ border:1px solid var(--line); background:var(--paper); color:var(--ink-soft); cursor:pointer; transition:var(--t-fast); }
+ .sbtn:hover{ color:#fff; transform:translateY(-2px); background:var(--ink); border-color:var(--ink); }
+
+ /* form fields (memo-quote register) */
+ .field{ display:block; font-size:12px; letter-spacing:.04em; color:var(--ink); margin-bottom:14px;
+ text-transform:uppercase; max-width:440px; }
+ .field input,.field textarea{ display:block; width:100%; margin-top:5px; padding:11px 12px; border:1px solid var(--line);
+ border-radius:var(--r-input); font:inherit; font-size:14px; background:#fff; color:var(--ink); }
+ .field input:focus,.field textarea:focus{ outline:none; border-color:var(--gold); }
+
+ footer.sg{ background:var(--paper); border-top:1px solid var(--line); margin-top:40px;
+ padding:34px var(--gutter); font-size:12px; color:var(--ink-soft); }
+ footer.sg b{ color:var(--ink); }
+</style>
+</head>
+<body>
+
+<header class="sg">
+ <div class="wrap">
+ <span class="wm">Designer Wallcoverings<span class="dot">.</span></span>
+ <div class="eyebrow" style="margin-top:18px">Design Language</div>
+ <h1>Style Guide</h1>
+ <p>The editorial-house look — warm cream paper, Lora display, Inter body, champagne-taupe & gold accents.
+ Every value below is driven live by <code class="tok">tokens.css</code>; build with the variables, never hard-code.</p>
+ </div>
+</header>
+
+<div class="wrap">
+
+ <!-- PALETTE -->
+ <section class="sg">
+ <div class="sg-label"><b>01</b> Palette</div>
+ <p class="note">Seven tokens carry the whole surface. Per-line themes override only these
+ (e.g. <code class="tok">body[data-line="fallingstar"]{--accent:…}</code>) — never the components.</p>
+ <div class="swatches" id="swatches"></div>
+ </section>
+
+ <!-- TYPE -->
+ <section class="sg">
+ <div class="sg-label"><b>02</b> Typography</div>
+ <p class="note"><b style="color:var(--ink)">Lora</b> (serif) for display & product names · <b style="color:var(--ink)">Inter</b> (sans) for body & UI ·
+ eyebrows are uppercase Inter with wide tracking.</p>
+
+ <div class="type-row"><div class="demo"><div class="t-eyebrow">Eyebrow / Series Label</div></div><div class="spec">Inter · 11px · .32em · gold</div></div>
+ <div class="type-row"><div class="demo"><div class="t-hero">Hand-screened</div></div><div class="spec">Lora 500 · clamp 52–118px · hero h1</div></div>
+ <div class="type-row"><div class="demo"><div class="t-h1">China Seas Collection</div></div><div class="spec">Lora 500 · 46px · page / PDP h1</div></div>
+ <div class="type-row"><div class="demo"><div class="t-h2">New this season</div></div><div class="spec">Lora 500 · 38px · section h2</div></div>
+ <div class="type-row"><div class="demo"><div class="t-sub">a quiet, collected interior</div></div><div class="spec">Lora italic · 24px · sub / lede</div></div>
+ <div class="type-row"><div class="demo"><div class="t-h3">The pattern</div></div><div class="spec">Lora 500 · 22px · story h3</div></div>
+ <div class="type-row"><div class="demo"><div class="t-body">Hand-screened to order. Request a memo sample and trade quote — pricing is provided to the trade, not shown publicly.</div></div><div class="spec">Inter · 15px / 1.55 · body</div></div>
+ </section>
+
+ <!-- BUTTONS -->
+ <section class="sg">
+ <div class="sg-label"><b>03</b> Buttons & CTAs</div>
+ <p class="note">Ink pill fills to <code class="tok">--accent</code> on hover. Ghost is the secondary action.</p>
+ <div class="swatch-band">
+ <button class="cta">Request memo sample & quote →</button>
+ <button class="cta ghost">Order a sample</button>
+ </div>
+ </section>
+
+ <!-- CHIPS + COLOR DOTS -->
+ <section class="sg">
+ <div class="sg-label"><b>04</b> Collection Chips & Color Filters</div>
+ <div class="swatch-band" style="margin-bottom:20px">
+ <span class="chip active">All <span class="n">428</span></span>
+ <span class="chip">China Seas <span class="n">96</span></span>
+ <span class="chip">Quadrille <span class="n">71</span></span>
+ <span class="chip">Alan Campbell <span class="n">54</span></span>
+ </div>
+ <div class="swatch-band">
+ <span class="cdot all active">All colors</span>
+ <span class="cdot"><span style="background:#1f3a5f"></span>Blue</span>
+ <span class="cdot"><span style="background:#6f7d3f"></span>Green</span>
+ <span class="cdot"><span style="background:#a98c54"></span>Gold</span>
+ <span class="cdot"><span style="background:#7a2e2e"></span>Red</span>
+ </div>
+ </section>
+
+ <!-- CARDS -->
+ <section class="sg">
+ <div class="sg-label"><b>05</b> Product Cards</div>
+ <p class="note">Cream card, hairline border, square image that scales on hover; serif name, gold series, soft-ink colorway.</p>
+ <div class="cards" id="cards"></div>
+ </section>
+
+ <!-- SPEC + SHARE -->
+ <section class="sg">
+ <div class="sg-label"><b>06</b> Spec Table & Share</div>
+ <div class="demo-grid" style="grid-template-columns:repeat(auto-fit,minmax(280px,1fr))">
+ <div class="spec">
+ <div class="row"><span class="k">Item code</span><span class="v">DWCW-701521</span></div>
+ <div class="row"><span class="k">Pattern</span><span class="v">China Seas</span></div>
+ <div class="row"><span class="k">Width</span><span class="v">27"</span></div>
+ <div class="row"><span class="k">Repeat</span><span class="v">36"</span></div>
+ <div class="row"><span class="k">Match</span><span class="v">Straight</span></div>
+ </div>
+ <div class="demo-stack">
+ <div class="share">
+ <span class="share-lbl">Share</span>
+ <span class="sbtn">in</span><span class="sbtn">f</span><span class="sbtn">x</span><span class="sbtn">@</span><span class="sbtn">↗</span>
+ </div>
+ <label class="field">Email<input type="email" placeholder="you@studio.com"></label>
+ <label class="field">Message<textarea rows="2" placeholder="quantity, project, ship-to"></textarea></label>
+ </div>
+ </div>
+ </section>
+
+</div>
+
+<footer class="sg">
+ <div class="wrap">
+ Tokens: <b>style-guide/tokens.css</b> · Reference: <b>style-guide/index.html</b> ·
+ Source look: the DW vendor-landing PDP (Lora / Inter · warm cream).
+ Import the tokens, build with the variables — that is the whole contract.
+ </div>
+</footer>
+
+<script>
+ // Render palette swatches straight from the live computed token values, so the
+ // guide can never drift from tokens.css — change a hex there, this updates.
+ const TOKENS=[
+ ['Background','--bg','page background'],
+ ['Paper','--paper','cards · rails · modals'],
+ ['Ink','--ink','headings & primary text'],
+ ['Ink soft','--ink-soft','secondary text & labels'],
+ ['Line','--line','borders & dividers'],
+ ['Taupe','--taupe','captions & italic notes'],
+ ['Accent','--accent','champagne-taupe · hover fill'],
+ ['Gold','--gold','eyebrows · series · price'],
+ ];
+ const cs=getComputedStyle(document.documentElement);
+ document.getElementById('swatches').innerHTML=TOKENS.map(([nm,v])=>{
+ const hex=cs.getPropertyValue(v).trim();
+ return `<div class="sw">
+ <div class="chip" style="background:${hex};border-bottom:1px solid var(--line)"></div>
+ <div class="meta"><div class="nm">${nm}</div><div class="var">var(${v})</div><div class="hex">${hex}</div></div>
+ </div>`;
+ }).join('');
+
+ // Demo cards with on-token placeholder art (no external images needed).
+ const CARDS=[
+ ['China Seas','Aga · Soft Blue','#cfe0ea','#1f3a5f'],
+ ['Quadrille','Lyford Trellis','#e7e0cf','#a98c54'],
+ ['Alan Campbell','Potalla · Jungle','#dfe6d2','#6f7d3f'],
+ ['China Seas','Fiorentina · Rust','#ecd9cf','#7a2e2e'],
+ ];
+ document.getElementById('cards').innerHTML=CARDS.map(([ser,nm,bg,dot])=>`
+ <div class="card">
+ <div class="ph" style="background:linear-gradient(135deg,${bg},var(--paper))"></div>
+ <div class="body">
+ <div class="ser">${ser}</div>
+ <div class="nm">${nm}</div>
+ <div class="col"><span class="dot" style="background:${dot}"></span>Colorway</div>
+ </div>
+ </div>`).join('');
+</script>
+</body>
+</html>
diff --git a/style-guide/tokens.css b/style-guide/tokens.css
new file mode 100644
index 00000000..ea09fddd
--- /dev/null
+++ b/style-guide/tokens.css
@@ -0,0 +1,98 @@
+/* ============================================================================
+ Designer Wallcoverings — Design Tokens (the :root contract)
+ ----------------------------------------------------------------------------
+ The canonical token layer for the DW vendor-landing / editorial-house look:
+ warm cream paper, warm near-black ink, champagne-taupe + gold accents, and
+ the Lora (serif display) / Inter (sans) pairing.
+
+ Source of truth: distilled from the live quadrille-house-site PDP
+ (the cream-background / clear-fonts treatment Steve approved, 2026-06-23).
+
+ USAGE
+ <link rel="stylesheet"
+ href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Inter:wght@400;500;600&display=swap">
+ <link rel="stylesheet" href="/style-guide/tokens.css">
+
+ Then build with the variables — never hard-code a hex or a font-family.
+ Per-line reskins override ONLY the tokens (e.g. body[data-line="x"]{--accent:…}),
+ never the components — that is what keeps runtime palette-injection safe.
+ ============================================================================ */
+
+:root{
+ /* ---- Surfaces ------------------------------------------------------------ */
+ --bg:#f6f2ec; /* warm cream — page background */
+ --paper:#fffdf9; /* off-white — cards, rails, modals, raised surfaces */
+
+ /* ---- Ink (text) ---------------------------------------------------------- */
+ --ink:#211d18; /* warm near-black — headings & primary text */
+ --ink-soft:#6b6258; /* warm grey — secondary text, labels, meta */
+
+ /* ---- Lines & accents ----------------------------------------------------- */
+ --line:#e3dccf; /* hairline borders & dividers */
+ --taupe:#b7a890; /* muted taupe — captions, italic notes, hover edges */
+ --accent:#8c7a5f; /* champagne-taupe — primary accent / hover fill */
+ --gold:#a98c54; /* gold — eyebrows, series labels, price ticks */
+
+ /* ---- Typography ---------------------------------------------------------- */
+ --serif:"Lora",Georgia,"Times New Roman",serif; /* display / house */
+ --sans:"Inter","Helvetica Neue",Arial,sans-serif; /* body / UI */
+
+ --fs-body:15px; /* base body size */
+ --lh-body:1.55; /* base body line-height */
+
+ /* Display type scale (clamps stay fluid on the hero, fixed elsewhere) */
+ --fs-hero:clamp(52px,9vw,118px); /* hero H1 */
+ --fs-h1:46px; /* PDP / page H1 */
+ --fs-h2:38px; /* section heading */
+ --fs-h3:22px; /* sub-section / story heading */
+ --fs-card:20px; /* card product name */
+
+ /* Eyebrow / label register — uppercase, wide tracking, small */
+ --fs-eyebrow:11px;
+ --track-eyebrow:.32em; /* eyebrows & section labels */
+ --track-wordmark:.2em; /* wordmark / logo */
+ --track-cta:.22em; /* buttons & uppercase CTAs */
+
+ /* ---- Radii --------------------------------------------------------------- */
+ --r-card:4px; /* cards, image frames, main gallery */
+ --r-input:8px; /* inputs, selects, pdf chips */
+ --r-pill:40px; /* CTAs, chips, color dots (fully rounded) */
+ --r-modal:12px; /* modal cards */
+
+ /* ---- Elevation ----------------------------------------------------------- */
+ --shadow-card:0 16px 40px -22px rgba(33,29,24,.45); /* card hover lift */
+ --shadow-modal:0 24px 60px rgba(0,0,0,.30); /* modal */
+
+ /* ---- Layout -------------------------------------------------------------- */
+ --maxw:1320px; /* content max-width */
+ --cols:4; /* product-grid columns (responsive overrides below) */
+ --gutter:26px; /* page side padding */
+
+ /* ---- Motion -------------------------------------------------------------- */
+ --ease:cubic-bezier(.2,.7,.2,1);
+ --t-fast:.2s;
+ --t-med:.25s;
+}
+
+/* Responsive grid density (matches the live vendor-landing breakpoints) */
+@media (max-width:1080px){ :root{ --cols:3; } }
+@media (max-width:720px){ :root{ --cols:2; } }
+
+/* ----------------------------------------------------------------------------
+ Minimal base — opt-in foundation so a fresh page inherits the house defaults.
+ Components live in the style-guide page / per-site CSS, not here; this file
+ is the stable contract, nothing more.
+ ---------------------------------------------------------------------------- */
+*{ box-sizing:border-box; }
+html{ scroll-behavior:smooth; }
+body{
+ margin:0;
+ background:var(--bg);
+ color:var(--ink);
+ font-family:var(--sans);
+ font-size:var(--fs-body);
+ line-height:var(--lh-body);
+ -webkit-font-smoothing:antialiased;
+}
+a{ color:inherit; text-decoration:none; }
+img{ display:block; max-width:100%; }
← a08ffaa0 Close dedup-specs audit: 0.17% duplication (5/3000) — catalo
·
back to Designer Wallcoverings
·
Serve DW style guide standalone via pm2 (dw-style-guide, :99 008e67ab →