← back to Model Arena
data/artifacts/15fb25164101/claude-code.html
485 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Designer Wallcoverings — Shop by Color</title>
<style>
:root{
/* ——— design tokens ——— */
--bg:#0d0d0f;
--surface:#17171c;
--surface-2:#1e1e24;
--ink:#f5f2ea;
--muted:#8a8578;
--accent:#c9a961;
--accent-deep:#7d6a45;
--line:rgba(201,169,97,.22);
--hair:rgba(245,242,234,.10);
--shadow:0 8px 24px rgba(0,0,0,.35);
--shadow-lg:0 16px 48px rgba(0,0,0,.5);
--glow:0 0 26px rgba(201,169,97,.28);
--ease:cubic-bezier(.22,1,.36,1);
--serif:'Cormorant Garamond','Didot','Bodoni MT','Playfair Display',Georgia,serif;
--sans:-apple-system,'Segoe UI','Helvetica Neue',Arial,sans-serif;
--s2:8px; --s3:12px; --s4:16px; --s6:24px; --s8:32px; --s12:48px; --s16:64px;
}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
html,body{height:100%}
body{
background:var(--bg);
color:var(--ink);
font-family:var(--sans);
overflow-x:hidden;
min-height:100vh;
position:relative;
}
/* ambient drift */
body::before{
content:"";
position:fixed;inset:-30%;
background:
radial-gradient(40% 40% at 25% 20%, rgba(201,169,97,.08), transparent 70%),
radial-gradient(45% 45% at 80% 75%, rgba(125,106,69,.10), transparent 72%);
z-index:0;
animation:drift 22s ease-in-out infinite alternate;
pointer-events:none;
}
@keyframes drift{from{transform:translate3d(-2%,-1%,0) scale(1)}to{transform:translate3d(3%,2%,0) scale(1.08)}}
/* ——— top bar / wordmark lockup ——— */
.topbar{
position:fixed;top:0;left:0;right:0;height:64px;z-index:20;
display:flex;align-items:center;justify-content:space-between;
padding:0 var(--s6);
background:linear-gradient(180deg,rgba(13,13,15,.94),rgba(13,13,15,.72) 70%,transparent);
backdrop-filter:blur(8px);
}
.lockup{
max-width:220px;line-height:1;
border-left:1px solid var(--line);
padding-left:var(--s4);
}
.lockup .l1,.lockup .l2{
font-family:var(--serif);
font-weight:600;
text-transform:uppercase;
letter-spacing:.18em;
color:var(--ink);
display:block;
}
.lockup .l1{font-size:15px}
.lockup .l2{font-size:15px;color:var(--accent)}
.topbar .tag{
font-family:var(--sans);
font-size:10px;letter-spacing:.24em;text-transform:uppercase;
color:var(--muted);
}
/* ——— main ——— */
main{
position:relative;z-index:1;
max-width:900px;margin:0 auto;
padding:96px var(--s6) var(--s12);
text-align:center;
}
.eyebrow{
font-size:11px;letter-spacing:.28em;text-transform:uppercase;color:var(--accent);
margin-bottom:var(--s4);
animation:fadeUp .7s var(--ease) both;
}
h1{
font-family:var(--serif);
font-weight:500;
font-size:clamp(38px,9vw,64px);
line-height:1.02;
letter-spacing:.01em;
margin-bottom:var(--s3);
animation:fadeUp .7s var(--ease) .06s both;
}
h1 em{font-style:italic;color:var(--accent)}
.lede{
font-size:13px;letter-spacing:.22em;text-transform:uppercase;color:var(--muted);
animation:fadeUp .7s var(--ease) .12s both;
}
/* ——— wheel ——— */
.wheel-stage{
margin:var(--s12) auto var(--s8);
width:min(80vw,340px);height:min(80vw,340px);
position:relative;
animation:fadeUp .8s var(--ease) .18s both;
}
.marker{
position:absolute;top:-6px;left:50%;transform:translateX(-50%) rotate(45deg);
width:14px;height:14px;
background:var(--accent);
box-shadow:var(--glow);
z-index:5;border-radius:2px;
}
.marker::after{
content:"";position:absolute;inset:4px;background:var(--bg);border-radius:1px;
}
.wheel{
position:absolute;inset:0;border-radius:50%;
cursor:grab;touch-action:none;
box-shadow:var(--shadow-lg), inset 0 0 0 1px var(--hair);
transition:transform .7s var(--ease);
}
.wheel.dragging{cursor:grabbing;transition:none}
.wheel::after{ /* inner ring */
content:"";position:absolute;inset:14%;border-radius:50%;
box-shadow:inset 0 0 0 1px rgba(245,242,234,.14), 0 0 40px rgba(0,0,0,.4) inset;
pointer-events:none;
}
.hub{
position:absolute;top:50%;left:50%;
width:34%;height:34%;
transform:translate(-50%,-50%);
border-radius:50%;
background:var(--surface);
box-shadow:0 0 0 1px var(--line), 0 8px 22px rgba(0,0,0,.5), var(--glow);
z-index:4;pointer-events:none;
display:flex;align-items:center;justify-content:center;
transition:background .6s var(--ease);
}
.hub-core{
width:58%;height:58%;border-radius:50%;
box-shadow:inset 0 2px 6px rgba(0,0,0,.4), 0 0 0 1px rgba(255,255,255,.14);
transition:background .6s var(--ease);
}
/* ——— selection caption ——— */
.selection{
margin-bottom:var(--s8);min-height:70px;
}
.selection .story{
font-size:11px;letter-spacing:.28em;text-transform:uppercase;color:var(--muted);
margin-bottom:6px;
}
.selection .fam{
font-family:var(--serif);font-style:italic;font-weight:500;
font-size:clamp(28px,7vw,40px);color:var(--accent);
transition:color .5s var(--ease);
}
/* ——— filmstrip ——— */
.filmstrip{
display:flex;gap:var(--s4);
overflow-x:auto;scroll-snap-type:x mandatory;
padding:var(--s2) 2px var(--s6);
justify-content:flex-start;
scrollbar-width:none;
}
.filmstrip::-webkit-scrollbar{display:none}
@media(min-width:760px){ .filmstrip{justify-content:center} }
.film-card{
flex:0 0 auto;width:min(56vw,190px);
scroll-snap-align:center;
background:var(--surface);
border:1px solid var(--hair);
border-radius:14px;overflow:hidden;
box-shadow:var(--shadow);
transition:transform .25s var(--ease),box-shadow .25s var(--ease),border-color .25s var(--ease);
animation:cardIn .6s var(--ease) both;
}
.film-card:hover{
transform:translateY(-4px);
box-shadow:var(--shadow-lg);
border-color:var(--line);
}
.swatch{
height:min(56vw,190px);width:100%;
background-repeat:repeat;
border-bottom:1px solid var(--hair);
}
.cap{padding:var(--s4) var(--s4) var(--s6);text-align:left}
.cap .pname{
display:block;font-family:var(--serif);font-style:italic;
font-size:15px;color:var(--muted);margin-bottom:5px;
}
.cap .cname{
display:block;font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink);
}
.cap .rule{height:1px;background:var(--line);margin:var(--s3) 0 0;width:28px}
/* ——— share bar ——— */
.sharebar{
margin-top:var(--s8);
display:flex;gap:var(--s3);flex-wrap:wrap;justify-content:center;align-items:center;
animation:fadeUp .7s var(--ease) .3s both;
}
.share-btn{
appearance:none;background:transparent;color:var(--ink);
border:1px solid var(--line);border-radius:999px;
padding:11px 20px;
font-family:var(--sans);font-size:11px;letter-spacing:.2em;text-transform:uppercase;
cursor:pointer;
transition:all .2s var(--ease);
display:inline-flex;align-items:center;gap:8px;
}
.share-btn svg{width:13px;height:13px;stroke:var(--accent);fill:none;stroke-width:1.4}
.share-btn:hover{background:var(--accent);color:var(--bg);border-color:var(--accent);box-shadow:var(--glow)}
.share-btn:hover svg{stroke:var(--bg)}
.share-btn.copied{background:var(--accent-deep);color:var(--ink);border-color:var(--accent-deep)}
footer{
margin-top:var(--s12);text-align:center;
}
footer .mark{
font-family:var(--serif);text-transform:uppercase;letter-spacing:.28em;
font-size:12px;color:var(--muted);
}
footer .mark b{color:var(--accent);font-weight:600}
@keyframes fadeUp{from{opacity:0;transform:translateY(28px)}to{opacity:1;transform:none}}
@keyframes cardIn{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:none}}
</style>
</head>
<body>
<header class="topbar">
<div class="lockup">
<span class="l1">Designer</span>
<span class="l2">Wallcoverings</span>
</div>
<span class="tag">Est. Atelier</span>
</header>
<main>
<p class="eyebrow">The Color Wheel</p>
<h1>Shop by <em>Color</em></h1>
<p class="lede">Turn the wheel · tap a hue</p>
<div class="wheel-stage">
<div class="marker"></div>
<div class="wheel" id="wheel"></div>
<div class="hub"><div class="hub-core" id="hubCore"></div></div>
</div>
<div class="selection">
<div class="story">Color Story</div>
<div class="fam" id="famName">Celadon</div>
</div>
<div class="filmstrip" id="filmstrip"></div>
<div class="sharebar">
<button class="share-btn" data-act="share">
<svg viewBox="0 0 24 24"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="M8.6 13.5l6.8 4M15.4 6.5l-6.8 4"/></svg>Share
</button>
<button class="share-btn" data-act="save">
<svg viewBox="0 0 24 24"><path d="M5 3h14v18l-7-5-7 5z"/></svg>Save
</button>
<button class="share-btn" data-act="pin">
<svg viewBox="0 0 24 24"><path d="M12 2a7 7 0 0 1 7 7c0 5-7 13-7 13S5 14 5 9a7 7 0 0 1 7-7z"/><circle cx="12" cy="9" r="2.4"/></svg>Pin
</button>
<button class="share-btn" data-act="copy" id="copyBtn">
<svg viewBox="0 0 24 24"><path d="M10 13a5 5 0 0 0 7 0l2-2a5 5 0 0 0-7-7l-1 1"/><path d="M14 11a5 5 0 0 0-7 0l-2 2a5 5 0 0 0 7 7l1-1"/></svg>Copy Link
</button>
</div>
<footer>
<div class="mark">Designer <b>Wallcoverings</b></div>
</footer>
</main>
<script>
/* ——— color families around the wheel ——— */
const FAMILIES = [
{name:'Celadon', h:155,s:40,l:45, names:['Celadon','Verdigris','Sage','Malachite']},
{name:'Peacock', h:185,s:42,l:42, names:['Peacock','Lagoon','Viridian','Juniper']},
{name:'Cerulean', h:210,s:45,l:44, names:['Cerulean','Prussian','Delft','Slate']},
{name:'Iris', h:235,s:40,l:46, names:['Midnight','Iris','Ink','Twilight']},
{name:'Wisteria', h:270,s:34,l:47, names:['Wisteria','Aubergine','Heather','Plum']},
{name:'Blush', h:335,s:42,l:53, names:['Blush','Rosewood','Coral','Dusk']},
{name:'Carmine', h:353,s:48,l:47, names:['Carmine','Oxblood','Vermilion','Claret']},
{name:'Terracotta',h:25, s:52,l:49, names:['Ochre','Terracotta','Sienna','Saffron']},
{name:'Champagne', h:43, s:50,l:53, names:['Champagne','Honey','Brass','Chamois']},
{name:'Citron', h:70, s:36,l:47, names:['Citron','Absinthe','Moss','Olive']},
{name:'Oatmeal', h:35, s:12,l:56, names:['Oatmeal','Greige','Alabaster','Taupe']},
{name:'Pewter', h:220,s:6, l:32, names:['Pewter','Graphite','Onyx','Ebony']},
];
const N = FAMILIES.length;
const SEG = 360 / N;
const mid = f => `hsl(${f.h} ${f.s}% ${f.l}%)`;
const tones = f => ([
`hsl(${f.h} ${Math.round(f.s*0.7)}% ${Math.round(f.l*0.30)}%)`,
`hsl(${f.h} ${Math.round(f.s*0.9)}% ${Math.round(f.l*0.50)}%)`,
`hsl(${f.h} ${Math.round(f.s*0.7)}% ${Math.round(f.l*0.72)}%)`,
`hsl(${f.h} ${Math.round(f.s*0.45)}% ${Math.round(f.l*0.90)}%)`,
]);
/* ——— CSS-drawn wallpaper patterns ——— */
const PATTERNS = [
{ name:'Damask', build:p=>({
backgroundColor:p[0],
backgroundImage:[
`radial-gradient(ellipse 24% 30% at 50% 18%, ${p[3]} 0 42%, transparent 46%)`,
`radial-gradient(ellipse 15% 22% at 50% 52%, ${p[2]} 0 44%, transparent 48%)`,
`radial-gradient(ellipse 8% 13% at 50% 82%, ${p[3]} 0 50%, transparent 54%)`,
`radial-gradient(circle at 50% 50%, ${p[1]} 0 7%, transparent 8%)`
].join(','),
backgroundSize:'64px 96px', backgroundPosition:'center'
})},
{ name:'Regency Stripe', build:p=>({
backgroundImage:`repeating-linear-gradient(90deg, ${p[0]} 0 15px, ${p[2]} 15px 21px, ${p[3]} 21px 25px, ${p[2]} 25px 31px)`
})},
{ name:'Trellis', build:p=>({
backgroundColor:p[0],
backgroundImage:[
`repeating-linear-gradient(45deg, transparent 0 20px, ${p[3]} 20px 22px)`,
`repeating-linear-gradient(-45deg, transparent 0 20px, ${p[3]} 20px 22px)`,
`radial-gradient(circle at 50% 50%, ${p[1]} 0 12%, transparent 14%)`
].join(','),
backgroundSize:'auto,auto,28px 28px'
})},
{ name:'Sprig', build:p=>({
backgroundColor:p[3],
backgroundImage:[
`radial-gradient(circle at 50% 50%, ${p[1]} 0 16%, transparent 19%)`,
`radial-gradient(circle at 50% 50%, ${p[2]} 0 10%, transparent 13%)`
].join(','),
backgroundSize:'28px 28px,28px 28px',
backgroundPosition:'0 0,14px 14px'
})},
];
/* ——— build the wheel conic gradient ——— */
const wheel = document.getElementById('wheel');
const stops = FAMILIES.map((f,i)=>`${mid(f)} ${i*SEG}deg ${(i+1)*SEG}deg`).join(',');
wheel.style.background = `conic-gradient(from ${-SEG/2}deg, ${stops})`;
const famName = document.getElementById('famName');
const hubCore = document.getElementById('hubCore');
const strip = document.getElementById('filmstrip');
let rotation = 0; // current wheel rotation (deg, clockwise +)
let selected = -1;
function idxFromRotation(r){
return ((Math.round(-r/SEG) % N) + N) % N;
}
function render(idx){
if(idx === selected) return;
selected = idx;
const f = FAMILIES[idx];
const p = tones(f);
famName.textContent = f.name;
famName.style.color = `hsl(${f.h} ${Math.min(f.s+8,60)}% ${Math.max(f.l+8,55)}%)`;
hubCore.style.background = mid(f);
document.querySelector('.hub').style.background = `hsl(${f.h} ${Math.round(f.s*0.4)}% 14%)`;
strip.innerHTML = '';
PATTERNS.forEach((pat,i)=>{
const card = document.createElement('div');
card.className = 'film-card';
card.style.animationDelay = (i*0.09)+'s';
const sw = document.createElement('div');
sw.className = 'swatch';
Object.assign(sw.style, pat.build(p));
const cap = document.createElement('div');
cap.className = 'cap';
cap.innerHTML = `<span class="pname">${pat.name}</span>
<span class="cname">${f.names[i]}</span>
<span class="rule"></span>`;
card.append(sw,cap);
strip.append(card);
});
}
function applyRotation(animate){
wheel.classList.toggle('dragging', !animate);
wheel.style.transform = `rotate(${rotation}deg)`;
}
/* ——— pointer interaction: drag to spin, tap to select ——— */
let dragging=false, startAng=0, startRot=0, moved=0, cx=0, cy=0, downAng=0;
function angleAt(e){
const r = wheel.getBoundingClientRect();
cx = r.left + r.width/2; cy = r.top + r.height/2;
const x = (e.touches?e.touches[0].clientX:e.clientX) - cx;
const y = (e.touches?e.touches[0].clientY:e.clientY) - cy;
return Math.atan2(x, -y) * 180/Math.PI; // clockwise from top
}
function down(e){
dragging=true; moved=0;
downAng = startAng = angleAt(e);
startRot = rotation;
wheel.classList.add('dragging');
}
function move(e){
if(!dragging) return;
e.preventDefault();
let a = angleAt(e);
let d = a - startAng;
if(d>180) d-=360; if(d<-180) d+=360;
moved += Math.abs(d);
rotation = startRot + (a - downAng);
// normalise for continuity
let cont = a - downAng;
if(cont>180) cont-=360; if(cont<-180) cont+=360;
rotation = startRot + cont;
applyRotation(false);
render(idxFromRotation(rotation));
startAng = a;
}
function up(e){
if(!dragging) return;
dragging=false;
if(moved < 6){
// treat as tap on a segment
const a = downAng; // screen angle of tap
const local = a - (rotation % 360);
const j = ((Math.round(local/SEG) % N)+N)%N;
rotation = -j*SEG;
} else {
// snap to nearest segment
rotation = Math.round(rotation/SEG)*SEG;
}
applyRotation(true);
render(idxFromRotation(rotation));
}
wheel.addEventListener('mousedown',down);
window.addEventListener('mousemove',move);
window.addEventListener('mouseup',up);
wheel.addEventListener('touchstart',down,{passive:true});
window.addEventListener('touchmove',move,{passive:false});
window.addEventListener('touchend',up);
/* keyboard for accessibility */
wheel.tabIndex=0;
wheel.addEventListener('keydown',e=>{
if(e.key==='ArrowLeft'||e.key==='ArrowUp'){ rotation += SEG; applyRotation(true); render(idxFromRotation(rotation)); }
if(e.key==='ArrowRight'||e.key==='ArrowDown'){ rotation -= SEG; applyRotation(true); render(idxFromRotation(rotation)); }
});
/* ——— share bar ——— */
document.querySelectorAll('.share-btn').forEach(btn=>{
btn.addEventListener('click',()=>{
const act = btn.dataset.act;
if(act==='copy'){
const txt = `Designer Wallcoverings — Shop by Color: ${FAMILIES[selected].name}`;
if(navigator.clipboard) navigator.clipboard.writeText(txt).catch(()=>{});
btn.classList.add('copied');
const label = btn.childNodes[btn.childNodes.length-1];
const prev = label.textContent;
label.textContent = 'Copied';
setTimeout(()=>{btn.classList.remove('copied');label.textContent=prev;},1400);
} else if(act==='share' && navigator.share){
navigator.share({title:'Designer Wallcoverings',text:`Color story: ${FAMILIES[selected].name}`}).catch(()=>{});
}
});
});
/* init */
applyRotation(true);
render(0);
</script>
</body>
</html>