← back to Model Arena
data/artifacts/15fb25164101/kimi.html
729 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>
/* ============================================
DESIGN TOKENS — LUXURY SYSTEM
============================================ */
:root {
/* Color Palette: luxe-noir */
--bg: #0d0d0f;
--surface: #17171c;
--surface-elevated: #1e1e26;
--ink: #f5f2ea;
--muted: #8a8578;
--accent: #c9a961;
--accent-dim: #7d6a45;
/* Neutrals */
--line: rgba(245, 242, 234, 0.08);
--line-strong: rgba(245, 242, 234, 0.2);
--shadow: rgba(0, 0, 0, 0.4);
--shadow-soft: rgba(0, 0, 0, 0.2);
/* Typography */
--font-display: "Playfair Display", "Didot", "Bodoni MT", "EB Garamond", Georgia, serif;
--font-body: "Avenir Next", "Century Gothic", Futura, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
/* Scale */
--text-caption: 11px;
--text-body: 15px;
--text-lead: 18px;
--text-h3: 26px;
--text-h2: 34px;
/* Motion */
--ease-out: cubic-bezier(.22, .9, .24, 1);
--ease-in-out: cubic-bezier(.77, 0, .18, 1);
--ease-spring: cubic-bezier(.34, 1.56, .64, 1);
--duration-micro: 200ms;
--duration-enter: 600ms;
--duration-move: 400ms;
/* Spacing */
--space-xs: 8px;
--space-sm: 16px;
--space-md: 24px;
--space-lg: 40px;
--space-xl: 64px;
/* Layout */
--header-height: 72px;
--wheel-size: min(72vw, 420px);
--filmstrip-height: 180px;
}
/* ============================================
RESET & BASE
============================================ */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
html {
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: var(--font-body);
font-size: var(--text-body);
line-height: 1.6;
color: var(--ink);
background: var(--bg);
min-height: 100vh;
overflow-x: hidden;
}
/* ============================================
HEADER — FIXED TOP BAR
============================================ */
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: var(--header-height);
background: rgba(13, 13, 15, 0.92);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border-bottom: 1px solid var(--line);
z-index: 1000;
display: flex;
align-items: center;
padding: 0 var(--space-md);
}
.wordmark {
font-family: var(--font-display);
font-size: 13px;
font-weight: 500;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink);
max-width: 220px;
line-height: 1;
}
/* ============================================
MAIN LAYOUT
============================================ */
.main {
padding-top: var(--header-height);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding-bottom: var(--space-xl);
}
.hero-zone {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
padding: var(--space-lg) var(--space-md);
position: relative;
}
.section-label {
font-size: var(--text-caption);
font-weight: 500;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: var(--space-sm);
}
/* ============================================
COLOR WHEEL
============================================ */
.wheel-container {
position: relative;
width: var(--wheel-size);
height: var(--wheel-size);
margin: var(--space-md) 0 var(--space-lg);
touch-action: none;
user-select: none;
}
.wheel {
width: 100%;
height: 100%;
border-radius: 50%;
position: relative;
background: conic-gradient(
from 0deg,
hsl(0, 65%, 45%),
hsl(30, 65%, 45%),
hsl(60, 55%, 50%),
hsl(90, 45%, 45%),
hsl(120, 40%, 40%),
hsl(150, 45%, 38%),
hsl(180, 50%, 40%),
hsl(210, 55%, 45%),
hsl(240, 60%, 50%),
hsl(270, 55%, 48%),
hsl(300, 50%, 45%),
hsl(330, 60%, 48%),
hsl(360, 65%, 45%)
);
box-shadow:
inset 0 0 0 1px var(--line-strong),
0 24px 60px var(--shadow),
0 0 0 12px var(--surface);
transition: transform var(--duration-move) var(--ease-out);
}
.wheel::before {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
background: radial-gradient(circle at 35% 35%, rgba(255,255,255,0.12), transparent 50%);
pointer-events: none;
}
.wheel-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 28%;
height: 28%;
background: var(--surface-elevated);
border-radius: 50%;
border: 1px solid var(--line-strong);
box-shadow:
0 8px 32px var(--shadow),
inset 0 1px 0 rgba(255,255,255,0.05);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.wheel-indicator {
position: absolute;
top: 8%;
left: 50%;
transform: translateX(-50%);
width: 4px;
height: 16px;
background: var(--accent);
border-radius: 2px;
box-shadow: 0 0 12px var(--accent);
transition: all var(--duration-micro) var(--ease-out);
}
.color-readout {
text-align: center;
padding: var(--space-xs) var(--space-sm);
}
.color-name {
font-family: var(--font-display);
font-size: var(--text-h3);
font-weight: 500;
color: var(--ink);
line-height: 1.2;
margin-bottom: 4px;
transition: opacity var(--duration-micro) var(--ease-out);
}
.color-hue {
font-size: var(--text-caption);
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(--muted);
}
/* ============================================
FILMSTRIP — PATTERN SHOWCASE
============================================ */
.filmstrip-container {
width: 100%;
max-width: 720px;
margin: 0 auto;
padding: 0 var(--space-md);
}
.filmstrip {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-sm);
height: var(--filmstrip-height);
opacity: 0;
transform: translateY(20px);
transition:
opacity var(--duration-enter) var(--ease-out),
transform var(--duration-enter) var(--ease-out);
}
.filmstrip.active {
opacity: 1;
transform: translateY(0);
}
.pattern-tile {
position: relative;
background: var(--surface);
border-radius: 4px;
overflow: hidden;
border: 1px solid var(--line);
box-shadow: 0 4px 20px var(--shadow-soft);
cursor: pointer;
transition:
transform var(--duration-micro) var(--ease-out),
box-shadow var(--duration-micro) var(--ease-out);
}
.pattern-tile:hover {
transform: translateY(-4px);
box-shadow: 0 12px 40px var(--shadow);
}
.pattern-tile::before {
content: '';
position: absolute;
inset: 0;
opacity: 0.9;
transition: opacity var(--duration-micro) var(--ease-out);
}
.pattern-tile:hover::before {
opacity: 1;
}
/* Pattern 1: Herringbone */
.pattern-herringbone::before {
background:
repeating-linear-gradient(
45deg,
transparent,
transparent 6px,
currentColor 6px,
currentColor 7px
),
repeating-linear-gradient(
-45deg,
transparent,
transparent 6px,
currentColor 6px,
currentColor 7px
);
color: var(--pattern-color, var(--accent));
}
/* Pattern 2: Damask-inspired radial */
.pattern-damask::before {
background:
radial-gradient(circle at 30% 30%, currentColor 0%, transparent 25%),
radial-gradient(circle at 70% 70%, currentColor 0%, transparent 25%),
radial-gradient(circle at 50% 50%, transparent 30%, currentColor 31%, currentColor 32%, transparent 33%);
color: var(--pattern-color, var(--accent));
background-size: 40px 40px;
}
/* Pattern 3: Pinstripe */
.pattern-stripe::before {
background: repeating-linear-gradient(
90deg,
var(--surface),
var(--surface) 3px,
currentColor 3px,
currentColor 4px
);
color: var(--pattern-color, var(--accent));
}
/* Pattern 4: Organic texture */
.pattern-organic::before {
background:
radial-gradient(ellipse 20px 30px at 20% 30%, currentColor 0%, transparent 50%),
radial-gradient(ellipse 25px 20px at 70% 60%, currentColor 0%, transparent 45%),
radial-gradient(ellipse 15px 25px at 50% 80%, currentColor 0%, transparent 40%);
color: var(--pattern-color, var(--accent));
background-size: 60px 60px;
}
.pattern-label {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: var(--space-xs) var(--space-sm);
background: linear-gradient(to top, rgba(13,13,15,0.95), transparent);
font-size: 10px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ink);
opacity: 0.8;
}
/* ============================================
SHARE BAR
============================================ */
.share-zone {
margin-top: var(--space-xl);
padding: var(--space-md);
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-sm);
}
.share-label {
font-size: var(--text-caption);
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(--muted);
}
.share-bar {
display: flex;
gap: var(--space-sm);
}
.share-btn {
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--surface);
border: 1px solid var(--line);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition:
background var(--duration-micro) var(--ease-out),
border-color var(--duration-micro) var(--ease-out),
transform var(--duration-micro) var(--ease-out);
}
.share-btn:hover {
background: var(--surface-elevated);
border-color: var(--accent);
transform: scale(1.08);
}
.share-btn svg {
width: 18px;
height: 18px;
fill: var(--ink);
transition: fill var(--duration-micro) var(--ease-out);
}
.share-btn:hover svg {
fill: var(--accent);
}
/* ============================================
INSTRUCTION HINT
============================================ */
.hint {
margin-top: var(--space-md);
font-size: var(--text-caption);
letter-spacing: 0.1em;
color: var(--muted);
opacity: 0.7;
display: flex;
align-items: center;
gap: var(--space-xs);
}
.hint::before {
content: '◉';
font-size: 8px;
color: var(--accent);
}
/* ============================================
ANIMATIONS
============================================ */
@keyframes pulse-glow {
0%, 100% { box-shadow: 0 0 12px var(--accent); }
50% { box-shadow: 0 0 20px var(--accent), 0 0 30px rgba(201, 169, 97, 0.3); }
}
.wheel-indicator.active {
animation: pulse-glow 2s var(--ease-in-out) infinite;
}
/* ============================================
RESPONSIVE
============================================ */
@media (max-width: 640px) {
:root {
--wheel-size: 80vw;
--filmstrip-height: 140px;
}
.filmstrip {
grid-template-columns: repeat(2, 1fr);
height: auto;
gap: var(--space-xs);
}
.pattern-tile {
aspect-ratio: 1;
}
.color-name {
font-size: 22px;
}
.wordmark {
font-size: 11px;
letter-spacing: 0.15em;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<header class="header">
<div class="wordmark">Designer Wallcoverings</div>
</header>
<main class="main">
<div class="hero-zone">
<span class="section-label">Shop by Color</span>
<div class="wheel-container" id="wheelContainer">
<div class="wheel" id="wheel">
<div class="wheel-indicator" id="indicator"></div>
</div>
<div class="wheel-center">
<div class="color-readout">
<div class="color-name" id="colorName">Select Hue</div>
<div class="color-hue" id="colorHue">Drag wheel</div>
</div>
</div>
</div>
<p class="hint">Drag or tap the wheel to explore</p>
</div>
<div class="filmstrip-container">
<div class="filmstrip" id="filmstrip">
<div class="pattern-tile pattern-herringbone" data-pattern="Herringbone">
<span class="pattern-label">Herringbone</span>
</div>
<div class="pattern-tile pattern-damask" data-pattern="Damask">
<span class="pattern-label">Damask</span>
</div>
<div class="pattern-tile pattern-stripe" data-pattern="Pinstripe">
<span class="pattern-label">Pinstripe</span>
</div>
<div class="pattern-tile pattern-organic" data-pattern="Lacuna">
<span class="pattern-label">Lacuna</span>
</div>
</div>
</div>
<div class="share-zone">
<span class="share-label">Share This Palette</span>
<div class="share-bar">
<button class="share-btn" aria-label="Copy link">
<svg viewBox="0 0 24 24"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
</button>
<button class="share-btn" aria-label="Share to Pinterest">
<svg viewBox="0 0 24 24"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.08 3.16 9.42 7.62 11.16-.1-.95-.2-2.4.04-3.44l1.38-5.88s-.35-.7-.35-1.74c0-1.62.94-2.84 2.1-2.84 1 0 1.48.74 1.48 1.64 0 1-.64 2.48-.96 3.86-.28 1.14.58 2.08 1.72 2.08 2.06 0 3.66-2.18 3.66-5.32 0-2.78-2-4.72-4.84-4.72-3.3 0-5.24 2.48-5.24 5.04 0 1 .38 2.08.86 2.66.1.1.1.2.08.32l-.32 1.24c-.06.2-.18.26-.4.16-1.48-.7-2.4-2.84-2.4-4.58 0-3.72 2.7-7.14 7.78-7.14 4.08 0 7.26 2.92 7.26 6.82 0 4.06-2.56 7.32-6.12 7.32-1.2 0-2.32-.62-2.7-1.36l-.74 2.82c-.26 1.02-.1 2.28-.04 2.54C21.18 20.26 24 16.42 24 12c0-6.63-5.37-12-12-12z"/></svg>
</button>
<button class="share-btn" aria-label="Share to Instagram">
<svg viewBox="0 0 24 24"><path d="M12 2.16c3.2 0 3.58.01 4.85.07 3.25.15 4.77 1.69 4.92 4.92.06 1.27.07 1.65.07 4.85 0 3.2-.01 3.58-.07 4.85-.15 3.23-1.66 4.77-4.92 4.92-1.27.06-1.64.07-4.85.07-3.2 0-3.58-.01-4.85-.07-3.26-.15-4.77-1.7-4.92-4.92-.06-1.27-.07-1.64-.07-4.85 0-3.2.01-3.58.07-4.85.15-3.23 1.66-4.77 4.92-4.92 1.27-.06 1.64-.07 4.85-.07M12 0C8.74 0 8.33.01 7.05.07 2.7.27.27 2.7.07 7.05.01 8.33 0 8.74 0 12s.01 3.67.07 4.95c.2 4.35 2.63 6.78 6.98 6.98 1.28.06 1.69.07 4.95.07s3.67-.01 4.95-.07c4.35-.2 6.78-2.63 6.98-6.98.06-1.28.07-1.69.07-4.95s-.01-3.67-.07-4.95c-.2-4.35-2.63-6.78-6.98-6.98C15.67.01 15.26 0 12 0zm0 5.84a6.16 6.16 0 1 0 0 12.32 6.16 6.16 0 0 0 0-12.32zM12 16a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm7.85-10.4a1.44 1.44 0 1 0 0 2.88 1.44 1.44 0 0 0 0-2.88z"/></svg>
</button>
</div>
</div>
</main>
<script>
/* ============================================
COLOR WHEEL INTERACTION
============================================ */
const wheel = document.getElementById('wheel');
const wheelContainer = document.getElementById('wheelContainer');
const indicator = document.getElementById('indicator');
const colorName = document.getElementById('colorName');
const colorHue = document.getElementById('colorHue');
const filmstrip = document.getElementById('filmstrip');
const patternTiles = document.querySelectorAll('.pattern-tile');
// Designer color names mapped to hue ranges
const colorFamilies = [
{ min: 0, max: 15, name: 'Vermillion', hue: 'Red' },
{ min: 15, max: 35, name: 'Terra Cotta', hue: 'Orange' },
{ min: 35, max: 50, name: 'Amber', hue: 'Gold' },
{ min: 50, max: 70, name: 'Saffron', hue: 'Yellow' },
{ min: 70, max: 95, name: 'Olive', hue: 'Green' },
{ min: 95, max: 140, name: 'Sage', hue: 'Green' },
{ min: 140, max: 170, name: 'Verdigris', hue: 'Teal' },
{ min: 170, max: 200, name: 'Petrol', hue: 'Blue' },
{ min: 200, max: 230, name: 'Indigo', hue: 'Blue' },
{ min: 230, max: 260, name: 'Lapis', hue: 'Blue' },
{ min: 260, max: 290, name: 'Amethyst', hue: 'Purple' },
{ min: 290, max: 320, name: 'Aubergine', hue: 'Purple' },
{ min: 320, max: 340, name: 'Claret', hue: 'Red' },
{ min: 340, max: 360, name: 'Crimson', hue: 'Red' }
];
let currentRotation = 0;
let isDragging = false;
let startAngle = 0;
let startRotation = 0;
let currentHue = 0;
function getAngle(x, y) {
const rect = wheelContainer.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
return Math.atan2(y - centerY, x - centerX) * 180 / Math.PI;
}
function updateColorFromRotation(rotation) {
// Normalize rotation to 0-360
const normalized = ((rotation % 360) + 360) % 360;
currentHue = normalized;
// Find color family
const family = colorFamilies.find(f => normalized >= f.min && normalized < f.max) || colorFamilies[0];
// Update text
colorName.textContent = family.name;
colorHue.textContent = `${Math.round(normalized)}° — ${family.hue}`;
// Update pattern colors
const saturation = 45 + (normalized % 30) / 30 * 20; // Vary saturation slightly
const lightness = 40 + (normalized % 45) / 45 * 15; // Vary lightness
const colorString = `hsl(${normalized}, ${saturation}%, ${lightness}%)`;
patternTiles.forEach((tile, index) => {
tile.style.setProperty('--pattern-color', colorString);
// Stagger animation
setTimeout(() => {
tile.style.opacity = '1';
tile.style.transform = 'translateY(0)';
}, index * 80);
});
// Show filmstrip
filmstrip.classList.add('active');
indicator.classList.add('active');
}
function handleStart(e) {
isDragging = true;
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
const clientY = e.touches ? e.touches[0].clientY : e.clientY;
startAngle = getAngle(clientX, clientY);
startRotation = currentRotation;
wheelContainer.style.cursor = 'grabbing';
indicator.style.transform = 'translateX(-50%) scale(1.2)';
}
function handleMove(e) {
if (!isDragging) return;
e.preventDefault();
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
const clientY = e.touches ? e.touches[0].clientY : e.clientY;
const angle = getAngle(clientX, clientY);
const delta = angle - startAngle;
currentRotation = startRotation + delta;
wheel.style.transform = `rotate(${currentRotation}deg)`;
updateColorFromRotation(currentRotation);
}
function handleEnd() {
isDragging = false;
wheelContainer.style.cursor = 'grab';
indicator.style.transform = 'translateX(-50%) scale(1)';
// Snap to nearest 5 degrees for tactile feel
const snap = Math.round(currentRotation / 5) * 5;
currentRotation = snap;
wheel.style.transition = 'transform 400ms cubic-bezier(.22, .9, .24, 1)';
wheel.style.transform = `rotate(${currentRotation}deg)`;
setTimeout(() => {
wheel.style.transition = 'transform 400ms cubic-bezier(.22, .9, .24, 1)';
}, 400);
}
// Event listeners
wheelContainer.addEventListener('mousedown', handleStart);
wheelContainer.addEventListener('touchstart', handleStart, { passive: false });
document.addEventListener('mousemove', handleMove);
document.addEventListener('touchmove', handleMove, { passive: false });
document.addEventListener('mouseup', handleEnd);
document.addEventListener('touchend', handleEnd);
// Click to jump
wheelContainer.addEventListener('click', (e) => {
if (isDragging) return;
const rect = wheelContainer.getBoundingClientRect();
const clickX = e.clientX - rect.left;
const clickY = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
// Calculate angle from center
const angle = Math.atan2(clickY - centerY, clickX - centerX) * 180 / Math.PI;
const targetRotation = angle + 90; // Offset for indicator position
currentRotation = targetRotation;
wheel.style.transform = `rotate(${currentRotation}deg)`;
updateColorFromRotation(currentRotation);
});
// Initialize
wheelContainer.style.cursor = 'grab';
patternTiles.forEach(tile => {
tile.style.opacity = '0';
tile.style.transform = 'translateY(10px)';
tile.style.transition = 'opacity 300ms ease, transform 300ms ease';
});
// Share buttons
document.querySelectorAll('.share-btn').forEach(btn => {
btn.addEventListener('click', () => {
const originalTransform = btn.style.transform;
btn.style.transform = 'scale(0.92)';
setTimeout(() => {
btn.style.transform = originalTransform;
}, 150);
// Copy link functionality
if (btn.getAttribute('aria-label') === 'Copy link') {
const url = window.location.href;
navigator.clipboard.writeText(url).then(() => {
const original = btn.innerHTML;
btn.innerHTML = '<svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>';
setTimeout(() => {
btn.innerHTML = original;
}, 1500);
});
}
});
});
</script>
</body>
</html>