← back to Nova Recolor
draperies/index.html
144 lines
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Novasuede — Drapery Color Library (Reinhard)</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{
font-family:-apple-system,BlinkMacSystemFont,sans-serif;
color:#fff;
min-height:100vh;
background:#1a1a1a;
transition:background-image .3s ease;
background-size:cover;
background-position:center;
background-repeat:no-repeat;
background-attachment:fixed;
}
body.has-bg::before{
content:"";
position:fixed;inset:0;background:rgba(0,0,0,.35);z-index:-1;pointer-events:none;
}
header{padding:24px 28px 12px;background:rgba(0,0,0,.55);backdrop-filter:blur(8px);position:sticky;top:0;z-index:10;border-bottom:1px solid rgba(255,255,255,.08)}
h1{font-size:22px;font-weight:600;letter-spacing:-.01em}
.sub{font-size:13px;opacity:.75;margin-top:4px}
.toolbar{display:flex;gap:12px;align-items:center;margin-top:14px;flex-wrap:wrap}
.toolbar input{background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.18);color:#fff;padding:8px 12px;border-radius:8px;font:inherit;min-width:240px;outline:none}
.toolbar input::placeholder{color:rgba(255,255,255,.5)}
.toolbar button{background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.22);color:#fff;padding:8px 14px;border-radius:8px;font:inherit;cursor:pointer}
.toolbar button:hover{background:rgba(255,255,255,.22)}
.grid{
display:grid;
grid-template-columns:repeat(auto-fill,minmax(110px,1fr));
gap:8px;
padding:18px;
}
.chip{
aspect-ratio:1.4/1;
border-radius:10px;
cursor:pointer;
border:2px solid rgba(255,255,255,.22);
transition:transform .12s, border-color .12s, box-shadow .15s;
position:relative;
overflow:hidden;
box-shadow:0 1px 4px rgba(0,0,0,.4);
}
.chip:hover{transform:translateY(-2px);border-color:rgba(255,255,255,.6);box-shadow:0 6px 18px rgba(0,0,0,.5)}
.chip.active{border-color:#fff;box-shadow:0 0 0 3px rgba(255,255,255,.55), 0 6px 18px rgba(0,0,0,.6)}
.chip .label{
position:absolute;left:0;right:0;bottom:0;padding:6px 8px;
background:linear-gradient(transparent, rgba(0,0,0,.78));
font-size:11px;font-weight:600;letter-spacing:.01em;
text-shadow:0 1px 2px rgba(0,0,0,.7);text-transform:capitalize;
}
.status{position:fixed;left:18px;bottom:18px;padding:10px 14px;border-radius:10px;background:rgba(0,0,0,.7);backdrop-filter:blur(6px);font-size:13px;font-weight:600;letter-spacing:.01em;border:1px solid rgba(255,255,255,.18);min-width:180px}
.status .name{font-size:15px;text-transform:capitalize;margin-bottom:2px}
.status .meta{font-size:11px;opacity:.65;font-weight:400}
.reset{position:fixed;right:18px;bottom:18px;padding:10px 16px;border-radius:10px;background:rgba(0,0,0,.7);backdrop-filter:blur(6px);font-size:13px;font-weight:600;border:1px solid rgba(255,255,255,.18);cursor:pointer;color:#fff}
.reset:hover{background:rgba(0,0,0,.85)}
</style>
</head>
<body>
<header>
<h1>Novasuede™ — Drapery Color Library</h1>
<div class="sub">Click any color to set the full-bleed drapery as the page background. Reinhard color transfer applied to a single ruby drapery base across <span id="cnt">117</span> Novasuede colors.</div>
<div class="toolbar">
<input id="filter" type="search" placeholder="Filter colors…">
<button id="random">Random</button>
</div>
</header>
<div class="grid" id="grid"></div>
<div class="status" id="status" style="display:none">
<div class="name" id="status-name">—</div>
<div class="meta" id="status-meta"></div>
</div>
<button class="reset" onclick="reset()">Reset</button>
<script>
let manifest = [];
const grid = document.getElementById('grid');
const status = document.getElementById('status');
const statusName = document.getElementById('status-name');
const statusMeta = document.getElementById('status-meta');
fetch('manifest.json').then(r => r.json()).then(m => {
manifest = m;
document.getElementById('cnt').textContent = m.length;
render(m);
});
function render(items){
grid.innerHTML = '';
items.forEach(it => {
const chip = document.createElement('div');
chip.className = 'chip';
chip.style.background = it.hex;
chip.dataset.slug = it.slug;
chip.dataset.hex = it.hex;
chip.dataset.drapery = it.drapery;
chip.title = it.slug + ' · ' + it.hex;
const label = document.createElement('div');
label.className = 'label';
label.textContent = it.slug.replace(/-/g, ' ');
chip.appendChild(label);
chip.addEventListener('click', () => activate(chip));
grid.appendChild(chip);
});
}
function activate(chip){
document.querySelectorAll('.chip.active').forEach(c => c.classList.remove('active'));
chip.classList.add('active');
const url = chip.dataset.drapery;
document.body.classList.add('has-bg');
document.body.style.backgroundImage = 'url("' + url + '")';
statusName.textContent = chip.dataset.slug.replace(/-/g, ' ');
statusMeta.textContent = chip.dataset.hex + ' · ' + url;
status.style.display = 'block';
}
function reset(){
document.querySelectorAll('.chip.active').forEach(c => c.classList.remove('active'));
document.body.classList.remove('has-bg');
document.body.style.backgroundImage = '';
status.style.display = 'none';
}
document.getElementById('filter').addEventListener('input', e => {
const q = e.target.value.trim().toLowerCase();
const filtered = !q ? manifest : manifest.filter(m => m.slug.includes(q));
render(filtered);
});
document.getElementById('random').addEventListener('click', () => {
const chips = document.querySelectorAll('.chip');
if (chips.length) activate(chips[Math.floor(Math.random()*chips.length)]);
});
</script>
</body>
</html>