← back to Model Arena
data/artifacts/15fb25164101/grok.html
719 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shop by Color — Designer Wallcoverings</title>
<style>
:root {
--bg: #0d0d0f;
--surface: #17171c;
--surface-2: #1e1e24;
--ink: #f5f2ea;
--muted: #8a8578;
--accent: #c9a961;
--accent-dim: #7d6a45;
--line: rgba(245, 242, 234, 0.12);
--line-strong: rgba(245, 242, 234, 0.22);
--shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.3);
--ease: cubic-bezier(.22, .9, .24, 1);
--ease-out: cubic-bezier(.22, 1, .36, 1);
--ease-power: cubic-bezier(.77, 0, .18, 1);
--serif: 'Didot', 'Bodoni MT', 'Playfair Display', Georgia, 'Times New Roman', serif;
--sans: 'Avenir Next', 'Century Gothic', Futura, 'Segoe UI', sans-serif;
--radius: 14px;
--radius-sm: 6px;
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 24px;
--space-6: 32px;
--space-7: 48px;
--space-8: 64px;
--hue: 30;
--sat: 42%;
--lit: 55%;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
height: 100%;
background: var(--bg);
color: var(--ink);
font-family: var(--sans);
font-size: 16px;
line-height: 1.55;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
}
/* ── Fixed top bar ── */
.topbar {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 100;
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px var(--space-5);
background: rgba(13, 13, 15, 0.85);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-bottom: 1px solid var(--line);
}
.wordmark {
font-family: var(--serif);
font-size: 11px;
font-weight: 400;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink);
max-width: 220px;
white-space: nowrap;
line-height: 1.3;
}
.topbar-meta {
font-family: var(--sans);
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);
}
/* ── Main ── */
main {
min-height: 100%;
padding: 72px var(--space-5) var(--space-7);
display: flex;
flex-direction: column;
align-items: center;
max-width: 720px;
margin: 0 auto;
}
.intro {
text-align: center;
margin-bottom: var(--space-6);
animation: fadeUp 0.8s var(--ease-out) both;
}
.eyebrow {
font-family: var(--sans);
font-size: 11px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: var(--space-3);
}
.intro h1 {
font-family: var(--serif);
font-size: 38px;
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.1;
color: var(--ink);
margin-bottom: var(--space-3);
}
.intro p {
font-size: 15px;
color: var(--muted);
max-width: 36ch;
margin: 0 auto;
line-height: 1.6;
}
/* ── Wheel stage ── */
.wheel-stage {
position: relative;
width: min(300px, 78vw);
aspect-ratio: 1;
margin: 0 auto var(--space-6);
animation: scaleIn 0.9s var(--ease-out) 0.15s both;
touch-action: none;
user-select: none;
-webkit-user-select: none;
}
.wheel-ring {
position: absolute;
inset: 0;
border-radius: 50%;
background: conic-gradient(
from 0deg,
hsl(0, 55%, 52%),
hsl(30, 58%, 52%),
hsl(60, 52%, 48%),
hsl(90, 48%, 44%),
hsl(120, 45%, 42%),
hsl(150, 48%, 42%),
hsl(180, 50%, 44%),
hsl(210, 55%, 50%),
hsl(240, 52%, 54%),
hsl(270, 50%, 52%),
hsl(300, 48%, 50%),
hsl(330, 52%, 52%),
hsl(0, 55%, 52%)
);
box-shadow: var(--shadow), inset 0 0 0 1px var(--line);
cursor: grab;
}
.wheel-ring:active { cursor: grabbing; }
.wheel-inner {
position: absolute;
inset: 18%;
border-radius: 50%;
background: var(--bg);
box-shadow: inset 0 0 0 1px var(--line), 0 0 0 1px var(--line);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 6px;
pointer-events: none;
}
.swatch-preview {
width: 52px;
height: 52px;
border-radius: 50%;
background: hsl(var(--hue), var(--sat), var(--lit));
box-shadow: 0 0 0 1px var(--line-strong), var(--shadow-soft);
transition: background 0.35s var(--ease);
}
.hue-label {
font-family: var(--sans);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--muted);
transition: color 0.3s var(--ease);
}
.pointer {
position: absolute;
top: 4px;
left: 50%;
width: 3px;
height: 18px;
margin-left: -1.5px;
background: var(--ink);
border-radius: 2px;
box-shadow: 0 1px 4px rgba(0,0,0,0.5);
z-index: 2;
pointer-events: none;
}
.pointer::after {
content: '';
position: absolute;
bottom: -5px;
left: 50%;
transform: translateX(-50%);
border: 5px solid transparent;
border-top-color: var(--ink);
}
/* ── Active family name ── */
.family-name {
text-align: center;
margin-bottom: var(--space-5);
min-height: 2.4em;
animation: fadeUp 0.7s var(--ease-out) 0.25s both;
}
.family-name h2 {
font-family: var(--serif);
font-size: 28px;
font-weight: 400;
letter-spacing: -0.01em;
color: var(--ink);
transition: opacity 0.3s var(--ease);
}
.family-name span {
display: block;
font-family: var(--sans);
font-size: 11px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);
margin-top: 6px;
}
/* ── Filmstrip ── */
.filmstrip-wrap {
width: 100%;
overflow: hidden;
margin-bottom: var(--space-7);
animation: fadeUp 0.8s var(--ease-out) 0.35s both;
}
.filmstrip {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-3);
transition: opacity 0.28s var(--ease), transform 0.35s var(--ease-out);
}
.filmstrip.is-shifting {
opacity: 0;
transform: translateY(10px);
}
.tile {
position: relative;
aspect-ratio: 3 / 4;
border-radius: var(--radius-sm);
overflow: hidden;
background: var(--surface);
border: 1px solid var(--line);
box-shadow: var(--shadow-soft);
cursor: pointer;
transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease), border-color 0.3s var(--ease);
}
.tile:hover, .tile:focus-visible {
transform: translateY(-4px);
box-shadow: var(--shadow);
border-color: var(--line-strong);
outline: none;
}
.tile:active { transform: translateY(-1px); }
.tile-pattern {
position: absolute;
inset: 0;
transition: background 0.4s var(--ease);
}
/* CSS pattern variants */
.pat-damask {
background-color: var(--c1);
background-image:
radial-gradient(ellipse 40% 30% at 50% 30%, var(--c2) 0%, transparent 70%),
radial-gradient(ellipse 30% 40% at 50% 70%, var(--c3) 0%, transparent 65%),
radial-gradient(circle at 50% 50%, transparent 28%, var(--c2) 29%, var(--c2) 31%, transparent 32%);
}
.pat-herringbone {
background-color: var(--c1);
background-image:
repeating-linear-gradient(
45deg,
var(--c2) 0 2px,
transparent 2px 10px
),
repeating-linear-gradient(
-45deg,
var(--c3) 0 2px,
transparent 2px 10px
);
}
.pat-toile {
background-color: var(--c1);
background-image:
radial-gradient(circle at 25% 25%, var(--c2) 1.5px, transparent 2px),
radial-gradient(circle at 75% 75%, var(--c2) 1.5px, transparent 2px),
radial-gradient(circle at 50% 50%, var(--c3) 8px, transparent 9px),
radial-gradient(circle at 20% 70%, var(--c3) 5px, transparent 6px),
radial-gradient(circle at 80% 30%, var(--c3) 5px, transparent 6px);
background-size: 28px 28px, 28px 28px, 56px 56px, 56px 56px, 56px 56px;
}
.pat-grasscloth {
background-color: var(--c1);
background-image:
repeating-linear-gradient(
90deg,
transparent,
transparent 3px,
var(--c2) 3px,
var(--c2) 4px
),
repeating-linear-gradient(
0deg,
transparent,
transparent 1px,
var(--c3) 1px,
var(--c3) 2px
);
background-size: 8px 100%, 100% 6px;
}
.tile-meta {
position: absolute;
left: 0; right: 0; bottom: 0;
padding: 28px 10px 10px;
background: linear-gradient(to top, rgba(0,0,0,0.72) 0%, transparent 100%);
pointer-events: none;
}
.tile-name {
font-family: var(--serif);
font-size: 13px;
font-weight: 400;
letter-spacing: 0.01em;
color: var(--ink);
line-height: 1.2;
}
.tile-tone {
display: block;
margin-top: 3px;
font-family: var(--sans);
font-size: 9px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: rgba(245, 242, 234, 0.65);
}
/* ── Share bar ── */
.share-bar {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
padding-top: var(--space-5);
border-top: 1px solid var(--line);
animation: fadeUp 0.8s var(--ease-out) 0.45s both;
}
.share-label {
font-family: var(--sans);
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--muted);
margin-right: var(--space-2);
}
.share-btn {
appearance: none;
border: 1px solid var(--line);
background: var(--surface);
color: var(--ink);
font-family: var(--sans);
font-size: 10px;
letter-spacing: 0.14em;
text-transform: uppercase;
padding: 10px 16px;
border-radius: 999px;
cursor: pointer;
transition: border-color 0.3s var(--ease), background 0.3s var(--ease), color 0.3s var(--ease), transform 0.25s var(--ease);
box-shadow: var(--shadow-soft);
}
.share-btn:hover, .share-btn:focus-visible {
border-color: var(--accent);
color: var(--accent);
outline: none;
}
.share-btn:active { transform: scale(0.97); }
.share-btn.copied {
border-color: var(--accent);
background: rgba(201, 169, 97, 0.12);
color: var(--accent);
}
.brand-foot {
margin-top: var(--space-6);
text-align: center;
font-family: var(--serif);
font-size: 10px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--muted);
animation: fadeUp 0.8s var(--ease-out) 0.55s both;
}
.brand-foot em {
font-style: normal;
color: var(--accent-dim);
}
/* ── Motion ── */
@keyframes fadeUp {
from { opacity: 0; transform: translateY(28px); }
to { opacity: 1; transform: none; }
}
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.92); }
to { opacity: 1; transform: none; }
}
/* ── Responsive ── */
@media (max-width: 480px) {
.intro h1 { font-size: 28px; }
.family-name h2 { font-size: 22px; }
.filmstrip { gap: 8px; }
.tile-name { font-size: 11px; }
.share-bar { flex-wrap: wrap; }
main { padding-left: 16px; padding-right: 16px; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<header class="topbar">
<div class="wordmark">Designer Wallcoverings</div>
<div class="topbar-meta">Shop by Color</div>
</header>
<main>
<div class="intro">
<div class="eyebrow">Curated Palettes</div>
<h1>Shop by Color</h1>
<p>Turn the wheel to explore pattern families. Four wallcovering designs in every hue.</p>
</div>
<div class="wheel-stage" id="wheelStage" aria-label="Color wheel — drag or tap to select a hue">
<div class="pointer" aria-hidden="true"></div>
<div class="wheel-ring" id="wheelRing"></div>
<div class="wheel-inner">
<div class="swatch-preview" id="swatchPreview"></div>
<div class="hue-label" id="hueLabel">Amber</div>
</div>
</div>
<div class="family-name">
<h2 id="familyTitle">Gilded Amber</h2>
<span id="familySub">Warm metallics · Four designs</span>
</div>
<div class="filmstrip-wrap">
<div class="filmstrip" id="filmstrip" role="list"></div>
</div>
<div class="share-bar">
<span class="share-label">Share</span>
<button type="button" class="share-btn" id="btnCopy" aria-label="Copy link">Copy Link</button>
<button type="button" class="share-btn" id="btnSave" aria-label="Save palette">Save Palette</button>
</div>
<div class="brand-foot">Designer Wallcoverings <em>·</em> Est. Atelier</div>
</main>
<script>
(function () {
const FAMILIES = [
{ h: 8, name: 'Vermilion', title: 'Ember Rouge', sub: 'Fiery reds · Four designs',
colors: ['Scarlet Atelier', 'Russet Vein', 'Cinnabar Cloth', 'Garnet Loom'],
sats: [48, 42, 38, 44], lits: [48, 38, 32, 42] },
{ h: 28, name: 'Amber', title: 'Gilded Amber', sub: 'Warm metallics · Four designs',
colors: ['Honey Leaf', 'Bronze Damask', 'Saffron Thread', 'Ochre Field'],
sats: [52, 40, 48, 36], lits: [52, 42, 48, 38] },
{ h: 48, name: 'Champagne', title: 'Sunlit Straw', sub: 'Golden neutrals · Four designs',
colors: ['Wheat Sheaf', 'Pale Brass', 'Flax Linen', 'Marigold Dust'],
sats: [38, 30, 28, 42], lits: [58, 50, 62, 48] },
{ h: 78, name: 'Olive', title: 'Botanical Olive', sub: 'Garden greens · Four designs',
colors: ['Moss Canopy', 'Chartreuse Silk', 'Fern Archive', 'Lichen Wall'],
sats: [32, 40, 28, 24], lits: [38, 48, 34, 42] },
{ h: 145, name: 'Jade', title: 'Deep Verdure', sub: 'Forest tones · Four designs',
colors: ['Emerald Grove', 'Tea Leaf', 'Malachite', 'Willow Shade'],
sats: [40, 28, 36, 22], lits: [36, 44, 32, 40] },
{ h: 175, name: 'Teal', title: 'Aegean Tide', sub: 'Coastal blues · Four designs',
colors: ['Seaglass', 'Petrol Damask', 'Celadon Mist', 'Harbor Ink'],
sats: [36, 42, 28, 38], lits: [48, 34, 56, 30] },
{ h: 210, name: 'Indigo', title: 'Midnight Indigo', sub: 'Deep blues · Four designs',
colors: ['Cobalt Archive', 'Slate Navy', 'Powder Blue', 'Ink Well'],
sats: [48, 32, 36, 40], lits: [42, 36, 58, 28] },
{ h: 255, name: 'Violet', title: 'Amethyst Hour', sub: 'Jewel violets · Four designs',
colors: ['Iris Veil', 'Plum Damask', 'Wisteria', 'Aubergine'],
sats: [38, 34, 30, 36], lits: [50, 36, 58, 30] },
{ h: 310, name: 'Rose', title: 'Blush Nocturne', sub: 'Soft magentas · Four designs',
colors: ['Peony Silk', 'Mauve Archive', 'Orchid Dust', 'Berry Cloth'],
sats: [36, 28, 32, 40], lits: [56, 46, 60, 40] },
{ h: 345, name: 'Crimson', title: 'Burgundy Salon', sub: 'Wine reds · Four designs',
colors: ['Bordeaux', 'Rosewood', 'Claret Weave', 'Port Velvet'],
sats: [44, 36, 40, 42], lits: [38, 34, 42, 30] }
];
const PATTERNS = ['pat-damask', 'pat-herringbone', 'pat-toile', 'pat-grasscloth'];
const root = document.documentElement;
const filmstrip = document.getElementById('filmstrip');
const swatchPreview = document.getElementById('swatchPreview');
const hueLabel = document.getElementById('hueLabel');
const familyTitle = document.getElementById('familyTitle');
const familySub = document.getElementById('familySub');
const stage = document.getElementById('wheelStage');
const ring = document.getElementById('wheelRing');
let currentIdx = 1;
let dragging = false;
function nearestFamily(hue) {
let best = 0, bestDist = 999;
FAMILIES.forEach((f, i) => {
let d = Math.abs(f.h - hue);
if (d > 180) d = 360 - d;
if (d < bestDist) { bestDist = d; best = i; }
});
return best;
}
function applyFamily(idx, animate) {
const f = FAMILIES[idx];
currentIdx = idx;
root.style.setProperty('--hue', f.h);
root.style.setProperty('--sat', '42%');
root.style.setProperty('--lit', '52%');
hueLabel.textContent = f.name;
const paint = () => {
familyTitle.textContent = f.title;
familySub.textContent = f.sub;
filmstrip.innerHTML = '';
f.colors.forEach((name, i) => {
const h = f.h;
const s1 = f.sats[i];
const l1 = f.lits[i];
const c1 = `hsl(${h}, ${s1}%, ${l1}%)`;
const c2 = `hsl(${h}, ${Math.max(10, s1 - 12)}%, ${Math.min(70, l1 + 12)}%)`;
const c3 = `hsl(${h}, ${Math.min(60, s1 + 8)}%, ${Math.max(18, l1 - 14)}%)`;
const tile = document.createElement('article');
tile.className = 'tile';
tile.setAttribute('role', 'listitem');
tile.setAttribute('tabindex', '0');
tile.setAttribute('aria-label', name);
tile.style.setProperty('--i', i);
const pat = document.createElement('div');
pat.className = 'tile-pattern ' + PATTERNS[i];
pat.style.setProperty('--c1', c1);
pat.style.setProperty('--c2', c2);
pat.style.setProperty('--c3', c3);
const meta = document.createElement('div');
meta.className = 'tile-meta';
meta.innerHTML = '<div class="tile-name"></div><span class="tile-tone"></span>';
meta.querySelector('.tile-name').textContent = name;
meta.querySelector('.tile-tone').textContent = f.name + ' · 0' + (i + 1);
tile.appendChild(pat);
tile.appendChild(meta);
filmstrip.appendChild(tile);
});
filmstrip.classList.remove('is-shifting');
};
if (animate) {
filmstrip.classList.add('is-shifting');
setTimeout(paint, 280);
} else {
paint();
}
}
function hueFromEvent(e) {
const rect = ring.getBoundingClientRect();
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
const point = e.touches ? e.touches[0] : e;
const dx = point.clientX - cx;
const dy = point.clientY - cy;
let angle = Math.atan2(dy, dx) * (180 / Math.PI) + 90;
if (angle < 0) angle += 360;
return angle;
}
function onPointerDown(e) {
dragging = true;
onPointerMove(e);
e.preventDefault();
}
function onPointerMove(e) {
if (!dragging && e.type !== 'click') return;
if (!dragging && e.type === 'click') dragging = true;
const hue = hueFromEvent(e);
const idx = nearestFamily(hue);
root.style.setProperty('--hue', Math.round(hue));
const f = FAMILIES[idx];
root.style.setProperty('--sat', '45%');
root.style.setProperty('--lit', '52%');
if (idx !== currentIdx) {
applyFamily(idx, true);
}
if (e.type === 'click') dragging = false;
}
function onPointerUp() { dragging = false; }
ring.addEventListener('mousedown', onPointerDown);
ring.addEventListener('touchstart', onPointerDown, { passive: false });
window.addEventListener('mousemove', onPointerMove);
window.addEventListener('touchmove', onPointerMove, { passive: false });
window.addEventListener('mouseup', onPointerUp);
window.addEventListener('touchend', onPointerUp);
// Keyboard: arrow keys cycle families
stage.setAttribute('tabindex', '0');
stage.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight' || e.key === 'ArrowUp') {
applyFamily((currentIdx + 1) % FAMILIES.length, true);
e.preventDefault();
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') {
applyFamily((currentIdx - 1 + FAMILIES.length) % FAMILIES.length, true);
e.preventDefault();
}
});
// Share actions
const btnCopy = document.getElementById('btnCopy');
const btnSave = document.getElementById('btnSave');
btnCopy.addEventListener('click', async () => {
const f = FAMILIES[currentIdx];
const text = 'Designer Wallcoverings — ' + f.title + ' palette';
try {
if (navigator.clipboard) await navigator.clipboard.writeText(text + ' · Shop by Color');
} catch (_) {}
btnCopy.textContent = 'Copied';
btnCopy.classList.add('copied');
setTimeout(() => {
btnCopy.textContent = 'Copy Link';
btnCopy.classList.remove('copied');
}, 1800);
});
btnSave.addEventListener('click', () => {
const f = FAMILIES[currentIdx];
btnSave.textContent = 'Saved · ' + f.name;
btnSave.classList.add('copied');
setTimeout(() => {
btnSave.textContent = 'Save Palette';
btnSave.classList.remove('copied');
}, 1800);
});
// Init
applyFamily(1, false);
})();
</script>
</body>
</html>