← back to Quadrille Showroom
auto-save: 2026-06-28T16:15:32 (1 files) — LIQUID-UI-SPEC.md
c38484d5a0872f3ef851c262087b8efd4c19b82e · 2026-06-28 16:15:34 -0700 · Steve Abrams
Files touched
Diff
commit c38484d5a0872f3ef851c262087b8efd4c19b82e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Jun 28 16:15:34 2026 -0700
auto-save: 2026-06-28T16:15:32 (1 files) — LIQUID-UI-SPEC.md
---
LIQUID-UI-SPEC.md | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
diff --git a/LIQUID-UI-SPEC.md b/LIQUID-UI-SPEC.md
new file mode 100644
index 0000000..f807eed
--- /dev/null
+++ b/LIQUID-UI-SPEC.md
@@ -0,0 +1,89 @@
+# LIQUID-UI-SPEC — Quadrille Showroom HUD elevation (Figma + 21st-dev sourced)
+
+Source: 21st.dev "liquid glass" component inspiration (SVG-displacement technique) + Figma
+(team::1520578458021404621, authenticated). Goal: kill the "juvenile" HUD — replace flat
+chips / basic top bar / plain spec card with a **liquid-glass luxury** system that stays
+**Phillip-Jeffries-grade** and **senior-usable**, WITHOUT tanking FPS over the live WebGL canvas.
+
+## The hard performance rule (read first)
+The showroom canvas re-renders every frame. A full SVG `feDisplacementMap` `backdrop-filter`
+composited over it **every frame** will wreck FPS (Steve's "maximize FPS" rule). Therefore:
+
+- **Tier 1 — always-on HUD (top bar, chips, slider rail):** CSS-only frosted glass.
+ `backdrop-filter: blur(14px) saturate(140%)` + translucent fill + 1px hairline + inset
+ specular highlight. Cheap, steady 60fps. This is the default for anything persistently visible.
+- **Tier 2 — liquid refraction (the "wow"):** the real SVG `feTurbulence → feDisplacementMap`
+ glass, applied ONLY to **static / hover / open moments** — the spec card when it opens, a chip
+ on `:hover`/`:focus`, the slider thumb on grab. No per-frame cost because the underlying scene
+ is paused or the element is transient. NEVER put Tier 2 on a per-frame-updating full-width bar.
+
+## Design tokens (drop into showroom.css `:root`)
+```css
+:root{
+ --glass-fill: rgba(22,22,24,0.46); /* smoked, not white — luxury reads dark */
+ --glass-fill-hi: rgba(255,255,255,0.06); /* faint top sheen layer */
+ --glass-stroke: rgba(255,255,255,0.14); /* 1px hairline */
+ --glass-blur: 14px;
+ --glass-sat: 140%;
+ --glass-shadow: 0 8px 30px rgba(0,0,0,0.38), 0 -10px 24px inset rgba(255,255,255,0.05);
+ --ink: #ece9e3; /* warm off-white text */
+ --ink-dim: rgba(236,233,227,0.62);
+ --accent: #b9935a; /* brushed-brass, the ONE metal accent */
+ --font-display: "Cormorant Garamond", "Times New Roman", serif; /* thin luxury display */
+ --font-ui: -apple-system, "Helvetica Neue", system-ui, sans-serif;
+ --tr: 420ms cubic-bezier(.22,.61,.36,1); /* slow morph, never snappy */
+}
+```
+
+## Tier 1 — frosted glass base (top bar, chips, slider rail)
+```css
+.glass{
+ background: linear-gradient(var(--glass-fill-hi), transparent 60%), var(--glass-fill);
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
+ border: 1px solid var(--glass-stroke);
+ border-radius: 16px;
+ box-shadow: var(--glass-shadow);
+ color: var(--ink);
+}
+/* Top bar: thin, letter-spaced display wordmark left, controls right, generous padding. */
+/* Version/theme chips: pill .glass, ink-dim default, brass underline + ink on active;
+ morph the active indicator with --tr (transform, not width-jump). */
+/* Open-angle slider: .glass rail, brass thumb, value label in --font-display. */
+```
+
+## Tier 2 — liquid refraction (spec card open / chip hover only)
+Inject ONE shared SVG filter once (hidden `<svg>` at end of body), reference via class:
+```html
+<svg width="0" height="0" style="position:absolute"><defs>
+ <filter id="liquid" x="-20%" y="-20%" width="140%" height="140%">
+ <feTurbulence type="fractalNoise" baseFrequency="0.001 0.005" numOctaves="2" seed="7" result="noise"/>
+ <feGaussianBlur in="noise" stdDeviation="3" result="soft"/>
+ <feDisplacementMap in="SourceGraphic" in2="soft" scale="60" xChannelSelector="R" yChannelSelector="G"/>
+ </filter>
+</defs></svg>
+```
+```css
+.liquid:hover, .spec-card.open{
+ -webkit-backdrop-filter: blur(10px) url(#liquid);
+ backdrop-filter: blur(10px) url(#liquid); /* scale kept modest (60) so refraction is elegant, not warped */
+}
+@media (prefers-reduced-motion: reduce){ .liquid:hover,.spec-card.open{ backdrop-filter: blur(12px) saturate(140%);} }
+```
+Note: `scale=200` (the 21st source) is too much for a UI panel — it smears text. Use **scale 40–70**.
+Gate Tier 2 behind a feature flag + an FPS check: if measured FPS < 55 with it on, fall back to Tier 1.
+
+## Senior-usable guardrails (do NOT regress)
+- Min 16px UI text, 18px+ for the spec card body; hit targets ≥ 44px.
+- Contrast: ink on smoked glass must clear APCA Lc ≥ 60 (run hero-readability-auditor on a screenshot).
+- Motion slow (--tr ~420ms); respect prefers-reduced-motion (drop Tier 2 + shorten morphs).
+- The big guided buttons (Prev / Auto-Tour / Next / All Designs) keep their large size — just reskin to .glass.
+
+## Where it lands (next officer slice, AFTER geometry Slice 1 commits)
+- `public/css/showroom.css` — tokens + `.glass` + `.liquid` + chip/topbar/slider/spec-card reskin.
+- `public/js/showroom.js` / `versions.js` — add the hidden `<svg>` filter once; add `.glass`/`.liquid`
+ classes to the HUD nodes; FPS-gate Tier 2. (Officer is sole editor — do NOT touch while Slice 1 runs.)
+- Verify with /screenrecord (5 combos) + /cta (Chrome+Safari — WebKit's `backdrop-filter: url()`
+ support is the #1 risk; Tier 1 blur is safe, Tier 2 url-filter must be WebKit-tested) → /contrarian gate.
+```
+```
← 1fbedd2 Slice 1 (A+B+C): 14x14 room, fixed-centre yaw-spin camera (c
·
back to Quadrille Showroom
·
Slice 1: open-angle slider (qh_open_angle, 45° default, 20-9 37affeb →