Initial commit
This commit is contained in:
@@ -0,0 +1,525 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex · Dropdown vs. ACTIVE strip — 10 fixes</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0a0710;
|
||||
--bg-1: #14101e;
|
||||
--bg-2: #1c1729;
|
||||
--fg: #e8e6f0;
|
||||
--fg-dim: #a8a4b8;
|
||||
--fg-muted: #6e6a80;
|
||||
--cyan: #22d3ee;
|
||||
--violet: #a78bfa;
|
||||
--mint: #34d399;
|
||||
--coral: #fb7185;
|
||||
--amber: #fbbf24;
|
||||
--glass: rgba(255,255,255,0.04);
|
||||
--glass-strong: rgba(255,255,255,0.08);
|
||||
--border: rgba(255,255,255,0.08);
|
||||
--border-strong: rgba(255,255,255,0.18);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0; background: var(--bg-0); color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.page { max-width: 1500px; margin: 0 auto; padding: 24px; }
|
||||
.page-header {
|
||||
margin-bottom: 24px; padding: 16px 20px; border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(34,211,238,0.08), rgba(167,139,250,0.05));
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.page-header h1 { margin: 0 0 8px 0; font-size: 22px; font-weight: 600; }
|
||||
.page-header p { margin: 0; color: var(--fg-dim); font-size: 13px; }
|
||||
|
||||
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
|
||||
|
||||
.variant {
|
||||
border: 1px solid var(--border); border-radius: 14px; background: var(--bg-1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.variant-head {
|
||||
padding: 12px 16px; border-bottom: 1px solid var(--border); background: var(--glass);
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
}
|
||||
.variant-num {
|
||||
width: 26px; height: 26px; border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--cyan), var(--violet));
|
||||
display: grid; place-items: center;
|
||||
font-weight: 700; font-size: 12px; color: #0a0710;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.variant-title { font-size: 15px; font-weight: 600; }
|
||||
.variant-desc { color: var(--fg-dim); font-size: 12px; }
|
||||
.variant-body {
|
||||
padding: 24px 18px; background: var(--bg-0);
|
||||
min-height: 320px;
|
||||
display: flex; align-items: flex-start; justify-content: center;
|
||||
}
|
||||
|
||||
/* shared bits */
|
||||
.toolbar {
|
||||
display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
|
||||
}
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
padding: 5px 12px; border-radius: 999px;
|
||||
background: var(--glass); border: 1px solid var(--border-strong);
|
||||
font-size: 12px; color: var(--fg-dim);
|
||||
white-space: nowrap; min-width: 100px; justify-content: space-between;
|
||||
}
|
||||
.pill .cv { font-size: 9px; opacity: 0.5; }
|
||||
.pill.all { background: rgba(34,211,238,0.15); border-color: rgba(34,211,238,0.4); color: var(--cyan); min-width: auto; }
|
||||
.pill.active { background: rgba(34,211,238,0.10); border-color: rgba(34,211,238,0.4); color: var(--cyan); }
|
||||
.pill.disabled { opacity: 0.4; }
|
||||
.pill .badge {
|
||||
background: var(--cyan); color: #0a0710; padding: 0 5px;
|
||||
border-radius: 999px; font-size: 9px; font-weight: 700;
|
||||
font-family: ui-monospace, monospace; min-width: 16px;
|
||||
height: 16px; display: inline-grid; place-items: center;
|
||||
}
|
||||
|
||||
.strip {
|
||||
display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
|
||||
padding: 6px 10px; border: 1px dashed var(--border-strong);
|
||||
background: rgba(34,211,238,0.05);
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.strip .label {
|
||||
color: var(--fg-muted); font-family: ui-monospace, monospace;
|
||||
font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.chip {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 2px 4px 2px 10px; border-radius: 999px;
|
||||
background: rgba(34,211,238,0.12); border: 1px solid rgba(34,211,238,0.4);
|
||||
color: var(--cyan); font-family: ui-monospace, monospace; font-size: 11px;
|
||||
}
|
||||
.chip.amber { background: rgba(251,191,36,0.12); border-color: rgba(251,191,36,0.4); color: var(--amber); }
|
||||
.chip.violet { background: rgba(167,139,250,0.12); border-color: rgba(167,139,250,0.4); color: var(--violet); }
|
||||
.chip .x { padding: 0 4px; opacity: 0.6; cursor: pointer; }
|
||||
|
||||
.dropdown {
|
||||
background: var(--bg-0);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 16px 40px rgba(0,0,0,0.6);
|
||||
width: 320px; padding: 14px;
|
||||
color: var(--fg-dim); font-size: 13px;
|
||||
}
|
||||
.dropdown .row { padding: 6px 0; display: flex; gap: 8px; }
|
||||
.dropdown .row.on { color: var(--cyan); }
|
||||
.dropdown .seg {
|
||||
display: flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden;
|
||||
margin: 2px 0;
|
||||
}
|
||||
.dropdown .seg .opt {
|
||||
flex: 1; text-align: center; padding: 4px;
|
||||
font-size: 10px; color: var(--fg-muted); font-family: ui-monospace, monospace;
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
.dropdown .seg .opt:last-child { border: 0; }
|
||||
.dropdown .seg .opt.on { background: rgba(34,211,238,0.2); color: var(--cyan); }
|
||||
|
||||
.arrow {
|
||||
color: var(--coral); font-size: 18px; align-self: center;
|
||||
margin: 0 4px;
|
||||
}
|
||||
.note { color: var(--fg-muted); font-size: 11px; padding: 6px 0; font-style: italic; }
|
||||
|
||||
/* layout helpers per variant */
|
||||
.stack { display: flex; flex-direction: column; align-items: flex-start; gap: 0; width: 100%; }
|
||||
.canvas {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(rgba(255,255,255,0.02), rgba(255,255,255,0));
|
||||
padding: 10px;
|
||||
border: 1px solid var(--border);
|
||||
min-height: 280px;
|
||||
position: relative;
|
||||
}
|
||||
.placeholder {
|
||||
height: 60px; border: 1px dashed var(--border); border-radius: 8px;
|
||||
margin-top: 8px;
|
||||
display: grid; place-items: center; color: var(--fg-muted);
|
||||
font-size: 11px; font-style: italic;
|
||||
}
|
||||
.pos-rel { position: relative; }
|
||||
.pop-down { position: absolute; left: 220px; top: 38px; z-index: 4; }
|
||||
.pop-up { position: absolute; left: 220px; bottom: 38px; z-index: 4; }
|
||||
.pop-side { position: absolute; left: calc(100% + 6px); top: 0; z-index: 4; }
|
||||
|
||||
.pick {
|
||||
margin: 32px 0 12px; padding: 14px 18px;
|
||||
border-radius: 12px; background: rgba(52,211,153,0.07);
|
||||
border: 1px solid rgba(52,211,153,0.25); color: var(--mint); font-size: 13px;
|
||||
}
|
||||
.pick strong { color: var(--mint); }
|
||||
.pick em { color: var(--fg); font-style: normal; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="page-header">
|
||||
<h1>Active strip vs. dropdown — 10 fixes</h1>
|
||||
<p>Currently the Filter dropdown opens downward and covers the ACTIVE strip below the toolbar. 10 ways to solve it.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
|
||||
<!-- 1 -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">1</div>
|
||||
<div>
|
||||
<div class="variant-title">Move ACTIVE above the toolbar</div>
|
||||
<div class="variant-desc">Strip pinned to the top. Dropdown opens down into empty space, never collides.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="canvas">
|
||||
<div class="strip">
|
||||
<span class="label">ACTIVE</span>
|
||||
<span class="chip">VIP <span class="x">×</span></span>
|
||||
<span class="chip amber">Favorite <span class="x">×</span></span>
|
||||
<span class="chip violet">Owned <span class="x">×</span></span>
|
||||
</div>
|
||||
<div style="height: 8px;"></div>
|
||||
<div class="toolbar pos-rel">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span>📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active"><span>🔖 Filter <span class="badge">3</span></span><span class="cv">▾</span></span>
|
||||
<div class="pop-down">
|
||||
<div class="dropdown">
|
||||
<div class="row on">◆ VIP ✓</div>
|
||||
<div class="row on amber" style="color: var(--amber);">★ Favorite ✓</div>
|
||||
<div class="row on violet" style="color: var(--violet);">▣ Owned ✓</div>
|
||||
<div class="row">⊖ Unmarked</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="placeholder">Cover grid</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2 -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">2</div>
|
||||
<div>
|
||||
<div class="variant-title">Open dropdown upward</div>
|
||||
<div class="variant-desc">Drop direction flips when the strip is present. Strip stays put.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="canvas" style="padding-top: 80px;">
|
||||
<div class="toolbar pos-rel">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span>📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active"><span>🔖 Filter <span class="badge">3</span></span><span class="cv">▾</span></span>
|
||||
<div class="pop-up">
|
||||
<div class="dropdown">
|
||||
<div class="row on">◆ VIP ✓</div>
|
||||
<div class="row" style="color: var(--amber);">★ Favorite ✓</div>
|
||||
<div class="row" style="color: var(--violet);">▣ Owned ✓</div>
|
||||
<div class="row">⊖ Unmarked</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="strip">
|
||||
<span class="label">ACTIVE</span>
|
||||
<span class="chip">VIP <span class="x">×</span></span>
|
||||
<span class="chip amber">Favorite <span class="x">×</span></span>
|
||||
<span class="chip violet">Owned <span class="x">×</span></span>
|
||||
</div>
|
||||
<div class="placeholder">Cover grid</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3 -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">3</div>
|
||||
<div>
|
||||
<div class="variant-title">Inline expand (push, don't overlay)</div>
|
||||
<div class="variant-desc">Dropdown is part of the flow. Opening it slides everything below down.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="canvas">
|
||||
<div class="toolbar">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span>📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active"><span>🔖 Filter <span class="badge">3</span></span><span class="cv">▴</span></span>
|
||||
</div>
|
||||
<div class="dropdown" style="margin-top: 6px; width: 100%;">
|
||||
<div class="row on">◆ VIP ✓ <span style="color: var(--amber);">★ Favorite ✓</span> <span style="color: var(--violet);">▣ Owned ✓</span> ⊖ Unmarked</div>
|
||||
</div>
|
||||
<div class="strip">
|
||||
<span class="label">ACTIVE</span>
|
||||
<span class="chip">VIP <span class="x">×</span></span>
|
||||
<span class="chip amber">Favorite <span class="x">×</span></span>
|
||||
<span class="chip violet">Owned <span class="x">×</span></span>
|
||||
</div>
|
||||
<div class="placeholder">Cover grid (pushed down)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4 -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">4</div>
|
||||
<div>
|
||||
<div class="variant-title">Chips inside the dropdown header</div>
|
||||
<div class="variant-desc">Active state lives inside the popover top — visible while editing, vanishes when closed.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="canvas">
|
||||
<div class="toolbar pos-rel">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span>📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active"><span>🔖 Filter <span class="badge">3</span></span><span class="cv">▾</span></span>
|
||||
<div class="pop-down">
|
||||
<div class="dropdown">
|
||||
<div class="strip" style="margin: 0 0 10px 0;">
|
||||
<span class="chip">VIP <span class="x">×</span></span>
|
||||
<span class="chip amber">Favorite <span class="x">×</span></span>
|
||||
<span class="chip violet">Owned <span class="x">×</span></span>
|
||||
</div>
|
||||
<div class="row">◆ VIP ✓</div>
|
||||
<div class="row" style="color: var(--amber);">★ Favorite ✓</div>
|
||||
<div class="row" style="color: var(--violet);">▣ Owned ✓</div>
|
||||
<div class="row">⊖ Unmarked</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="placeholder">Cover grid (no strip below)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5 -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">5</div>
|
||||
<div>
|
||||
<div class="variant-title">Sidebar dropdown — opens to the right</div>
|
||||
<div class="variant-desc">Popover anchors to the side of the button. Below stays untouched.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="canvas">
|
||||
<div class="toolbar pos-rel">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span>📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active" style="position: relative;">
|
||||
<span>🔖 Filter <span class="badge">3</span></span><span class="cv">▸</span>
|
||||
<div class="pop-side">
|
||||
<div class="dropdown">
|
||||
<div class="row on">◆ VIP ✓</div>
|
||||
<div class="row" style="color: var(--amber);">★ Favorite ✓</div>
|
||||
<div class="row" style="color: var(--violet);">▣ Owned ✓</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="strip">
|
||||
<span class="label">ACTIVE</span>
|
||||
<span class="chip">VIP <span class="x">×</span></span>
|
||||
<span class="chip amber">Favorite <span class="x">×</span></span>
|
||||
<span class="chip violet">Owned <span class="x">×</span></span>
|
||||
</div>
|
||||
<div class="placeholder">Cover grid</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6 -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">6</div>
|
||||
<div>
|
||||
<div class="variant-title">Auto-flip based on space</div>
|
||||
<div class="variant-desc">If chips are below, open up. If at top of page, open down. Hands-off heuristic.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="canvas" style="padding-top: 80px;">
|
||||
<div class="toolbar pos-rel">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span>📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active"><span>🔖 Filter <span class="badge">3</span></span><span class="cv">▴</span></span>
|
||||
<div class="pop-up">
|
||||
<div class="dropdown">
|
||||
<div class="row on">◆ VIP ✓</div>
|
||||
<div class="row" style="color: var(--amber);">★ Favorite ✓</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="strip">
|
||||
<span class="label">ACTIVE</span>
|
||||
<span class="chip">VIP <span class="x">×</span></span>
|
||||
<span class="chip amber">Favorite <span class="x">×</span></span>
|
||||
</div>
|
||||
<div class="note">Same control, but opens up only when the strip is present below. Down otherwise.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 7 -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">7</div>
|
||||
<div>
|
||||
<div class="variant-title">Floating chip dock — bottom of viewport</div>
|
||||
<div class="variant-desc">Chips never live in the toolbar's vertical column at all.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="canvas" style="padding-bottom: 60px; position: relative;">
|
||||
<div class="toolbar pos-rel">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span>📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active"><span>🔖 Filter <span class="badge">3</span></span><span class="cv">▾</span></span>
|
||||
<div class="pop-down">
|
||||
<div class="dropdown">
|
||||
<div class="row on">◆ VIP ✓</div>
|
||||
<div class="row" style="color: var(--amber);">★ Favorite ✓</div>
|
||||
<div class="row" style="color: var(--violet);">▣ Owned ✓</div>
|
||||
<div class="row">⊖ Unmarked</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="placeholder">Cover grid</div>
|
||||
<div style="position: absolute; left: 50%; bottom: 6px; transform: translateX(-50%);">
|
||||
<div class="strip" style="background: var(--bg-1); border: 1px solid var(--border-strong); box-shadow: 0 8px 24px rgba(0,0,0,0.6);">
|
||||
<span class="label">ACTIVE</span>
|
||||
<span class="chip">VIP <span class="x">×</span></span>
|
||||
<span class="chip amber">Favorite <span class="x">×</span></span>
|
||||
<span class="chip violet">Owned <span class="x">×</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 8 -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">8</div>
|
||||
<div>
|
||||
<div class="variant-title">Drawer style — full-width slide from top</div>
|
||||
<div class="variant-desc">Filter expands as a horizontal drawer. Strip stays visible because the drawer occupies its own row above.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="canvas">
|
||||
<div class="toolbar">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span>📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active"><span>🔖 Filter <span class="badge">3</span></span><span class="cv">▴</span></span>
|
||||
</div>
|
||||
<div style="margin-top: 8px; padding: 12px; background: var(--bg-0); border: 1px solid var(--border-strong); border-radius: 12px; display: flex; gap: 18px; flex-wrap: wrap;">
|
||||
<div style="font-family: ui-monospace, monospace; font-size: 10px; color: var(--cyan); text-transform: uppercase;">Marks · ✓ VIP ✓ Favorite ✓ Owned ⊖ Unmarked</div>
|
||||
<div style="font-family: ui-monospace, monospace; font-size: 10px; color: var(--cyan); text-transform: uppercase;">Watch · 👁 ALL ★ ALL</div>
|
||||
<div style="font-family: ui-monospace, monospace; font-size: 10px; color: var(--cyan); text-transform: uppercase;">Has · 📂 ALL 🏷 ALL</div>
|
||||
</div>
|
||||
<div class="strip">
|
||||
<span class="label">ACTIVE</span>
|
||||
<span class="chip">VIP <span class="x">×</span></span>
|
||||
<span class="chip amber">Favorite <span class="x">×</span></span>
|
||||
<span class="chip violet">Owned <span class="x">×</span></span>
|
||||
</div>
|
||||
<div class="placeholder">Cover grid</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 9 -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">9</div>
|
||||
<div>
|
||||
<div class="variant-title">Hide strip while popover open</div>
|
||||
<div class="variant-desc">Strip fades out when the popover opens (chips visible inside the popover instead). Restores on close.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="canvas">
|
||||
<div class="toolbar pos-rel">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span>📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active"><span>🔖 Filter <span class="badge">3</span></span><span class="cv">▾</span></span>
|
||||
<div class="pop-down">
|
||||
<div class="dropdown">
|
||||
<div class="row on">◆ VIP ✓</div>
|
||||
<div class="row" style="color: var(--amber);">★ Favorite ✓</div>
|
||||
<div class="row" style="color: var(--violet);">▣ Owned ✓</div>
|
||||
<div class="row">⊖ Unmarked</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="strip" style="opacity: 0.18;">
|
||||
<span class="label">ACTIVE</span>
|
||||
<span class="chip">VIP <span class="x">×</span></span>
|
||||
<span class="chip amber">Favorite <span class="x">×</span></span>
|
||||
<span class="chip violet">Owned <span class="x">×</span></span>
|
||||
</div>
|
||||
<div class="note">Strip dimmed (or hidden) while menu open; reappears when closed.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 10 -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">10</div>
|
||||
<div>
|
||||
<div class="variant-title">Toolbar & strip share a row</div>
|
||||
<div class="variant-desc">Strip lives <em>inside</em> the toolbar to the right of the buttons. Dropdown opens below, but there's nothing for it to cover.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="canvas">
|
||||
<div class="toolbar pos-rel" style="flex-wrap: nowrap; overflow-x: auto;">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span>📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active"><span>🔖 Filter <span class="badge">3</span></span><span class="cv">▾</span></span>
|
||||
<span class="pill disabled"><span>🏷 Mark As</span><span class="cv">▾</span></span>
|
||||
<span style="margin: 0 4px; color: var(--fg-muted);">|</span>
|
||||
<span class="chip">VIP <span class="x">×</span></span>
|
||||
<span class="chip amber">Favorite <span class="x">×</span></span>
|
||||
<span class="chip violet">Owned <span class="x">×</span></span>
|
||||
<div class="pop-down">
|
||||
<div class="dropdown">
|
||||
<div class="row on">◆ VIP ✓</div>
|
||||
<div class="row" style="color: var(--amber);">★ Favorite ✓</div>
|
||||
<div class="row" style="color: var(--violet);">▣ Owned ✓</div>
|
||||
<div class="row">⊖ Unmarked</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="placeholder" style="margin-top: 60px;">Cover grid</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="pick">
|
||||
<strong>My pick: #1 (move ACTIVE above the toolbar).</strong> Simplest fix, no popover gymnastics, chips stay readable while editing because the strip is now permanently above the dropdown anchor. <em>Runner-up: #2 (open upward)</em> — keeps the page chrome unchanged but flips the menu direction when chips are present.
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,362 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Actress Detail Page — Spacing Pass (Layout Untouched)</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-bg-0: oklch(0.13 0.025 280);
|
||||
--color-bg-1: oklch(0.17 0.04 285);
|
||||
--color-fg: oklch(0.97 0.01 280);
|
||||
--color-fg-dim: oklch(0.72 0.025 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: color-mix(in oklch, white 6%, transparent);
|
||||
--color-glass-strong: color-mix(in oklch, white 10%, transparent);
|
||||
--color-glass-border: color-mix(in oklch, white 14%, transparent);
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-violet: oklch(0.72 0.22 305);
|
||||
--color-mint: oklch(0.80 0.16 155);
|
||||
--color-coral: oklch(0.72 0.20 25);
|
||||
--color-amber: oklch(0.84 0.14 80);
|
||||
|
||||
--spacing-card: 15px;
|
||||
--spacing-section: 15px;
|
||||
--spacing-card-gap: 9px;
|
||||
--spacing-chip: 7px;
|
||||
--spacing-label: 7px;
|
||||
}
|
||||
body {
|
||||
background: var(--color-bg-0);
|
||||
color: var(--color-fg);
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
.glass { background: var(--color-glass); border: 1px solid var(--color-glass-border); }
|
||||
.label { font-family: ui-monospace, monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 8px; }
|
||||
.label.after { color: var(--color-cyan); }
|
||||
.why { font-size: 12px; color: var(--color-fg-muted); }
|
||||
|
||||
.pg {
|
||||
background: oklch(0.10 0.025 280);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pg-h {
|
||||
padding: 12px 16px;
|
||||
background: oklch(0.20 0.04 285);
|
||||
border-bottom: 1px solid var(--color-glass-border);
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 12px;
|
||||
color: var(--color-fg-dim);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.pg-h .tag { color: var(--color-cyan); font-weight: 600; }
|
||||
.pg-body { padding: 18px; }
|
||||
|
||||
/* Portrait frames — golden ratio (1.618), DO NOT TOUCH */
|
||||
.pframe {
|
||||
--PHI: 1.618;
|
||||
height: 308px;
|
||||
width: 190px; /* 308 / 1.618 */
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(135deg, oklch(0.30 0.05 290), oklch(0.18 0.04 285) 80%);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
position: relative;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.pframe.horiz {
|
||||
width: 498px; /* 308 * 1.618 */
|
||||
}
|
||||
.pframe .slot {
|
||||
position: absolute; top: 6px; left: 6px;
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 9px; text-transform: uppercase; letter-spacing: 0.1em;
|
||||
color: white; padding: 2px 6px; border-radius: 4px;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
.pframe .face { position: absolute; inset: 0; display: grid; place-items: center; color: rgba(255,255,255,0.4); font-size: 48px; }
|
||||
.biopanel {
|
||||
height: 308px;
|
||||
width: 190px;
|
||||
border-radius: 12px;
|
||||
background: oklch(0.17 0.04 285 / 0.4);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
padding: 12px;
|
||||
flex: 0 0 auto;
|
||||
display: flex; flex-direction: column; gap: 12px;
|
||||
}
|
||||
.biopanel h4 { font-family: ui-monospace, monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-cyan); margin-bottom: 6px; }
|
||||
.biopanel .row { display: flex; justify-content: space-between; font-size: 13px; }
|
||||
.biopanel .row span:first-child { color: var(--color-fg-muted); }
|
||||
.biopanel .row span:last-child { font-family: ui-monospace, monospace; color: var(--color-fg); }
|
||||
|
||||
.chip-cat {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px; font-size: 12px; font-family: ui-monospace, monospace;
|
||||
border: 1px solid var(--color-glass-border); color: var(--color-fg-muted);
|
||||
background: transparent;
|
||||
}
|
||||
.chip-cat.active { color: var(--color-violet); border-color: oklch(0.72 0.22 305 / 0.6); background: oklch(0.72 0.22 305 / 0.15); }
|
||||
.chip-cat.active.cyan { color: var(--color-cyan); border-color: oklch(0.82 0.16 200 / 0.6); background: oklch(0.82 0.16 200 / 0.15); }
|
||||
|
||||
.alt-chip {
|
||||
display: inline-flex; align-items: center;
|
||||
padding: 0 8px; height: 22px;
|
||||
border-radius: 999px; font-size: 11px; font-family: ui-monospace, monospace;
|
||||
border: 1px solid var(--color-glass-border); color: var(--color-fg-dim);
|
||||
background: transparent;
|
||||
}
|
||||
.alt-chip.auto { color: var(--color-cyan); border-color: oklch(0.82 0.16 200 / 0.3); background: oklch(0.82 0.16 200 / 0.05); }
|
||||
|
||||
.cstar {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
padding: 4px 12px 4px 4px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--color-glass-border);
|
||||
background: var(--color-glass);
|
||||
font-size: 13px;
|
||||
}
|
||||
.cstar .av { width: 28px; height: 28px; border-radius: 999px; background: oklch(0.30 0.05 290); flex: 0 0 auto; }
|
||||
.cstar .num { font-family: ui-monospace, monospace; font-size: 10px; color: var(--color-cyan); }
|
||||
|
||||
.thumb {
|
||||
aspect-ratio: 800/538;
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(135deg, oklch(0.30 0.05 290), oklch(0.20 0.04 280));
|
||||
border: 1px solid var(--color-glass-border);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="space-y-6 max-w-[1600px] mx-auto p-6">
|
||||
|
||||
<header class="space-y-1">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Actress Detail Page · Spacing Pass</h1>
|
||||
<p class="why max-w-[820px]">
|
||||
<code class="text-[var(--color-cyan)]">/actress/[slug]</code> — back link + Edit, hero card (name + alt-name chips + 4 portraits + landscape + bio + categories + notes), CoStars row, FilterBar, LetterBar, MasonryGrid. Only the hero card's internal padding/gaps adjust. Portraits are golden-ratio (1.618) — <strong>not touched</strong>.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- ====================================================================
|
||||
Audit
|
||||
==================================================================== -->
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<div class="label">Spacing changes only — 4 lines</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-2 text-[12px] font-mono text-[var(--color-fg-dim)]">
|
||||
<div>1 · hero card padding</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">p-5</code> 20 px → <code class="text-[var(--color-cyan)]">p-card</code> 15 px</div>
|
||||
<div>2 · hero card internal section gap</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">gap-5</code> 20 px → <code class="text-[var(--color-cyan)]">gap-section</code> 15 px</div>
|
||||
<div>3 · "Categories" label margin</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">mb-1.5</code> 6 px → <code class="text-[var(--color-cyan)]">mb-label</code> 7 px</div>
|
||||
<div>4 · category chip cluster gap</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">gap-1.5</code> 6 px → <code class="text-[var(--color-cyan)]">gap-chip</code> 7 px</div>
|
||||
</div>
|
||||
<p class="why mt-3">
|
||||
<strong>Untouched:</strong> page outer <code class="text-[var(--color-cyan)]">px-6 py-6</code>, top back/edit row <code class="text-[var(--color-cyan)]">mb-4</code>, hero card <code class="text-[var(--color-cyan)]">mb-6</code> (matches LetterBar's <code class="text-[var(--color-cyan)]">my-6</code>), portrait flex <code class="text-[var(--color-cyan)]">gap-3</code> (image rhythm), portrait golden-ratio aspect, alt-name chips <code class="text-[var(--color-cyan)]">gap-1.5 mt-2</code>, notes <code class="text-[var(--color-cyan)]">mt-4</code>, CoStarsRow <code class="text-[var(--color-cyan)]">my-6 / mb-3 / gap-2</code>, FilterBar, MasonryGrid.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================
|
||||
BEFORE
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag">BEFORE</span> · current /actress/[slug]</span><span>p-5 · gap-5 · mb-1.5 · gap-1.5</span></div>
|
||||
<div class="pg-body">
|
||||
<!-- Top row -->
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;">
|
||||
<span style="font-size: 13px; color: var(--color-fg-dim);">← All Actresses</span>
|
||||
<span style="display:inline-flex; align-items:center; gap: 6px; font-size: 12px; padding: 4px 10px; border-radius: 8px;" class="glass">✎ Edit</span>
|
||||
</div>
|
||||
|
||||
<!-- Hero card -->
|
||||
<div class="glass rounded-2xl" style="padding: 20px; margin-bottom: 24px; display: flex; flex-direction: column; gap: 20px; box-shadow: 0 0 0 2px var(--color-violet), 0 0 24px -4px oklch(0.72 0.22 305 / 0.4);">
|
||||
<!-- title row -->
|
||||
<div style="display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;">
|
||||
<div style="min-width: 0;">
|
||||
<h1 class="text-3xl font-semibold tracking-tight" style="color: var(--color-violet);">Aika</h1>
|
||||
<div style="display:flex; flex-wrap: wrap; gap: 6px; margin-top: 8px;">
|
||||
<span class="alt-chip">Yamakawa Aika</span>
|
||||
<span class="alt-chip auto">Aika Yamakawa</span>
|
||||
<span class="alt-chip">あいか</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: right; flex-shrink: 0;">
|
||||
<div class="text-2xl font-mono font-semibold tabular-nums">42</div>
|
||||
<div style="text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-fg-muted); font-size: 10px;">Covers</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- portraits row (gap-3 — image rhythm, untouched) -->
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 12px; align-items: stretch; justify-content: center;">
|
||||
<div class="pframe"><span class="slot">P1</span><div class="face">👤</div></div>
|
||||
<div class="pframe"><span class="slot">P2</span><div class="face">👤</div></div>
|
||||
<div class="pframe"><span class="slot">P3</span><div class="face">👤</div></div>
|
||||
<div class="pframe"><span class="slot">P4</span><div class="face">👤</div></div>
|
||||
<div class="pframe horiz"><span class="slot">L</span><div class="face">👤</div></div>
|
||||
<div class="biopanel">
|
||||
<div>
|
||||
<h4>Personal</h4>
|
||||
<div class="row"><span>Age</span><span>26</span></div>
|
||||
<div class="row"><span>Born</span><span>1999-03-21</span></div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Body</h4>
|
||||
<div class="row"><span>Height</span><span>165 cm</span></div>
|
||||
<div class="row"><span>Weight</span><span>48 kg</span></div>
|
||||
<div class="row"><span>Cup Size</span><span>D</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- categories + notes -->
|
||||
<div style="min-width: 0; display: flex; flex-direction: column;">
|
||||
<div>
|
||||
<div style="text-transform: uppercase; letter-spacing: 0.1em; font-family: ui-monospace, monospace; font-size: 10px; color: var(--color-fg-muted); margin-bottom: 6px;">Categories</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span class="chip-cat active">★ Favorite</span>
|
||||
<span class="chip-cat active cyan">⬢ VIP</span>
|
||||
<span class="chip-cat">Newcomer</span>
|
||||
<span class="chip-cat">Veteran</span>
|
||||
<span class="chip-cat">Comeback</span>
|
||||
</div>
|
||||
</div>
|
||||
<p style="font-size: 14px; color: var(--color-fg-dim); margin-top: 16px; line-height: 1.6;">
|
||||
Debuted 2019. Known for emotional performances and consistent collaborations with Madonna. Speaks fluent English in two interviews on file.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CoStars row -->
|
||||
<section style="margin: 24px 0;">
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px;">
|
||||
<span style="color: var(--color-fg-muted);">👥</span>
|
||||
<h2 style="font-family: ui-monospace, monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted);">Frequent co-stars</h2>
|
||||
<span style="font-family: ui-monospace, monospace; font-size: 10px; color: var(--color-fg-muted);">(6)</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<span class="cstar"><span class="av"></span><span>Ichika Matsumoto</span><span class="num">8</span></span>
|
||||
<span class="cstar"><span class="av"></span><span>Yua Mikami</span><span class="num">6</span></span>
|
||||
<span class="cstar"><span class="av"></span><span>Suzu Honjo</span><span class="num">4</span></span>
|
||||
<span class="cstar"><span class="av"></span><span>Mei Washio</span><span class="num">3</span></span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Filter / letter / grid (untouched, sketched briefly) -->
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 12px;"><span class="alt-chip">All</span><span class="alt-chip">+ Filter</span></div>
|
||||
<div style="margin: 24px 0;">
|
||||
<div style="display: flex; gap: 1px; flex-wrap: wrap;">
|
||||
<span style="width:24px;height:24px;border-radius:4px;background:var(--color-cyan);color:black;font-family:monospace;font-size:11px;display:grid;place-items:center;">A</span>
|
||||
<span style="width:24px;height:24px;border-radius:4px;color:var(--color-fg-muted);font-family:monospace;font-size:11px;display:grid;place-items:center;">B</span>
|
||||
<span style="width:24px;height:24px;border-radius:4px;color:var(--color-fg-muted);font-family:monospace;font-size:11px;display:grid;place-items:center;">C</span>
|
||||
<span style="width:24px;height:24px;border-radius:4px;color:var(--color-fg-muted);font-family:monospace;font-size:11px;display:grid;place-items:center;">D</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;">
|
||||
<div class="thumb"></div><div class="thumb"></div><div class="thumb"></div><div class="thumb"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
AFTER
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">AFTER</span> · spacing-only</span><span>p-card · gap-section · mb-label · gap-chip</span></div>
|
||||
<div class="pg-body">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;">
|
||||
<span style="font-size: 13px; color: var(--color-fg-dim);">← All Actresses</span>
|
||||
<span style="display:inline-flex; align-items:center; gap: 6px; font-size: 12px; padding: 4px 10px; border-radius: 8px;" class="glass">✎ Edit</span>
|
||||
</div>
|
||||
|
||||
<!-- Hero card with new tokens -->
|
||||
<div class="glass rounded-2xl" style="padding: var(--spacing-card); margin-bottom: 24px; display: flex; flex-direction: column; gap: var(--spacing-section); box-shadow: 0 0 0 2px var(--color-violet), 0 0 24px -4px oklch(0.72 0.22 305 / 0.4);">
|
||||
<div style="display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;">
|
||||
<div style="min-width: 0;">
|
||||
<h1 class="text-3xl font-semibold tracking-tight" style="color: var(--color-violet);">Aika</h1>
|
||||
<div style="display:flex; flex-wrap: wrap; gap: 6px; margin-top: 8px;">
|
||||
<span class="alt-chip">Yamakawa Aika</span>
|
||||
<span class="alt-chip auto">Aika Yamakawa</span>
|
||||
<span class="alt-chip">あいか</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: right; flex-shrink: 0;">
|
||||
<div class="text-2xl font-mono font-semibold tabular-nums">42</div>
|
||||
<div style="text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-fg-muted); font-size: 10px;">Covers</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 12px; align-items: stretch; justify-content: center;">
|
||||
<div class="pframe"><span class="slot">P1</span><div class="face">👤</div></div>
|
||||
<div class="pframe"><span class="slot">P2</span><div class="face">👤</div></div>
|
||||
<div class="pframe"><span class="slot">P3</span><div class="face">👤</div></div>
|
||||
<div class="pframe"><span class="slot">P4</span><div class="face">👤</div></div>
|
||||
<div class="pframe horiz"><span class="slot">L</span><div class="face">👤</div></div>
|
||||
<div class="biopanel">
|
||||
<div>
|
||||
<h4>Personal</h4>
|
||||
<div class="row"><span>Age</span><span>26</span></div>
|
||||
<div class="row"><span>Born</span><span>1999-03-21</span></div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Body</h4>
|
||||
<div class="row"><span>Height</span><span>165 cm</span></div>
|
||||
<div class="row"><span>Weight</span><span>48 kg</span></div>
|
||||
<div class="row"><span>Cup Size</span><span>D</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="min-width: 0; display: flex; flex-direction: column;">
|
||||
<div>
|
||||
<div style="text-transform: uppercase; letter-spacing: 0.1em; font-family: ui-monospace, monospace; font-size: 10px; color: var(--color-fg-muted); margin-bottom: var(--spacing-label);">Categories</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: var(--spacing-chip);">
|
||||
<span class="chip-cat active">★ Favorite</span>
|
||||
<span class="chip-cat active cyan">⬢ VIP</span>
|
||||
<span class="chip-cat">Newcomer</span>
|
||||
<span class="chip-cat">Veteran</span>
|
||||
<span class="chip-cat">Comeback</span>
|
||||
</div>
|
||||
</div>
|
||||
<p style="font-size: 14px; color: var(--color-fg-dim); margin-top: 16px; line-height: 1.6;">
|
||||
Debuted 2019. Known for emotional performances and consistent collaborations with Madonna. Speaks fluent English in two interviews on file.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section style="margin: 24px 0;">
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px;">
|
||||
<span style="color: var(--color-fg-muted);">👥</span>
|
||||
<h2 style="font-family: ui-monospace, monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted);">Frequent co-stars</h2>
|
||||
<span style="font-family: ui-monospace, monospace; font-size: 10px; color: var(--color-fg-muted);">(6)</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<span class="cstar"><span class="av"></span><span>Ichika Matsumoto</span><span class="num">8</span></span>
|
||||
<span class="cstar"><span class="av"></span><span>Yua Mikami</span><span class="num">6</span></span>
|
||||
<span class="cstar"><span class="av"></span><span>Suzu Honjo</span><span class="num">4</span></span>
|
||||
<span class="cstar"><span class="av"></span><span>Mei Washio</span><span class="num">3</span></span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 12px;"><span class="alt-chip">All</span><span class="alt-chip">+ Filter</span></div>
|
||||
<div style="margin: 24px 0;">
|
||||
<div style="display: flex; gap: 1px; flex-wrap: wrap;">
|
||||
<span style="width:24px;height:24px;border-radius:4px;background:var(--color-cyan);color:black;font-family:monospace;font-size:11px;display:grid;place-items:center;">A</span>
|
||||
<span style="width:24px;height:24px;border-radius:4px;color:var(--color-fg-muted);font-family:monospace;font-size:11px;display:grid;place-items:center;">B</span>
|
||||
<span style="width:24px;height:24px;border-radius:4px;color:var(--color-fg-muted);font-family:monospace;font-size:11px;display:grid;place-items:center;">C</span>
|
||||
<span style="width:24px;height:24px;border-radius:4px;color:var(--color-fg-muted);font-family:monospace;font-size:11px;display:grid;place-items:center;">D</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;">
|
||||
<div class="thumb"></div><div class="thumb"></div><div class="thumb"></div><div class="thumb"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="text-[11px] text-[var(--color-fg-muted)]">
|
||||
Tell me <strong>go</strong> to apply, or point at anything you want different.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,321 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Actress Page — Spacing Pass (Layout Untouched)</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-bg-0: oklch(0.13 0.025 280);
|
||||
--color-bg-1: oklch(0.17 0.04 285);
|
||||
--color-fg: oklch(0.97 0.01 280);
|
||||
--color-fg-dim: oklch(0.72 0.025 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: color-mix(in oklch, white 6%, transparent);
|
||||
--color-glass-strong: color-mix(in oklch, white 10%, transparent);
|
||||
--color-glass-border: color-mix(in oklch, white 14%, transparent);
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-violet: oklch(0.72 0.22 305);
|
||||
--color-mint: oklch(0.80 0.16 155);
|
||||
--color-coral: oklch(0.72 0.20 25);
|
||||
--color-amber: oklch(0.84 0.14 80);
|
||||
|
||||
--spacing-card: 15px;
|
||||
--spacing-label: 7px;
|
||||
}
|
||||
body {
|
||||
background: var(--color-bg-0);
|
||||
color: var(--color-fg);
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
.glass { background: var(--color-glass); border: 1px solid var(--color-glass-border); }
|
||||
.label { font-family: ui-monospace, monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 8px; }
|
||||
.label.after { color: var(--color-cyan); }
|
||||
.why { font-size: 12px; color: var(--color-fg-muted); }
|
||||
|
||||
.pg {
|
||||
background: oklch(0.10 0.025 280);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pg-h {
|
||||
padding: 12px 16px;
|
||||
background: oklch(0.20 0.04 285);
|
||||
border-bottom: 1px solid var(--color-glass-border);
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 12px;
|
||||
color: var(--color-fg-dim);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.pg-h .tag { color: var(--color-cyan); font-weight: 600; }
|
||||
.pg-body { padding: 18px; }
|
||||
|
||||
.pill-sm {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 0 12px; height: 28px;
|
||||
border-radius: 999px; font-size: 12px; font-family: ui-monospace, monospace;
|
||||
border: 1px solid var(--color-glass-border); color: var(--color-fg-dim);
|
||||
background: var(--color-glass);
|
||||
}
|
||||
.pill-sm.cyan-fill { background: var(--color-cyan); color: black; border-color: transparent; font-weight: 600; }
|
||||
.pill-sm.amber { background: var(--color-amber); color: black; border-color: transparent; font-weight: 600; }
|
||||
|
||||
/* portrait card placeholder */
|
||||
.pcard {
|
||||
aspect-ratio: 2/3;
|
||||
background: linear-gradient(135deg, oklch(0.30 0.05 290), oklch(0.20 0.04 280));
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
}
|
||||
.pcard .name { position: absolute; bottom: 8px; left: 10px; right: 10px; font-size: 12px; color: var(--color-fg); font-weight: 600; }
|
||||
|
||||
.ltr-bar { display: flex; gap: 4px; width: 100%; }
|
||||
.ltr-bar > button {
|
||||
flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
padding: 8px 0; border-radius: 8px;
|
||||
border: 1px solid var(--color-glass-border);
|
||||
background: var(--color-glass);
|
||||
font-family: ui-monospace, monospace;
|
||||
color: var(--color-fg-muted);
|
||||
}
|
||||
.ltr-bar > button.active { background: var(--color-cyan); color: black; border-color: transparent; }
|
||||
.ltr-bar > button.disabled { opacity: 0.4; }
|
||||
.ltr-bar > button .big { font-size: 14px; font-weight: 600; }
|
||||
.ltr-bar > button .sm { font-size: 9px; opacity: 0.7; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="space-y-6 max-w-[1600px] mx-auto p-6">
|
||||
|
||||
<header class="space-y-1">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Actress Page · Spacing Pass</h1>
|
||||
<p class="why max-w-[820px]">
|
||||
Same layout as <code class="text-[var(--color-cyan)]">app/actress/page.tsx</code> + <code class="text-[var(--color-cyan)]">ActressDirectory.tsx</code>: title row + create bar, search field + bulk button, category pills, letter bar, then the cast grid (or section-by-letter when a letter is active). Only padding/margin changes — no components moved.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- ====================================================================
|
||||
Audit table
|
||||
==================================================================== -->
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<div class="label">Spacing changes only — 4 lines total</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-2 text-[12px] font-mono text-[var(--color-fg-dim)]">
|
||||
<div>1 · page-level empty state</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">p-12 · mb-2</code> → <code class="text-[var(--color-cyan)]">p-card · mb-label</code></div>
|
||||
<div>2 · "no matches" empty state in directory</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">p-12</code> → <code class="text-[var(--color-cyan)]">p-card</code></div>
|
||||
<div>3 · letter-bar wrapper top margin</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">mt-[13px]</code> 13 px → <code class="text-[var(--color-cyan)]">mt-4</code> 16 px</div>
|
||||
<div>4 · letter-bar wrapper bottom margin</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">mb-5</code> 20 px → <code class="text-[var(--color-cyan)]">mb-4</code> 16 px</div>
|
||||
</div>
|
||||
<p class="why mt-3">
|
||||
The current letter-bar margins (13 / 20) are off-rhythm with everything else on the page (which uses 16). Bringing them to 16/16 matches the row above (search + bulk row mb-4) without altering layout.
|
||||
</p>
|
||||
<p class="why mt-2">
|
||||
<strong>Untouched:</strong> page outer <code class="text-[var(--color-cyan)]">px-6 py-6</code>, title-to-create-bar row <code class="text-[var(--color-cyan)]">mb-6</code>, search row <code class="text-[var(--color-cyan)]">mb-4</code>, category-pill gaps <code class="text-[var(--color-cyan)]">gap-1.5</code>, grid gap <code class="text-[var(--color-cyan)]">gap-4</code>, per-letter section spacing <code class="text-[var(--color-cyan)]">space-y-8</code>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================
|
||||
BEFORE
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag">BEFORE</span> · current Actress page</span><span>app/actress/page.tsx + ActressDirectory.tsx</span></div>
|
||||
<div class="pg-body">
|
||||
<!-- Title row (mb-6) -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px;">
|
||||
<div>
|
||||
<h1 class="text-3xl font-semibold tracking-tight">Cast</h1>
|
||||
<p class="text-[var(--color-fg-dim)] mt-1 text-sm">324 total</p>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; align-items: center;">
|
||||
<span class="pill-sm" style="background: var(--color-glass-strong); height: 32px;">+ Create actress</span>
|
||||
<span class="pill-sm" style="height: 32px;">⤓ Import…</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search + bulk row (mb-4) -->
|
||||
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
|
||||
<div class="glass rounded-lg" style="flex: 1; padding: 8px 12px; padding-left: 32px; font-size: 13px; color: var(--color-fg-muted); position: relative;">
|
||||
<span style="position: absolute; left: 12px; top: 50%; transform: translateY(-50%); opacity: 0.6;">⌕</span>
|
||||
Filter Cast — Name, Reversed, Alt Names…
|
||||
</div>
|
||||
<div class="pill-sm" style="width: 180px; height: 36px; justify-content: center;">☐ Select All Visible <span style="opacity: 0.6;">324</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Category pills (no mb on this block; mt comes from next block's mt-[13px]) -->
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span class="pill-sm cyan-fill">ALL <span style="opacity: 0.7;">324</span></span>
|
||||
<span class="pill-sm">★ Favorite <span style="opacity: 0.7;">42</span></span>
|
||||
<span class="pill-sm">⬢ VIP <span style="opacity: 0.7;">18</span></span>
|
||||
<span class="pill-sm">Not Assigned <span style="opacity: 0.7;">203</span></span>
|
||||
<span class="pill-sm">Newcomer <span style="opacity: 0.7;">14</span></span>
|
||||
<span class="pill-sm">Veteran <span style="opacity: 0.7;">22</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Letter bar wrapper (mt-[13px] mb-5 — out of rhythm) -->
|
||||
<div style="margin-top: 13px; margin-bottom: 20px;">
|
||||
<div class="ltr-bar">
|
||||
<button class="active"><span class="big">ALL</span><span class="sm">324</span></button>
|
||||
<button><span class="big">A</span><span class="sm">12</span></button>
|
||||
<button><span class="big">B</span><span class="sm">8</span></button>
|
||||
<button><span class="big">C</span><span class="sm">5</span></button>
|
||||
<button><span class="big">D</span><span class="sm">14</span></button>
|
||||
<button><span class="big">E</span><span class="sm">3</span></button>
|
||||
<button><span class="big">F</span><span class="sm">7</span></button>
|
||||
<button><span class="big">G</span><span class="sm">9</span></button>
|
||||
<button><span class="big">H</span><span class="sm">11</span></button>
|
||||
<button><span class="big">I</span><span class="sm">16</span></button>
|
||||
<button><span class="big">J</span><span class="sm">8</span></button>
|
||||
<button><span class="big">K</span><span class="sm">22</span></button>
|
||||
<button><span class="big">L</span><span class="sm">7</span></button>
|
||||
<button><span class="big">M</span><span class="sm">19</span></button>
|
||||
<button class="disabled"><span class="big">N</span><span class="sm">0</span></button>
|
||||
<button><span class="big">O</span><span class="sm">6</span></button>
|
||||
<button><span class="big">P</span><span class="sm">4</span></button>
|
||||
<button><span class="big">R</span><span class="sm">9</span></button>
|
||||
<button><span class="big">S</span><span class="sm">28</span></button>
|
||||
<button><span class="big">T</span><span class="sm">14</span></button>
|
||||
<button class="disabled"><span class="big">U</span><span class="sm">0</span></button>
|
||||
<button><span class="big">V</span><span class="sm">2</span></button>
|
||||
<button><span class="big">W</span><span class="sm">3</span></button>
|
||||
<button><span class="big">Y</span><span class="sm">88</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grid (untouched) -->
|
||||
<div style="display: grid; grid-template-columns: repeat(6, 1fr); gap: 16px;">
|
||||
<div class="pcard"><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="pcard"><span class="name">Yua Mikami</span></div>
|
||||
<div class="pcard"><span class="name">Suzu Honjo</span></div>
|
||||
<div class="pcard"><span class="name">Mei Washio</span></div>
|
||||
<div class="pcard"><span class="name">Kana Momonogi</span></div>
|
||||
<div class="pcard"><span class="name">Maria Aine</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Empty-state inside directory (BEFORE p-12) -->
|
||||
<div style="margin-top: 20px;">
|
||||
<div class="label" style="margin-bottom: 4px;">Or — when no matches in current filter:</div>
|
||||
<div class="glass rounded-2xl text-center" style="padding: 48px;">
|
||||
<p class="text-[var(--color-fg-dim)] text-sm">No matches.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
AFTER
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">AFTER</span> · spacing-only</span><span>letter bar mt-4 mb-4 · empty p-card</span></div>
|
||||
<div class="pg-body">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px;">
|
||||
<div>
|
||||
<h1 class="text-3xl font-semibold tracking-tight">Cast</h1>
|
||||
<p class="text-[var(--color-fg-dim)] mt-1 text-sm">324 total</p>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; align-items: center;">
|
||||
<span class="pill-sm" style="background: var(--color-glass-strong); height: 32px;">+ Create actress</span>
|
||||
<span class="pill-sm" style="height: 32px;">⤓ Import…</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
|
||||
<div class="glass rounded-lg" style="flex: 1; padding: 8px 12px; padding-left: 32px; font-size: 13px; color: var(--color-fg-muted); position: relative;">
|
||||
<span style="position: absolute; left: 12px; top: 50%; transform: translateY(-50%); opacity: 0.6;">⌕</span>
|
||||
Filter Cast — Name, Reversed, Alt Names…
|
||||
</div>
|
||||
<div class="pill-sm" style="width: 180px; height: 36px; justify-content: center;">☐ Select All Visible <span style="opacity: 0.6;">324</span></div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span class="pill-sm cyan-fill">ALL <span style="opacity: 0.7;">324</span></span>
|
||||
<span class="pill-sm">★ Favorite <span style="opacity: 0.7;">42</span></span>
|
||||
<span class="pill-sm">⬢ VIP <span style="opacity: 0.7;">18</span></span>
|
||||
<span class="pill-sm">Not Assigned <span style="opacity: 0.7;">203</span></span>
|
||||
<span class="pill-sm">Newcomer <span style="opacity: 0.7;">14</span></span>
|
||||
<span class="pill-sm">Veteran <span style="opacity: 0.7;">22</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Letter bar wrapper — mt-4 mb-4 (16/16) -->
|
||||
<div style="margin-top: 16px; margin-bottom: 16px;">
|
||||
<div class="ltr-bar">
|
||||
<button class="active"><span class="big">ALL</span><span class="sm">324</span></button>
|
||||
<button><span class="big">A</span><span class="sm">12</span></button>
|
||||
<button><span class="big">B</span><span class="sm">8</span></button>
|
||||
<button><span class="big">C</span><span class="sm">5</span></button>
|
||||
<button><span class="big">D</span><span class="sm">14</span></button>
|
||||
<button><span class="big">E</span><span class="sm">3</span></button>
|
||||
<button><span class="big">F</span><span class="sm">7</span></button>
|
||||
<button><span class="big">G</span><span class="sm">9</span></button>
|
||||
<button><span class="big">H</span><span class="sm">11</span></button>
|
||||
<button><span class="big">I</span><span class="sm">16</span></button>
|
||||
<button><span class="big">J</span><span class="sm">8</span></button>
|
||||
<button><span class="big">K</span><span class="sm">22</span></button>
|
||||
<button><span class="big">L</span><span class="sm">7</span></button>
|
||||
<button><span class="big">M</span><span class="sm">19</span></button>
|
||||
<button class="disabled"><span class="big">N</span><span class="sm">0</span></button>
|
||||
<button><span class="big">O</span><span class="sm">6</span></button>
|
||||
<button><span class="big">P</span><span class="sm">4</span></button>
|
||||
<button><span class="big">R</span><span class="sm">9</span></button>
|
||||
<button><span class="big">S</span><span class="sm">28</span></button>
|
||||
<button><span class="big">T</span><span class="sm">14</span></button>
|
||||
<button class="disabled"><span class="big">U</span><span class="sm">0</span></button>
|
||||
<button><span class="big">V</span><span class="sm">2</span></button>
|
||||
<button><span class="big">W</span><span class="sm">3</span></button>
|
||||
<button><span class="big">Y</span><span class="sm">88</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(6, 1fr); gap: 16px;">
|
||||
<div class="pcard"><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="pcard"><span class="name">Yua Mikami</span></div>
|
||||
<div class="pcard"><span class="name">Suzu Honjo</span></div>
|
||||
<div class="pcard"><span class="name">Mei Washio</span></div>
|
||||
<div class="pcard"><span class="name">Kana Momonogi</span></div>
|
||||
<div class="pcard"><span class="name">Maria Aine</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Empty-state inside directory (AFTER p-card) -->
|
||||
<div style="margin-top: 16px;">
|
||||
<div class="label after" style="margin-bottom: 4px;">Or — no matches in current filter:</div>
|
||||
<div class="glass rounded-2xl text-center" style="padding: var(--spacing-card);">
|
||||
<p class="text-[var(--color-fg-dim)] text-sm">No matches.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
Empty state at page level (no actresses at all)
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag">BEFORE / AFTER</span> · page-level empty state when no actresses exist</span><span>app/actress/page.tsx</span></div>
|
||||
<div class="pg-body">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start;">
|
||||
<div>
|
||||
<div class="label">BEFORE — <code>p-12 · mb-2</code></div>
|
||||
<div class="glass rounded-2xl text-center" style="padding: 48px;">
|
||||
<div style="font-size: 32px; opacity: 0.4; margin-bottom: 8px;">👥</div>
|
||||
<p class="text-[var(--color-fg-dim)] text-sm">No actresses yet. Create one above or add from any cover.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label after">AFTER — <code>p-card · mb-label</code></div>
|
||||
<div class="glass rounded-2xl text-center" style="padding: var(--spacing-card);">
|
||||
<div style="font-size: 32px; opacity: 0.4; margin-bottom: var(--spacing-label);">👥</div>
|
||||
<p class="text-[var(--color-fg-dim)] text-sm">No actresses yet. Create one above or add from any cover.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="text-[11px] text-[var(--color-fg-muted)]">
|
||||
Tell me <strong>go</strong> to apply, or point at anything you want different. Then I'll move on to <strong>Database</strong>.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,298 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Categories Page — Spacing Pass (Layout Untouched)</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-bg-0: oklch(0.13 0.025 280);
|
||||
--color-bg-1: oklch(0.17 0.04 285);
|
||||
--color-fg: oklch(0.97 0.01 280);
|
||||
--color-fg-dim: oklch(0.72 0.025 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: color-mix(in oklch, white 6%, transparent);
|
||||
--color-glass-strong: color-mix(in oklch, white 10%, transparent);
|
||||
--color-glass-border: color-mix(in oklch, white 14%, transparent);
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-violet: oklch(0.72 0.22 305);
|
||||
--color-mint: oklch(0.80 0.16 155);
|
||||
--color-coral: oklch(0.72 0.20 25);
|
||||
--color-amber: oklch(0.84 0.14 80);
|
||||
|
||||
--spacing-card: 15px;
|
||||
--spacing-label: 7px;
|
||||
}
|
||||
body {
|
||||
background: var(--color-bg-0);
|
||||
color: var(--color-fg);
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
.glass { background: var(--color-glass); border: 1px solid var(--color-glass-border); }
|
||||
.label { font-family: ui-monospace, monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 8px; }
|
||||
.label.after { color: var(--color-cyan); }
|
||||
.why { font-size: 12px; color: var(--color-fg-muted); }
|
||||
|
||||
.pg {
|
||||
background: oklch(0.10 0.025 280);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pg-h {
|
||||
padding: 12px 16px;
|
||||
background: oklch(0.20 0.04 285);
|
||||
border-bottom: 1px solid var(--color-glass-border);
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 12px;
|
||||
color: var(--color-fg-dim);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.pg-h .tag { color: var(--color-cyan); font-weight: 600; }
|
||||
.pg-body { padding: 18px; }
|
||||
|
||||
.pill-sm {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 0 10px; height: 28px;
|
||||
border-radius: 6px; font-size: 12px; font-family: ui-monospace, monospace;
|
||||
border: 1px solid var(--color-glass-border); color: var(--color-fg-dim);
|
||||
background: var(--color-glass);
|
||||
}
|
||||
.pill-sm.active { background: var(--color-cyan); color: black; font-weight: 600; border-color: transparent; }
|
||||
.seg { display: flex; border-radius: 8px; overflow: hidden; border: 1px solid var(--color-glass-border); }
|
||||
.seg > .pill-sm { border-radius: 0; border: 0; height: 30px; }
|
||||
|
||||
/* Category cards — golden-ratio aspect untouched */
|
||||
.cat-portrait {
|
||||
aspect-ratio: 1 / 1.618;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, oklch(0.30 0.05 290), oklch(0.18 0.04 285) 80%);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.cat-landscape {
|
||||
aspect-ratio: 1.618 / 1;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, oklch(0.30 0.05 290), oklch(0.18 0.04 285) 80%);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.cat-card .name-overlay {
|
||||
position: absolute; inset-inline: 0; bottom: 0;
|
||||
padding: 12px;
|
||||
padding-top: 40px;
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.55) 50%, transparent);
|
||||
}
|
||||
.cat-card .name-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
||||
.cat-card .dot { width: 10px; height: 10px; border-radius: 999px; flex: 0 0 auto; }
|
||||
.cat-card .name { font-weight: 600; color: white; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.cat-card .desc { font-size: 11px; color: rgba(255,255,255,0.7); margin-top: 4px; line-height: 1.3; }
|
||||
.cat-card .meta { font-size: 10px; font-family: ui-monospace, monospace; color: rgba(255,255,255,0.6); margin-top: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="space-y-6 max-w-[1600px] mx-auto p-6">
|
||||
|
||||
<header class="space-y-1">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Categories Page · Spacing Pass</h1>
|
||||
<p class="why max-w-[820px]">
|
||||
Same layout as <code class="text-[var(--color-cyan)]">app/category/page.tsx</code>: title row + sort/create/view-toggle, then a grid of <code class="text-[var(--color-cyan)]">CategoryGridCard</code>s. Two views: portrait (golden-ratio 1:1.618 cards, 7-up at desktop) and landscape (1.618:1 cards, 3-up at desktop). The card aspect ratios are golden — <strong>not touched</strong>.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- ====================================================================
|
||||
Audit
|
||||
==================================================================== -->
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<div class="label">Spacing changes only — 2 lines</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-2 text-[12px] font-mono text-[var(--color-fg-dim)]">
|
||||
<div>1 · empty-state Panel padding</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">p-12</code> 48 px → <code class="text-[var(--color-cyan)]">p-card</code> 15 px</div>
|
||||
<div>2 · empty-state icon → text margin</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">mb-2</code> 8 px → <code class="text-[var(--color-cyan)]">mb-label</code> 7 px</div>
|
||||
</div>
|
||||
<p class="why mt-3">
|
||||
<strong>Untouched:</strong> page outer <code class="text-[var(--color-cyan)]">px-6 py-6</code>, title row <code class="text-[var(--color-cyan)]">mb-6</code>, sort/view toggle pills, create form, grid <code class="text-[var(--color-cyan)]">gap-4</code>, card responsive column counts, card golden-ratio aspect (1/1.618 and 1.618/1), card internal <code class="text-[var(--color-cyan)]">p-3 pt-10</code> name-overlay.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================
|
||||
PORTRAIT VIEW — Before vs After (only empty state visibly differs)
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag">PORTRAIT VIEW</span> · 7 cards per row · 1:1.618 aspect (golden, untouched)</span><span>view=portrait</span></div>
|
||||
<div class="pg-body">
|
||||
<!-- Title + controls row -->
|
||||
<div style="display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; margin-bottom: 24px;">
|
||||
<div>
|
||||
<h1 class="text-3xl font-semibold tracking-tight">Categories</h1>
|
||||
<p class="text-[var(--color-fg-dim)] mt-1 max-w-prose text-sm">Umbrellas that group related tags. A category like <span class="font-mono text-[var(--color-fg)]">BDSM</span> can collect <span class="font-mono text-[var(--color-fg)]">bondage</span>, <span class="font-mono text-[var(--color-fg)]">shibari</span>, <span class="font-mono text-[var(--color-fg)]">cuffs</span>, etc. Each tag belongs to at most one category.</p>
|
||||
<p class="text-sm text-[var(--color-fg-muted)] mt-1">7 categories</p>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-shrink: 0;">
|
||||
<div class="seg">
|
||||
<span class="pill-sm active">⇊ A-Z</span>
|
||||
<span class="pill-sm">⏚ Count</span>
|
||||
</div>
|
||||
<div class="glass rounded-lg" style="padding: 8px 12px; font-size: 13px; color: var(--color-fg-muted); width: 220px;">New category</div>
|
||||
<span class="pill-sm" style="background: var(--color-cyan); color: black; height: 32px; border-color: transparent; font-weight: 600;">+ Create</span>
|
||||
<div class="seg">
|
||||
<span class="pill-sm active">▭ P</span>
|
||||
<span class="pill-sm">▭ L</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Portrait grid · 7-up · golden aspect -->
|
||||
<div style="display: grid; grid-template-columns: repeat(7, 1fr); gap: 16px;">
|
||||
<div class="cat-card cat-portrait">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: var(--color-coral);"></span><span class="name">BDSM</span></div>
|
||||
<div class="meta">12 tags · 89 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-portrait">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: var(--color-violet);"></span><span class="name">Cosplay</span></div>
|
||||
<div class="meta">8 tags · 45 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-portrait">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: var(--color-mint);"></span><span class="name">Outdoor</span></div>
|
||||
<div class="meta">5 tags · 33 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-portrait">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: var(--color-cyan);"></span><span class="name">Group</span></div>
|
||||
<div class="meta">9 tags · 71 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-portrait">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: var(--color-amber);"></span><span class="name">Solo</span></div>
|
||||
<div class="meta">3 tags · 18 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-portrait">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: oklch(0.75 0.15 350);"></span><span class="name">School</span></div>
|
||||
<div class="meta">6 tags · 27 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-portrait">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: oklch(0.7 0.12 100);"></span><span class="name">Office</span></div>
|
||||
<div class="meta">4 tags · 16 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
LANDSCAPE VIEW
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag">LANDSCAPE VIEW</span> · 3 cards per row · 1.618:1 aspect (golden, untouched)</span><span>view=landscape</span></div>
|
||||
<div class="pg-body">
|
||||
<div style="display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; margin-bottom: 24px;">
|
||||
<div>
|
||||
<h1 class="text-3xl font-semibold tracking-tight">Categories</h1>
|
||||
<p class="text-[var(--color-fg-dim)] mt-1 max-w-prose text-sm">Umbrellas that group related tags. A category like <span class="font-mono text-[var(--color-fg)]">BDSM</span> can collect <span class="font-mono text-[var(--color-fg)]">bondage</span>, <span class="font-mono text-[var(--color-fg)]">shibari</span>, <span class="font-mono text-[var(--color-fg)]">cuffs</span>, etc.</p>
|
||||
<p class="text-sm text-[var(--color-fg-muted)] mt-1">7 categories</p>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-shrink: 0;">
|
||||
<div class="seg">
|
||||
<span class="pill-sm active">⇊ A-Z</span>
|
||||
<span class="pill-sm">⏚ Count</span>
|
||||
</div>
|
||||
<div class="glass rounded-lg" style="padding: 8px 12px; font-size: 13px; color: var(--color-fg-muted); width: 220px;">New category</div>
|
||||
<span class="pill-sm" style="background: var(--color-cyan); color: black; height: 32px; border-color: transparent; font-weight: 600;">+ Create</span>
|
||||
<div class="seg">
|
||||
<span class="pill-sm">▭ P</span>
|
||||
<span class="pill-sm active">▭ L</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;">
|
||||
<div class="cat-card cat-landscape">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: var(--color-coral);"></span><span class="name">BDSM</span></div>
|
||||
<div class="desc">Bondage, shibari, cuffs and related restraint kinks. Sub-tags inherit the umbrella's color.</div>
|
||||
<div class="meta">12 tags · 89 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-landscape">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: var(--color-violet);"></span><span class="name">Cosplay</span></div>
|
||||
<div class="desc">Anime, school uniforms, costume play. Includes themed wardrobes.</div>
|
||||
<div class="meta">8 tags · 45 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-landscape">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: var(--color-mint);"></span><span class="name">Outdoor</span></div>
|
||||
<div class="desc">Beach, park, public, nature settings.</div>
|
||||
<div class="meta">5 tags · 33 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-landscape">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: var(--color-cyan);"></span><span class="name">Group</span></div>
|
||||
<div class="desc">3+ participant scenes, orgy, threesome categories.</div>
|
||||
<div class="meta">9 tags · 71 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-landscape">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: var(--color-amber);"></span><span class="name">Solo</span></div>
|
||||
<div class="desc">Single-performer content; masturbation focus.</div>
|
||||
<div class="meta">3 tags · 18 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cat-card cat-landscape">
|
||||
<div class="name-overlay">
|
||||
<div class="name-row"><span class="dot" style="background: oklch(0.75 0.15 350);"></span><span class="name">School</span></div>
|
||||
<div class="desc">Classroom, uniforms, schoolgirl and teacher dynamics.</div>
|
||||
<div class="meta">6 tags · 27 covers</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
Empty state — only place that visibly changes
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag">EMPTY STATE</span> · BEFORE vs AFTER · only place spacing changes</span><span>cats.length === 0</span></div>
|
||||
<div class="pg-body">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start;">
|
||||
<div>
|
||||
<div class="label">BEFORE — <code>p-12 · mb-2</code></div>
|
||||
<div class="glass rounded-2xl text-center" style="padding: 48px;">
|
||||
<div style="font-size: 32px; opacity: 0.4; margin-bottom: 8px;">▦</div>
|
||||
<p class="text-[var(--color-fg-dim)] text-sm">No categories yet. Create one above to start grouping tags.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label after">AFTER — <code>p-card · mb-label</code></div>
|
||||
<div class="glass rounded-2xl text-center" style="padding: var(--spacing-card);">
|
||||
<div style="font-size: 32px; opacity: 0.4; margin-bottom: var(--spacing-label);">▦</div>
|
||||
<p class="text-[var(--color-fg-dim)] text-sm">No categories yet. Create one above to start grouping tags.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="text-[11px] text-[var(--color-fg-muted)]">
|
||||
Tell me <strong>go</strong> to apply, or point at anything you want different. Then I'll move on to <strong>Tags</strong>.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,882 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex · Context Menu — 10 Improvement Suggestions</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0a0710;
|
||||
--bg-1: #14101e;
|
||||
--fg: #e8e6f0;
|
||||
--fg-dim: #a8a4b8;
|
||||
--fg-muted: #6e6a80;
|
||||
--cyan: #22d3ee;
|
||||
--violet: #a78bfa;
|
||||
--mint: #34d399;
|
||||
--coral: #fb7185;
|
||||
--amber: #fbbf24;
|
||||
--glass: rgba(255,255,255,0.04);
|
||||
--glass-strong: rgba(255,255,255,0.08);
|
||||
--border: rgba(255,255,255,0.08);
|
||||
--border-strong: rgba(255,255,255,0.18);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg-0);
|
||||
color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.page { max-width: 1600px; margin: 0 auto; padding: 24px; }
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
padding: 16px 20px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(34,211,238,0.08), rgba(167,139,250,0.05));
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.page-header h1 { margin: 0 0 8px 0; font-size: 24px; font-weight: 600; }
|
||||
.page-header p { margin: 0; color: var(--fg-dim); font-size: 14px; }
|
||||
|
||||
/* Side-by-side comparison: current on left, all 10 mockups on right grid */
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: 320px 1fr;
|
||||
gap: 32px;
|
||||
align-items: start;
|
||||
}
|
||||
.current-pane {
|
||||
position: sticky;
|
||||
top: 24px;
|
||||
}
|
||||
.pane-label {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
font-family: ui-monospace, monospace;
|
||||
color: var(--fg-muted);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.grid-mocks {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
/* The actual menu */
|
||||
.menu {
|
||||
width: 320px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border-strong);
|
||||
background: rgba(10,7,16,0.96);
|
||||
backdrop-filter: blur(20px);
|
||||
overflow: hidden;
|
||||
font-size: 13px;
|
||||
box-shadow: 0 16px 32px rgba(0,0,0,0.5);
|
||||
}
|
||||
.menu-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.menu-header .title {
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--fg-muted);
|
||||
}
|
||||
.menu-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
color: var(--fg);
|
||||
transition: background 0.1s;
|
||||
}
|
||||
.menu-link:hover { background: var(--glass); }
|
||||
.menu-section {
|
||||
padding: 8px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 4px;
|
||||
margin-bottom: 4px;
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--fg-muted);
|
||||
}
|
||||
.section-head .count {
|
||||
background: var(--glass-strong);
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 9px;
|
||||
}
|
||||
.menu-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: var(--fg-dim);
|
||||
}
|
||||
.menu-row:hover { background: var(--glass); color: var(--fg); }
|
||||
.menu-row.on { color: var(--violet); }
|
||||
.menu-row.on .check { color: var(--violet); }
|
||||
.menu-row .check { width: 12px; }
|
||||
.menu-row .name { flex: 1; }
|
||||
.menu-row .meta { font-size: 10px; color: var(--fg-muted); font-family: ui-monospace, monospace; }
|
||||
.menu-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
background: rgba(10,7,16,0.6);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 6px 8px;
|
||||
font-size: 12px;
|
||||
color: var(--fg);
|
||||
margin-top: 6px;
|
||||
}
|
||||
.menu-input::placeholder { color: var(--fg-muted); }
|
||||
.menu-delete {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
color: var(--coral);
|
||||
border-top: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
}
|
||||
.menu-delete:hover { background: rgba(251,113,133,0.08); }
|
||||
|
||||
/* Mockup wrapper */
|
||||
.mock {
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.mock h2 {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: var(--cyan);
|
||||
font-weight: 600;
|
||||
}
|
||||
.mock .meta {
|
||||
font-size: 11px;
|
||||
color: var(--fg-dim);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.mock .body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 8px 0;
|
||||
background: rgba(255,255,255,0.02);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* Custom mock pieces */
|
||||
.thumb-preview {
|
||||
aspect-ratio: 800 / 538;
|
||||
width: 100%;
|
||||
background: linear-gradient(45deg, #2a1f3a 0%, #4a2a5a 50%, #2a1f3a 100%);
|
||||
border-radius: 8px;
|
||||
margin: 8px 8px 0 8px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-start;
|
||||
padding: 6px;
|
||||
color: rgba(255,255,255,0.5);
|
||||
font-size: 10px;
|
||||
font-family: ui-monospace, monospace;
|
||||
}
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
font-family: ui-monospace, monospace;
|
||||
}
|
||||
.pill.cyan { background: rgba(34,211,238,0.15); color: var(--cyan); }
|
||||
.pill.amber { background: rgba(251,191,36,0.15); color: var(--amber); }
|
||||
.pill.violet { background: rgba(167,139,250,0.15); color: var(--violet); }
|
||||
.quick-row {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.qbtn {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 6px 4px;
|
||||
border-radius: 6px;
|
||||
background: var(--glass);
|
||||
color: var(--fg-dim);
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.qbtn:hover { background: var(--glass-strong); color: var(--fg); }
|
||||
.qbtn.active { background: rgba(34,211,238,0.18); color: var(--cyan); }
|
||||
.qbtn.amber.active { background: rgba(251,191,36,0.18); color: var(--amber); }
|
||||
.qbtn.mint.active { background: rgba(52,211,153,0.18); color: var(--mint); }
|
||||
.qbtn.violet.active { background: rgba(167,139,250,0.18); color: var(--violet); }
|
||||
|
||||
.star-row {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
justify-content: center;
|
||||
}
|
||||
.star {
|
||||
width: 22px; height: 22px;
|
||||
display: grid; place-items: center;
|
||||
color: var(--fg-muted);
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
}
|
||||
.star.filled { color: var(--cyan); }
|
||||
|
||||
.category-header {
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 9px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--cyan);
|
||||
padding: 6px 4px 2px 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.category-header .dot {
|
||||
width: 6px; height: 6px;
|
||||
border-radius: 999px;
|
||||
background: var(--cyan);
|
||||
}
|
||||
|
||||
.recent-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
padding: 8px 12px 4px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.recent-chip {
|
||||
padding: 3px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
background: rgba(167,139,250,0.12);
|
||||
color: var(--violet);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.multi-banner {
|
||||
background: linear-gradient(90deg, rgba(34,211,238,0.18), rgba(167,139,250,0.18));
|
||||
color: var(--cyan);
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(34,211,238,0.3);
|
||||
}
|
||||
|
||||
.compact .menu-row { padding: 3px 6px; font-size: 12px; }
|
||||
.compact .menu-section { padding: 4px; }
|
||||
.compact .menu-link { padding: 6px 12px; }
|
||||
|
||||
.compact-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.actress-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.actress-row .av {
|
||||
width: 22px; height: 22px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(135deg, #a78bfa, #4a2a5a);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
width: 100%;
|
||||
background: rgba(10,7,16,0.6);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 4px 8px;
|
||||
font-size: 11px;
|
||||
color: var(--fg);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* Cover-collection action */
|
||||
.menu-row .pin {
|
||||
font-size: 10px;
|
||||
color: var(--amber);
|
||||
cursor: pointer;
|
||||
}
|
||||
.menu-row.cover-of { background: rgba(251,191,36,0.06); }
|
||||
.menu-row.cover-of .pin { color: var(--amber); }
|
||||
|
||||
/* Scrollable list — caps height to ~5 rows, scrolls beyond. */
|
||||
.menu-list-scroll {
|
||||
max-height: calc(5 * 30px); /* ~5 menu-row heights at 6px+content+6px */
|
||||
overflow-y: auto;
|
||||
/* Slim scrollbar that doesn't fight the dark theme */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--glass-strong) transparent;
|
||||
}
|
||||
.menu-list-scroll::-webkit-scrollbar { width: 6px; }
|
||||
.menu-list-scroll::-webkit-scrollbar-track { background: transparent; }
|
||||
.menu-list-scroll::-webkit-scrollbar-thumb { background: var(--glass-strong); border-radius: 3px; }
|
||||
.menu-list-scroll::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }
|
||||
|
||||
/* Submenu chevron row */
|
||||
.submenu-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
color: var(--fg);
|
||||
cursor: pointer;
|
||||
transition: background 0.1s;
|
||||
}
|
||||
.submenu-row:hover, .submenu-row.open {
|
||||
background: var(--glass);
|
||||
}
|
||||
.submenu-row.open {
|
||||
background: var(--glass-strong);
|
||||
}
|
||||
.submenu-row .name {
|
||||
flex: 1;
|
||||
}
|
||||
.submenu-row .count-pill {
|
||||
font-size: 10px;
|
||||
font-family: ui-monospace, monospace;
|
||||
color: var(--fg-muted);
|
||||
background: var(--glass);
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.submenu-row.open .count-pill {
|
||||
background: rgba(34,211,238,0.15);
|
||||
color: var(--cyan);
|
||||
}
|
||||
.submenu-row .chevron {
|
||||
color: var(--fg-muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Flyout container — sits to the right of the main menu */
|
||||
.flyout-wrap {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
.flyout {
|
||||
width: 320px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border-strong);
|
||||
background: rgba(10,7,16,0.96);
|
||||
backdrop-filter: blur(20px);
|
||||
overflow: hidden;
|
||||
box-shadow: 0 16px 32px rgba(0,0,0,0.5);
|
||||
position: relative;
|
||||
}
|
||||
.flyout::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -7px;
|
||||
top: 36px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 6px solid transparent;
|
||||
border-bottom: 6px solid transparent;
|
||||
border-right: 6px solid var(--border-strong);
|
||||
}
|
||||
|
||||
/* Filter-or-create: input + ghost "Create" affordance shown only when typed
|
||||
value doesn't match an existing item. */
|
||||
.filter-create-wrap {
|
||||
position: relative;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.filter-create-wrap .create-hint {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 10px;
|
||||
color: var(--cyan);
|
||||
background: rgba(34,211,238,0.12);
|
||||
border: 1px solid rgba(34,211,238,0.3);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-family: ui-monospace, monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<header class="page-header">
|
||||
<h1>Right-click context menu — 10 ideas</h1>
|
||||
<p>Current menu on the left (sticky). Each suggestion below shows the same content reorganised. Click anywhere on the page; the mockups are static.</p>
|
||||
</header>
|
||||
|
||||
<div class="layout">
|
||||
<!-- ===================== CURRENT ===================== -->
|
||||
<div class="current-pane">
|
||||
<div class="pane-label">CURRENT</div>
|
||||
<div class="menu">
|
||||
<div class="menu-header">
|
||||
<span class="title">IMAGE #651</span>
|
||||
<span class="title" style="cursor:pointer">×</span>
|
||||
</div>
|
||||
<div class="menu-link">↗ Open detail</div>
|
||||
<div class="menu-section">
|
||||
<div class="section-head"><span>🏷 TAGS</span><span class="count">3</span></div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">anal</span></div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">blowjob</span></div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">gyaru</span></div>
|
||||
<input class="menu-input" placeholder="New tag…" />
|
||||
</div>
|
||||
<div class="menu-section">
|
||||
<div class="section-head"><span>📁 COLLECTIONS</span><span class="count">2</span></div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">collection 1</span></div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">collection 2</span></div>
|
||||
<input class="menu-input" placeholder="New collection…" />
|
||||
</div>
|
||||
<div class="menu-delete">🗑 Delete image</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===================== MOCKUPS ===================== -->
|
||||
<div class="grid-mocks">
|
||||
|
||||
<!-- ⭐ COMBINED PICK: submenu version -->
|
||||
<div class="mock" style="grid-column: 1 / -1; border-color: rgba(34,211,238,0.4); background: linear-gradient(135deg, rgba(34,211,238,0.06), rgba(167,139,250,0.04));">
|
||||
<h2 style="font-size:14px;">⭐ Combined pick — Compact main menu + Tags / Collections as fly-out sub-menus</h2>
|
||||
<div class="meta">
|
||||
Main menu shows only the essentials (header, marks, two collapsed entries, delete).
|
||||
Hovering or clicking <strong style="color:var(--fg);">Tags ▸</strong> or <strong style="color:var(--fg);">Collections ▸</strong> opens a flyout sub-menu to the right with the filter-or-create input,
|
||||
recent chips, and scrollable list. Significantly reduces noise; sub-menu only renders when needed.
|
||||
</div>
|
||||
<div class="body" style="gap: 32px; flex-wrap: wrap; align-items: flex-start;">
|
||||
|
||||
<!-- Single-cover view: closed -->
|
||||
<div>
|
||||
<div style="font-size:10px; color:var(--fg-muted); font-family:ui-monospace,monospace; text-align:center; margin-bottom:6px; letter-spacing:0.08em;">SINGLE · COMPACT</div>
|
||||
<div class="menu" style="width: 320px;">
|
||||
<div class="menu-header" style="flex-direction:column; align-items:flex-start; gap:2px; padding:10px 12px;">
|
||||
<span style="color:var(--cyan); font-family:ui-monospace,monospace; font-weight:600; font-size:13px;">MIAA-291</span>
|
||||
<div style="font-size:11px; color:var(--fg-dim);">Kagami Shuna</div>
|
||||
</div>
|
||||
<div class="quick-row" style="gap:3px;">
|
||||
<div class="qbtn active">💎 VIP</div>
|
||||
<div class="qbtn amber">⭐ Fav</div>
|
||||
<div class="qbtn mint active">👁 Watched</div>
|
||||
<div class="qbtn violet active">📦 Owned</div>
|
||||
</div>
|
||||
<div class="submenu-row">
|
||||
<span>🏷</span><span class="name">Tags</span><span class="count-pill">3</span><span class="chevron">▸</span>
|
||||
</div>
|
||||
<div class="submenu-row">
|
||||
<span>📁</span><span class="name">Collections</span><span class="count-pill">2</span><span class="chevron">▸</span>
|
||||
</div>
|
||||
<div class="menu-delete">🗑 Delete image</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Single-cover view: Tags submenu open -->
|
||||
<div>
|
||||
<div style="font-size:10px; color:var(--fg-muted); font-family:ui-monospace,monospace; text-align:center; margin-bottom:6px; letter-spacing:0.08em;">SINGLE · TAGS FLYOUT</div>
|
||||
<div class="flyout-wrap">
|
||||
<div class="menu" style="width: 320px;">
|
||||
<div class="menu-header" style="flex-direction:column; align-items:flex-start; gap:2px; padding:10px 12px;">
|
||||
<span style="color:var(--cyan); font-family:ui-monospace,monospace; font-weight:600; font-size:13px;">MIAA-291</span>
|
||||
<div style="font-size:11px; color:var(--fg-dim);">Kagami Shuna</div>
|
||||
</div>
|
||||
<div class="quick-row" style="gap:3px;">
|
||||
<div class="qbtn active">💎 VIP</div>
|
||||
<div class="qbtn amber">⭐ Fav</div>
|
||||
<div class="qbtn mint active">👁 Watched</div>
|
||||
<div class="qbtn violet active">📦 Owned</div>
|
||||
</div>
|
||||
<div class="submenu-row open">
|
||||
<span>🏷</span><span class="name">Tags</span><span class="count-pill">3</span><span class="chevron" style="color:var(--cyan);">▸</span>
|
||||
</div>
|
||||
<div class="submenu-row">
|
||||
<span>📁</span><span class="name">Collections</span><span class="count-pill">2</span><span class="chevron">▸</span>
|
||||
</div>
|
||||
<div class="menu-delete">🗑 Delete image</div>
|
||||
</div>
|
||||
|
||||
<div class="flyout">
|
||||
<div class="section-head" style="padding:10px 12px 6px 12px; border-bottom:1px solid var(--border); margin-bottom:0;">
|
||||
<span>🏷 TAGS</span><span class="count">3 / 38</span>
|
||||
</div>
|
||||
<div style="padding:8px;">
|
||||
<div class="filter-create-wrap">
|
||||
<input class="filter-input" placeholder="🔎 Filter or add new tag…" style="margin-bottom:0; padding-right:60px;" />
|
||||
<span class="create-hint">+ Create</span>
|
||||
</div>
|
||||
<div style="font-size:9px; color:var(--fg-muted); padding:6px 4px 2px 4px; letter-spacing:0.06em;">RECENT</div>
|
||||
<div class="recent-row" style="border-bottom:none; padding:0 4px 8px 4px; gap:4px;">
|
||||
<span class="recent-chip">+ shibari</span>
|
||||
<span class="recent-chip">+ pov</span>
|
||||
<span class="recent-chip">+ facial</span>
|
||||
<span class="recent-chip">+ gyaru</span>
|
||||
<span class="recent-chip">+ ntr</span>
|
||||
</div>
|
||||
<div style="border-top:1px solid var(--border); padding-top:4px;">
|
||||
<div style="font-size:9px; color:var(--fg-muted); padding:2px 4px 4px 4px; letter-spacing:0.06em; display:flex; justify-content:space-between;">
|
||||
<span>ALL</span>
|
||||
<span>↕ scroll</span>
|
||||
</div>
|
||||
<div class="menu-list-scroll">
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">anal</span><span class="meta">42</span></div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">blowjob</span><span class="meta">68</span></div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">gyaru</span><span class="meta">21</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">bondage</span><span class="meta">19</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">creampie</span><span class="meta">95</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">facial</span><span class="meta">31</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">handjob</span><span class="meta">14</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">latex</span><span class="meta">8</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">pov</span><span class="meta">40</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bulk-select view: closed -->
|
||||
<div>
|
||||
<div style="font-size:10px; color:var(--fg-muted); font-family:ui-monospace,monospace; text-align:center; margin-bottom:6px; letter-spacing:0.08em;">BULK · 12 · COMPACT</div>
|
||||
<div class="menu" style="width: 320px;">
|
||||
<div class="multi-banner">⚡ Applying to 12 covers</div>
|
||||
<div class="quick-row" style="gap:3px;">
|
||||
<div class="qbtn">💎 VIP</div>
|
||||
<div class="qbtn amber">⭐ Fav</div>
|
||||
<div class="qbtn mint">👁 Watched</div>
|
||||
<div class="qbtn violet">📦 Owned</div>
|
||||
</div>
|
||||
<div class="submenu-row">
|
||||
<span>🏷</span><span class="name">Tags</span><span class="count-pill">mixed</span><span class="chevron">▸</span>
|
||||
</div>
|
||||
<div class="submenu-row">
|
||||
<span>📁</span><span class="name">Collections</span><span class="count-pill">mixed</span><span class="chevron">▸</span>
|
||||
</div>
|
||||
<div class="menu-delete">🗑 Delete 12 images</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bulk-select view: Collections submenu open -->
|
||||
<div>
|
||||
<div style="font-size:10px; color:var(--fg-muted); font-family:ui-monospace,monospace; text-align:center; margin-bottom:6px; letter-spacing:0.08em;">BULK · COLLECTIONS FLYOUT</div>
|
||||
<div class="flyout-wrap">
|
||||
<div class="menu" style="width: 320px;">
|
||||
<div class="multi-banner">⚡ Applying to 12 covers</div>
|
||||
<div class="quick-row" style="gap:3px;">
|
||||
<div class="qbtn">💎 VIP</div>
|
||||
<div class="qbtn amber">⭐ Fav</div>
|
||||
<div class="qbtn mint">👁 Watched</div>
|
||||
<div class="qbtn violet">📦 Owned</div>
|
||||
</div>
|
||||
<div class="submenu-row">
|
||||
<span>🏷</span><span class="name">Tags</span><span class="count-pill">mixed</span><span class="chevron">▸</span>
|
||||
</div>
|
||||
<div class="submenu-row open">
|
||||
<span>📁</span><span class="name">Collections</span><span class="count-pill">mixed</span><span class="chevron" style="color:var(--cyan);">▸</span>
|
||||
</div>
|
||||
<div class="menu-delete">🗑 Delete 12 images</div>
|
||||
</div>
|
||||
|
||||
<div class="flyout" style="margin-top: 60px;">
|
||||
<div class="section-head" style="padding:10px 12px 6px 12px; border-bottom:1px solid var(--border); margin-bottom:0;">
|
||||
<span>📁 COLLECTIONS</span><span class="count">mixed</span>
|
||||
</div>
|
||||
<div style="padding:8px;">
|
||||
<div class="filter-create-wrap">
|
||||
<input class="filter-input" placeholder="🔎 Filter or add new collection…" style="margin-bottom:0; padding-right:60px;" />
|
||||
<span class="create-hint">+ Create</span>
|
||||
</div>
|
||||
<div style="font-size:9px; color:var(--fg-muted); padding:6px 4px 2px 4px; letter-spacing:0.06em;">RECENT</div>
|
||||
<div class="recent-row" style="border-bottom:none; padding:0 4px 8px 4px; gap:4px;">
|
||||
<span class="recent-chip" style="background:rgba(34,211,238,0.12); color:var(--cyan);">+ slick & shiny</span>
|
||||
<span class="recent-chip" style="background:rgba(34,211,238,0.12); color:var(--cyan);">+ marathon 2025</span>
|
||||
<span class="recent-chip" style="background:rgba(34,211,238,0.12); color:var(--cyan);">+ to rewatch</span>
|
||||
<span class="recent-chip" style="background:rgba(34,211,238,0.12); color:var(--cyan);">+ collection 1</span>
|
||||
</div>
|
||||
<div style="border-top:1px solid var(--border); padding-top:4px;">
|
||||
<div style="font-size:9px; color:var(--fg-muted); padding:2px 4px 4px 4px; letter-spacing:0.06em; display:flex; justify-content:space-between;">
|
||||
<span>ALL</span>
|
||||
<span>↕ scroll</span>
|
||||
</div>
|
||||
<div class="menu-list-scroll">
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">collection 1</span><span class="meta">12 / 12</span></div>
|
||||
<div class="menu-row on"><span class="check">−</span><span class="name">collection 2</span><span class="meta">5 / 12</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">slick & shiny</span><span class="meta">0 / 12</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">marathon 2025</span><span class="meta">0 / 12</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="meta" style="border-top:1px solid var(--border); padding-top:10px; margin-top:6px;">
|
||||
<strong style="color:var(--fg);">Behaviour notes:</strong>
|
||||
<ul style="margin:6px 0 0 18px; padding:0; color:var(--fg-dim); line-height:1.6;">
|
||||
<li><strong style="color:var(--fg);">Compact main menu</strong>: 4 quick marks, Tags ▸ row, Collections ▸ row, Delete. That's it.</li>
|
||||
<li><strong style="color:var(--fg);">Submenu trigger</strong>: hover or click <code>Tags ▸</code> / <code>Collections ▸</code> opens the flyout to the right of the main menu (or to the left if there isn't room). Closing happens on selecting an item, clicking elsewhere, or pressing ESC.</li>
|
||||
<li><strong style="color:var(--fg);">Count pill</strong> on the row tells you how many tags/collections are already attached without opening the submenu. In bulk mode it reads <code>mixed</code> when coverage isn't uniform.</li>
|
||||
<li><strong style="color:var(--fg);">Filter-or-create input</strong>: typing filters the list as you type. If your input matches no existing item, the cyan <code style="background:rgba(34,211,238,0.1); padding:0 4px; border-radius:3px; color:var(--cyan); font-family:ui-monospace,monospace; font-size:10px;">+ Create</code> chip appears at the right edge — Enter or click commits.</li>
|
||||
<li><strong style="color:var(--fg);">Scroll cap</strong>: each list shows ~3 rows then becomes a thin scrollbar. Recent tags row is <em>not</em> capped; it stays as a single chip strip.</li>
|
||||
<li><strong style="color:var(--fg);">VIP / Favorite</strong> stay mutually exclusive (today's behaviour).</li>
|
||||
<li><strong style="color:var(--violet);">Owned</strong> is a new boolean — needs <code style="background:rgba(255,255,255,0.05); padding:1px 4px; border-radius:3px; font-family:ui-monospace,monospace; font-size:10px;">is_owned</code> column on <code style="background:rgba(255,255,255,0.05); padding:1px 4px; border-radius:3px; font-family:ui-monospace,monospace; font-size:10px;">images</code>.</li>
|
||||
<li><strong style="color:var(--fg);">Bulk mode</strong>: tag/collection rows show <code style="background:rgba(255,255,255,0.05); padding:1px 4px; border-radius:3px; font-family:ui-monospace,monospace; font-size:10px;">N / 12</code>. <code>−</code> indicates partial coverage; click to apply to all, click again to remove from all (tri-state cycle).</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 1. Thumbnail preview -->
|
||||
<div class="mock">
|
||||
<h2>1. Thumbnail preview at top</h2>
|
||||
<div class="meta">Shows which cover you're acting on. Critical when right-clicking from a dense grid where the mouse position alone doesn't tell you.</div>
|
||||
<div class="body">
|
||||
<div class="menu">
|
||||
<div class="menu-header">
|
||||
<span class="title">MIAA-291</span>
|
||||
<span class="title">×</span>
|
||||
</div>
|
||||
<div class="thumb-preview">800×538</div>
|
||||
<div class="menu-link">↗ Open detail</div>
|
||||
<div class="menu-section"><div class="section-head"><span>🏷 TAGS</span><span class="count">3</span></div></div>
|
||||
<div class="menu-delete">🗑 Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. Code + title header -->
|
||||
<div class="mock">
|
||||
<h2>2. Code + title header</h2>
|
||||
<div class="meta">Replace generic "IMAGE #651" with something identifying. Falls back to the ID only when there's no code.</div>
|
||||
<div class="body">
|
||||
<div class="menu">
|
||||
<div class="menu-header" style="flex-direction:column; align-items:flex-start; gap:2px; padding:10px 12px;">
|
||||
<div style="display:flex; align-items:center; gap:8px; width:100%; justify-content:space-between;">
|
||||
<span style="color:var(--cyan); font-family:ui-monospace,monospace; font-weight:600; font-size:13px;">MIAA-291</span>
|
||||
<span class="title">×</span>
|
||||
</div>
|
||||
<div style="font-size:11px; color:var(--fg-dim);">Kagami Shuna · 2024-08</div>
|
||||
</div>
|
||||
<div class="menu-link">↗ Open detail</div>
|
||||
<div class="menu-section"><div class="section-head"><span>🏷 TAGS</span><span class="count">3</span></div></div>
|
||||
<div class="menu-delete">🗑 Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Quick marks row -->
|
||||
<div class="mock">
|
||||
<h2>3. Quick marks row (VIP / Fav / Watched)</h2>
|
||||
<div class="meta">Most-used actions promoted to a single click. Currently buried in cover detail page.</div>
|
||||
<div class="body">
|
||||
<div class="menu">
|
||||
<div class="menu-header"><span class="title">MIAA-291</span><span class="title">×</span></div>
|
||||
<div class="quick-row">
|
||||
<div class="qbtn active">💎 VIP</div>
|
||||
<div class="qbtn amber">⭐ Fav</div>
|
||||
<div class="qbtn mint active">👁 Watched</div>
|
||||
</div>
|
||||
<div class="menu-link">↗ Open detail</div>
|
||||
<div class="menu-section"><div class="section-head"><span>🏷 TAGS</span><span class="count">3</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. Star rating row -->
|
||||
<div class="mock">
|
||||
<h2>4. Star rating row</h2>
|
||||
<div class="meta">Set 0-5 rating without opening detail. Click again on highest filled star to clear.</div>
|
||||
<div class="body">
|
||||
<div class="menu">
|
||||
<div class="menu-header"><span class="title">MIAA-291</span><span class="title">×</span></div>
|
||||
<div class="star-row">
|
||||
<span class="star filled">★</span>
|
||||
<span class="star filled">★</span>
|
||||
<span class="star filled">★</span>
|
||||
<span class="star filled">★</span>
|
||||
<span class="star">★</span>
|
||||
</div>
|
||||
<div class="menu-link">↗ Open detail</div>
|
||||
<div class="menu-section"><div class="section-head"><span>🏷 TAGS</span><span class="count">3</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5. Actress section -->
|
||||
<div class="mock">
|
||||
<h2>5. Actress section</h2>
|
||||
<div class="meta">Currently no way to add/remove actresses from context menu. Big gap — adds an Actress section between Tags and Collections.</div>
|
||||
<div class="body">
|
||||
<div class="menu">
|
||||
<div class="menu-header"><span class="title">MIAA-291</span><span class="title">×</span></div>
|
||||
<div class="menu-link">↗ Open detail</div>
|
||||
<div class="menu-section">
|
||||
<div class="section-head"><span>👥 ACTRESSES</span><span class="count">2</span></div>
|
||||
<div class="actress-row"><span class="av"></span><span style="flex:1; font-size:12px;">Kagami Shuna</span><span class="check" style="color:var(--violet)">✓</span></div>
|
||||
<div class="actress-row"><span class="av"></span><span style="flex:1; font-size:12px;">Yui Nagase</span><span class="check" style="color:var(--violet)">✓</span></div>
|
||||
<input class="menu-input" placeholder="Add actress…" />
|
||||
</div>
|
||||
<div class="menu-section"><div class="section-head"><span>🏷 TAGS</span><span class="count">3</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6. Tags grouped by category -->
|
||||
<div class="mock">
|
||||
<h2>6. Tags grouped by category</h2>
|
||||
<div class="meta">Mirrors the new /tag page. Surfaces structure when the tag list is long.</div>
|
||||
<div class="body">
|
||||
<div class="menu">
|
||||
<div class="menu-header"><span class="title">MIAA-291</span><span class="title">×</span></div>
|
||||
<div class="menu-section">
|
||||
<div class="section-head"><span>🏷 TAGS</span><span class="count">12</span></div>
|
||||
<div class="category-header"><span class="dot" style="background:#fbbf24"></span>BDSM</div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">bondage</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">shibari</span></div>
|
||||
<div class="category-header"><span class="dot" style="background:#a78bfa"></span>BUKKAKE</div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">facial</span></div>
|
||||
<div class="category-header"><span class="dot" style="background:var(--fg-muted)"></span>UNCATEGORISED</div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">anal</span></div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">blowjob</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 7. Recent / frequent tags pinned -->
|
||||
<div class="mock">
|
||||
<h2>7. Recent tags pinned</h2>
|
||||
<div class="meta">Last-5-used tags surface as one-click chips above the full list. After a tagging spree, the next tag is usually one of the same.</div>
|
||||
<div class="body">
|
||||
<div class="menu">
|
||||
<div class="menu-header"><span class="title">MIAA-291</span><span class="title">×</span></div>
|
||||
<div class="menu-section">
|
||||
<div class="section-head"><span>🏷 TAGS</span><span class="count">3</span></div>
|
||||
<div class="recent-row">
|
||||
<span class="recent-chip">+ gyaru</span>
|
||||
<span class="recent-chip">+ shibari</span>
|
||||
<span class="recent-chip">+ pov</span>
|
||||
<span class="recent-chip">+ facial</span>
|
||||
</div>
|
||||
<div style="font-size:9px; color:var(--fg-muted); padding:4px 8px;">RECENT · click to add</div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">anal</span></div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">blowjob</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 8. Search/filter input -->
|
||||
<div class="mock">
|
||||
<h2>8. Search input for long lists</h2>
|
||||
<div class="meta">Filters the rendered list as you type. Already exists when >5 items but could be more visible — promote to top of section unconditionally.</div>
|
||||
<div class="body">
|
||||
<div class="menu">
|
||||
<div class="menu-header"><span class="title">MIAA-291</span><span class="title">×</span></div>
|
||||
<div class="menu-section">
|
||||
<div class="section-head"><span>🏷 TAGS</span><span class="count">38</span></div>
|
||||
<input class="filter-input" placeholder="🔎 Filter tags…" value="bond" />
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">bondage</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">bondage-gear</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 9. Set-as-collection-cover -->
|
||||
<div class="mock">
|
||||
<h2>9. Set as collection cover</h2>
|
||||
<div class="meta">Each collection row shows a star. Click to designate this image as the collection's hero. Currently there's no in-line way to set a collection's cover.</div>
|
||||
<div class="body">
|
||||
<div class="menu">
|
||||
<div class="menu-header"><span class="title">MIAA-291</span><span class="title">×</span></div>
|
||||
<div class="menu-section">
|
||||
<div class="section-head"><span>📁 COLLECTIONS</span><span class="count">2</span></div>
|
||||
<div class="menu-row on cover-of">
|
||||
<span class="check">✓</span>
|
||||
<span class="name">collection 1</span>
|
||||
<span class="pin" title="This image is the collection's cover">★ cover</span>
|
||||
</div>
|
||||
<div class="menu-row on">
|
||||
<span class="check">✓</span>
|
||||
<span class="name">collection 2</span>
|
||||
<span class="pin" style="color:var(--fg-muted)" title="Make this the cover">☆</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 10. Multi-select banner + compact tag grid -->
|
||||
<div class="mock">
|
||||
<h2>10. Bulk-mode banner + compact tag grid</h2>
|
||||
<div class="meta">When N>1 selected: bright banner makes scope obvious; tags switch to a 2-column grid so 30 tags fit in the same height.</div>
|
||||
<div class="body">
|
||||
<div class="menu compact">
|
||||
<div class="multi-banner">⚡ Applying to 12 covers</div>
|
||||
<div class="menu-link">↗ Open first</div>
|
||||
<div class="menu-section">
|
||||
<div class="section-head"><span>🏷 TAGS</span><span class="count">12 / mixed</span></div>
|
||||
<div class="compact-grid">
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">anal</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">blowjob</span></div>
|
||||
<div class="menu-row on"><span class="check">−</span><span class="name">gyaru</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">pov</span></div>
|
||||
<div class="menu-row on"><span class="check">✓</span><span class="name">facial</span></div>
|
||||
<div class="menu-row"><span class="check"></span><span class="name">shibari</span></div>
|
||||
</div>
|
||||
<div style="font-size:9px; color:var(--fg-muted); padding:4px 8px;">✓ on all · − partial · empty = none</div>
|
||||
</div>
|
||||
<div class="menu-delete">🗑 Delete 12</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,328 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex · Cover label — 10 actress display options</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0a0710;
|
||||
--bg-1: #14101e;
|
||||
--fg: #e8e6f0;
|
||||
--fg-dim: #a8a4b8;
|
||||
--fg-muted: #6e6a80;
|
||||
--cyan: #22d3ee;
|
||||
--violet: #a78bfa;
|
||||
--mint: #34d399;
|
||||
--coral: #fb7185;
|
||||
--amber: #fbbf24;
|
||||
--glass: rgba(255,255,255,0.04);
|
||||
--glass-strong: rgba(255,255,255,0.08);
|
||||
--border: rgba(255,255,255,0.08);
|
||||
--border-strong: rgba(255,255,255,0.18);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg-0);
|
||||
color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.page { max-width: 1600px; margin: 0 auto; padding: 24px; }
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
padding: 16px 20px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(34,211,238,0.08), rgba(167,139,250,0.05));
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.page-header h1 { margin: 0 0 8px 0; font-size: 24px; font-weight: 600; }
|
||||
.page-header p { margin: 0; color: var(--fg-dim); font-size: 14px; }
|
||||
|
||||
.grid-mocks {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
/* Mockup wrapper */
|
||||
.mock {
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.mock h2 {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: var(--cyan);
|
||||
font-weight: 600;
|
||||
}
|
||||
.mock .meta {
|
||||
font-size: 11px;
|
||||
color: var(--fg-dim);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.mock .body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background: rgba(255,255,255,0.02);
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
/* The simulated JAV cover */
|
||||
.cover {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
aspect-ratio: 800 / 538;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, transparent 60%, rgba(0,0,0,0.85) 100%),
|
||||
linear-gradient(135deg, #2a1f3a, #1a1428);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.5);
|
||||
}
|
||||
.cover-fake-image {
|
||||
position: absolute; inset: 0;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: rgba(255,255,255,0.18);
|
||||
font-size: 64px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.04em;
|
||||
pointer-events: none;
|
||||
}
|
||||
/* Optional overlay/label area above the gradient */
|
||||
.cover .label {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
/* Reusable bits */
|
||||
.javid {
|
||||
color: var(--cyan);
|
||||
font-family: ui-monospace, SFMono-Regular, monospace;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
letter-spacing: -0.01em;
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.9);
|
||||
}
|
||||
.actress-text {
|
||||
color: rgba(255,255,255,0.85);
|
||||
font-size: 12px;
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.9);
|
||||
}
|
||||
.actress-text-dim { color: rgba(255,255,255,0.6); font-size: 11px; }
|
||||
.actress-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 1px 7px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: rgba(255,255,255,0.85);
|
||||
font-size: 10px;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
.actress-pill.violet {
|
||||
background: rgba(167,139,250,0.18);
|
||||
color: var(--violet);
|
||||
border: 1px solid rgba(167,139,250,0.3);
|
||||
}
|
||||
.avatar {
|
||||
width: 18px; height: 18px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(135deg, #f9a8d4, #a78bfa);
|
||||
flex-shrink: 0;
|
||||
border: 1.5px solid rgba(255,255,255,0.4);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<header class="page-header">
|
||||
<h1>Cover label — actress display options</h1>
|
||||
<p>Each card shows the same fake cover with a different way to render JAV ID + actress name(s). Compare visually.</p>
|
||||
</header>
|
||||
|
||||
<div class="grid-mocks">
|
||||
|
||||
<!-- 1. Stacked simple -->
|
||||
<div class="mock">
|
||||
<h2>1. Stacked simple</h2>
|
||||
<div class="meta">JAVID large on top, actress on the line below in a smaller, dimmer style. Closest to the existing layout — least disruptive.</div>
|
||||
<div class="body">
|
||||
<div class="cover">
|
||||
<div class="cover-fake-image">502 × 335</div>
|
||||
<div class="label">
|
||||
<div class="javid">YST-206</div>
|
||||
<div class="actress-text actress-text-dim">Reika Aiba</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. Inline with separator -->
|
||||
<div class="mock">
|
||||
<h2>2. Inline with separator</h2>
|
||||
<div class="meta">Single row, JAVID · actress. Fits small cards well, looks cluttered with many actresses. Falls back to first actress + count when 2+.</div>
|
||||
<div class="body">
|
||||
<div class="cover">
|
||||
<div class="cover-fake-image">502 × 335</div>
|
||||
<div class="label">
|
||||
<span class="javid">YST-206</span>
|
||||
<span style="color:rgba(255,255,255,0.4); margin: 0 6px;">·</span>
|
||||
<span class="actress-text">Reika Aiba</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Right-aligned actress -->
|
||||
<div class="mock">
|
||||
<h2>3. Right-aligned actress</h2>
|
||||
<div class="meta">JAVID left, actress hugs the right edge. Visual symmetry; good when both texts are short.</div>
|
||||
<div class="body">
|
||||
<div class="cover">
|
||||
<div class="cover-fake-image">502 × 335</div>
|
||||
<div class="label" style="display:flex; justify-content:space-between; align-items:baseline; gap:12px;">
|
||||
<span class="javid">YST-206</span>
|
||||
<span class="actress-text" style="text-align:right;">Reika Aiba</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. Top-corner JAVID, bottom actress -->
|
||||
<div class="mock">
|
||||
<h2>4. Diagonal split (top JAVID, bottom actress)</h2>
|
||||
<div class="meta">JAVID becomes a corner badge in the top-left; actress takes the full bottom row. Pulls them apart visually.</div>
|
||||
<div class="body">
|
||||
<div class="cover" style="align-items: stretch;">
|
||||
<div class="cover-fake-image">502 × 335</div>
|
||||
<div style="position:absolute; top:10px; left:10px; z-index:2; padding:3px 8px; border-radius:6px; background:rgba(0,0,0,0.6); backdrop-filter: blur(4px);">
|
||||
<span class="javid" style="font-size: 13px;">YST-206</span>
|
||||
</div>
|
||||
<div class="label" style="align-self:flex-end;">
|
||||
<div class="actress-text" style="font-weight: 500;">Reika Aiba</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5. Actress as pill -->
|
||||
<div class="mock">
|
||||
<h2>5. Actress as pill</h2>
|
||||
<div class="meta">JAVID stays plain; actress wears a glassy chip. Stands out when there's busy artwork behind. Multi-actress gets multiple chips.</div>
|
||||
<div class="body">
|
||||
<div class="cover">
|
||||
<div class="cover-fake-image">502 × 335</div>
|
||||
<div class="label">
|
||||
<div class="javid" style="margin-bottom: 6px;">YST-206</div>
|
||||
<span class="actress-pill">Reika Aiba</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6. Avatar + name -->
|
||||
<div class="mock">
|
||||
<h2>6. Mini avatar + name</h2>
|
||||
<div class="meta">Tiny circular actress portrait next to the name. Most visually rich; uses the actress portrait you've already uploaded. Multi-actress = stacked avatars.</div>
|
||||
<div class="body">
|
||||
<div class="cover">
|
||||
<div class="cover-fake-image">502 × 335</div>
|
||||
<div class="label">
|
||||
<div class="javid" style="margin-bottom: 6px;">YST-206</div>
|
||||
<div style="display:flex; align-items:center; gap:6px;">
|
||||
<span class="avatar"></span>
|
||||
<span class="actress-text">Reika Aiba</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 7. Hover-reveal actress -->
|
||||
<div class="mock">
|
||||
<h2>7. Hover-reveal actress</h2>
|
||||
<div class="meta">JAVID always visible. Actress fades in on hover, stacked below. Cleanest when not interacting; preview on demand. Sample shows hover state.</div>
|
||||
<div class="body">
|
||||
<div class="cover">
|
||||
<div class="cover-fake-image">502 × 335</div>
|
||||
<div class="label">
|
||||
<div class="javid">YST-206</div>
|
||||
<div class="actress-text actress-text-dim" style="opacity:0.95; margin-top: 2px;">Reika Aiba</div>
|
||||
<div style="font-size: 9px; color: var(--cyan); margin-top: 4px; text-transform: uppercase; letter-spacing: 0.06em; font-family: ui-monospace, monospace;">↑ shown on hover</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 8. Multi-actress chips -->
|
||||
<div class="mock">
|
||||
<h2>8. Multi-actress chips (2+ actresses)</h2>
|
||||
<div class="meta">Each actress gets a violet pill, wrapping if needed. Up to ~3 visible; "+N" overflow chip for more. Especially useful for collab releases.</div>
|
||||
<div class="body">
|
||||
<div class="cover">
|
||||
<div class="cover-fake-image">502 × 335</div>
|
||||
<div class="label">
|
||||
<div class="javid" style="margin-bottom: 6px;">CJOD-333</div>
|
||||
<div style="display:flex; flex-wrap:wrap; gap:4px;">
|
||||
<span class="actress-pill violet">Ichika Matsumoto</span>
|
||||
<span class="actress-pill violet">Mitsuki Nagisa</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 9. Subtle backdrop bar -->
|
||||
<div class="mock">
|
||||
<h2>9. Subtle backdrop label</h2>
|
||||
<div class="meta">Both texts sit inside a low-opacity dark bar across the bottom — high legibility on bright/busy covers. JAVID + actress on two lines.</div>
|
||||
<div class="body">
|
||||
<div class="cover">
|
||||
<div class="cover-fake-image">502 × 335</div>
|
||||
<div class="label" style="background: rgba(0,0,0,0.55); backdrop-filter: blur(8px); border-top: 1px solid rgba(255,255,255,0.08);">
|
||||
<div class="javid">YST-206</div>
|
||||
<div class="actress-text actress-text-dim">Reika Aiba</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 10. Icon-prefixed actress -->
|
||||
<div class="mock">
|
||||
<h2>10. Icon-prefixed actress</h2>
|
||||
<div class="meta">Tiny user icon (👤 or lucide Users) before the name acts as a visual marker, distinguishing actress from other potential meta lines (studio etc).</div>
|
||||
<div class="body">
|
||||
<div class="cover">
|
||||
<div class="cover-fake-image">502 × 335</div>
|
||||
<div class="label">
|
||||
<div class="javid">YST-206</div>
|
||||
<div style="display:flex; align-items:center; gap:5px; margin-top: 2px;">
|
||||
<svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="rgba(255,255,255,0.55)" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="12" cy="7" r="4"/>
|
||||
</svg>
|
||||
<span class="actress-text actress-text-dim">Reika Aiba</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,309 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Detail Page Metadata Strip — Mockups</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-bg-0: oklch(0.16 0.02 280);
|
||||
--color-bg-1: oklch(0.20 0.03 280);
|
||||
--color-fg: oklch(0.96 0.01 280);
|
||||
--color-fg-dim: oklch(0.78 0.02 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: oklch(0.27 0.04 280 / 0.45);
|
||||
--color-glass-strong: oklch(0.30 0.05 280 / 0.65);
|
||||
--color-glass-border: oklch(0.40 0.05 280 / 0.30);
|
||||
--color-glass-border-strong: oklch(0.50 0.06 280 / 0.45);
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-violet: oklch(0.72 0.18 305);
|
||||
--color-coral: oklch(0.74 0.18 25);
|
||||
--color-mint: oklch(0.82 0.14 165);
|
||||
--color-amber: oklch(0.84 0.14 80);
|
||||
}
|
||||
body {
|
||||
background: var(--color-bg-0);
|
||||
color: var(--color-fg);
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
.frame {
|
||||
background: oklch(0.18 0.025 280);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: 1rem;
|
||||
padding: 1rem 1.25rem;
|
||||
}
|
||||
.header {
|
||||
font-family: ui-monospace, "JetBrains Mono", monospace;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--color-cyan);
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
.why {
|
||||
font-size: 11px;
|
||||
color: var(--color-fg-muted);
|
||||
margin-top: 0.25rem;
|
||||
font-style: italic;
|
||||
}
|
||||
.label-mono {
|
||||
font-family: ui-monospace, "JetBrains Mono", monospace;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--color-fg-muted);
|
||||
}
|
||||
.val-mono {
|
||||
font-family: ui-monospace, "JetBrains Mono", monospace;
|
||||
color: var(--color-fg);
|
||||
font-weight: 600;
|
||||
}
|
||||
.glass {
|
||||
background: var(--color-glass);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
}
|
||||
.glass-strong {
|
||||
background: var(--color-glass-strong);
|
||||
border: 1px solid var(--color-glass-border-strong);
|
||||
}
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0.15rem 0.55rem;
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
font-family: ui-monospace, monospace;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.divider-v {
|
||||
width: 1px;
|
||||
align-self: stretch;
|
||||
background: var(--color-glass-border);
|
||||
}
|
||||
.row-current {
|
||||
/* The current production look the user dislikes — for reference at top. */
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 13px;
|
||||
color: var(--color-fg-muted);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="p-6 space-y-6">
|
||||
<header class="space-y-1 mb-2">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Detail Metadata Strip — 8 Variants</h1>
|
||||
<p class="text-sm text-[var(--color-fg-muted)]">Replaces the two-row "RESOLUTION / SIZE / IMPORTED" + "VIDEO (N PARTS) ..." block at <code class="text-[var(--color-cyan)]">localhost:3001/id/YUJ-001</code>. All variants render the same data: cover (800×538, 170 KB, imported 4/29/2026) and video (1920×1080, H.264, 5.7 Mbps, 4.90 GB, 1:56:55, 2 parts).</p>
|
||||
</header>
|
||||
|
||||
<!-- =========================================================
|
||||
Reference: current production
|
||||
========================================================= -->
|
||||
<section class="frame max-w-[820px]">
|
||||
<div class="header text-[var(--color-fg-muted)]">Current — for comparison</div>
|
||||
<div class="space-y-1">
|
||||
<div class="row-current flex flex-wrap items-baseline gap-x-4">
|
||||
<span class="inline-flex items-baseline gap-1.5"><span class="text-[10px] uppercase tracking-wider opacity-70">RESOLUTION</span><span class="text-[var(--color-fg)] font-bold">800×538</span></span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span class="inline-flex items-baseline gap-1.5"><span class="text-[10px] uppercase tracking-wider opacity-70">SIZE</span><span class="text-[var(--color-fg)] font-bold">170 KB</span></span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span class="inline-flex items-baseline gap-1.5"><span class="text-[10px] uppercase tracking-wider opacity-70">IMPORTED</span><span class="text-[var(--color-fg)] font-bold">4/29/2026</span></span>
|
||||
</div>
|
||||
<div class="row-current">
|
||||
<span class="inline-flex items-baseline gap-1.5"><span class="text-[10px] uppercase tracking-wider opacity-70">VIDEO (2 PARTS)</span><span class="text-[var(--color-fg)] font-bold">1920×1080 · H.264 · 5.7 Mbps · 4.90 GB · 1:56:55</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
|
||||
<!-- 1. Icon-prefixed two rows ----------------------------- -->
|
||||
<section class="frame">
|
||||
<div class="header">1 · Iconified rows</div>
|
||||
<p class="why">Replace the all-caps "RESOLUTION / SIZE / IMPORTED" labels with a single leading icon. Same two-row shape, but the row tells you what it is at a glance.</p>
|
||||
<div class="space-y-1.5 mt-3 font-mono text-[13px]">
|
||||
<div class="flex items-center gap-3 text-[var(--color-fg-dim)]">
|
||||
<svg class="w-3.5 h-3.5 shrink-0 text-[var(--color-fg-muted)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>
|
||||
<span class="text-[var(--color-fg)] font-semibold">800×538</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>170 KB</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>imported 4/29/2026</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 text-[var(--color-fg-dim)]">
|
||||
<svg class="w-3.5 h-3.5 shrink-0 text-[var(--color-cyan)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"/><line x1="7" y1="2" x2="7" y2="22"/><line x1="17" y1="2" x2="17" y2="22"/><line x1="2" y1="12" x2="22" y2="12"/><line x1="2" y1="7" x2="7" y2="7"/><line x1="2" y1="17" x2="7" y2="17"/><line x1="17" y1="17" x2="22" y2="17"/><line x1="17" y1="7" x2="22" y2="7"/></svg>
|
||||
<span class="text-[var(--color-fg)] font-semibold">1920×1080</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>H.264</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>5.7 Mbps</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>4.90 GB</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>1:56:55</span>
|
||||
<span class="ml-2 pill" style="background: oklch(0.30 0.04 280); color: var(--color-cyan); border: 1px solid oklch(0.50 0.10 200 / 0.3);">2 parts</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 2. Single line, vertical divider ---------------------- -->
|
||||
<section class="frame">
|
||||
<div class="header">2 · Single line, divided</div>
|
||||
<p class="why">Both rows fold into one. Cover stats sit muted on the left; video gets the cyan accent color it earns by being the actual content. Vertical rule between them makes it scan as two columns of one line.</p>
|
||||
<div class="mt-3 flex items-center gap-3 font-mono text-[13px]">
|
||||
<span class="text-[var(--color-fg-muted)]">800×538 · 170 KB</span>
|
||||
<span class="divider-v" style="height: 1.2rem;"></span>
|
||||
<span class="text-[var(--color-fg)] font-semibold">1920×1080</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>H.264</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>5.7 Mbps</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>4.90 GB</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>1:56:55</span>
|
||||
<span class="pill" style="background: oklch(0.30 0.04 280); color: var(--color-cyan); border: 1px solid oklch(0.50 0.10 200 / 0.3);">2 parts</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 3. Stat tiles (4-up) --------------------------------- -->
|
||||
<section class="frame col-span-1 lg:col-span-2">
|
||||
<div class="header">3 · Stat tiles, video-led</div>
|
||||
<p class="why">Four numeric tiles for the things that matter most — resolution, bitrate, size, length. Cover info reduced to one quiet line under the title. Each tile shows the value big and the label tiny, like a dashboard.</p>
|
||||
<div class="mt-3 grid grid-cols-2 sm:grid-cols-4 gap-2">
|
||||
<div class="rounded-lg p-3 glass">
|
||||
<div class="label-mono mb-1">Resolution</div>
|
||||
<div class="val-mono text-lg">1920×1080</div>
|
||||
</div>
|
||||
<div class="rounded-lg p-3 glass">
|
||||
<div class="label-mono mb-1">Bitrate</div>
|
||||
<div class="val-mono text-lg">5.7 <span class="text-[var(--color-fg-muted)] text-xs">Mbps</span></div>
|
||||
</div>
|
||||
<div class="rounded-lg p-3 glass">
|
||||
<div class="label-mono mb-1">File Size</div>
|
||||
<div class="val-mono text-lg">4.90 <span class="text-[var(--color-fg-muted)] text-xs">GB</span></div>
|
||||
</div>
|
||||
<div class="rounded-lg p-3 glass">
|
||||
<div class="label-mono mb-1">Length</div>
|
||||
<div class="val-mono text-lg">1:56:55</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 flex items-center gap-2 text-[11px] font-mono text-[var(--color-fg-muted)]">
|
||||
<span>cover 800×538 · 170 KB</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>2 parts · H.264</span>
|
||||
<span class="opacity-40">·</span>
|
||||
<span>imported 4/29/2026</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 4. Inline chips ------------------------------------- -->
|
||||
<section class="frame">
|
||||
<div class="header">4 · Inline chips</div>
|
||||
<p class="why">Each fact is a borderless chip — label and value packed inside one rounded badge. Easier to skip past than the current bare strip; harder to cram many facts in.</p>
|
||||
<div class="mt-3 flex flex-wrap gap-1.5">
|
||||
<span class="pill glass" style="color: var(--color-fg-dim);">cover <span class="text-[var(--color-fg)] ml-1">800×538</span></span>
|
||||
<span class="pill glass" style="color: var(--color-fg-dim);">cover size <span class="text-[var(--color-fg)] ml-1">170 KB</span></span>
|
||||
<span class="pill glass" style="color: var(--color-fg-dim);">imported <span class="text-[var(--color-fg)] ml-1">4/29/2026</span></span>
|
||||
</div>
|
||||
<div class="mt-2 flex flex-wrap gap-1.5">
|
||||
<span class="pill" style="background: oklch(0.50 0.10 200 / 0.10); color: var(--color-cyan); border: 1px solid oklch(0.50 0.10 200 / 0.30);">1920×1080</span>
|
||||
<span class="pill" style="background: oklch(0.50 0.10 200 / 0.10); color: var(--color-cyan); border: 1px solid oklch(0.50 0.10 200 / 0.30);">H.264</span>
|
||||
<span class="pill" style="background: oklch(0.50 0.10 200 / 0.10); color: var(--color-cyan); border: 1px solid oklch(0.50 0.10 200 / 0.30);">5.7 Mbps</span>
|
||||
<span class="pill" style="background: oklch(0.50 0.10 200 / 0.10); color: var(--color-cyan); border: 1px solid oklch(0.50 0.10 200 / 0.30);">4.90 GB</span>
|
||||
<span class="pill" style="background: oklch(0.50 0.10 200 / 0.10); color: var(--color-cyan); border: 1px solid oklch(0.50 0.10 200 / 0.30);">1:56:55</span>
|
||||
<span class="pill" style="background: oklch(0.55 0.18 305 / 0.12); color: var(--color-violet); border: 1px solid oklch(0.55 0.18 305 / 0.30);">2 parts</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 5. Definition list, two-column ---------------------- -->
|
||||
<section class="frame">
|
||||
<div class="header">5 · Definition list, two-column</div>
|
||||
<p class="why">Cover left, video right, side by side. Each row is "label : value" in a tight two-column grid. Most "official" looking — leans heavy on the form-y aesthetic.</p>
|
||||
<div class="mt-3 grid grid-cols-2 gap-x-6 gap-y-1.5 text-[12px] font-mono">
|
||||
<div class="flex justify-between gap-3"><span class="label-mono">Resolution</span><span class="val-mono">800×538</span></div>
|
||||
<div class="flex justify-between gap-3"><span class="label-mono">Codec</span><span class="val-mono">H.264</span></div>
|
||||
<div class="flex justify-between gap-3"><span class="label-mono">Cover Size</span><span class="val-mono">170 KB</span></div>
|
||||
<div class="flex justify-between gap-3"><span class="label-mono">Video Resolution</span><span class="val-mono">1920×1080</span></div>
|
||||
<div class="flex justify-between gap-3"><span class="label-mono">Imported</span><span class="val-mono">4/29/2026</span></div>
|
||||
<div class="flex justify-between gap-3"><span class="label-mono">Bitrate</span><span class="val-mono">5.7 Mbps</span></div>
|
||||
<div class="flex justify-between gap-3"><span class="label-mono">Parts</span><span class="val-mono">2</span></div>
|
||||
<div class="flex justify-between gap-3"><span class="label-mono">File Size</span><span class="val-mono">4.90 GB</span></div>
|
||||
<div></div>
|
||||
<div class="flex justify-between gap-3"><span class="label-mono">Length</span><span class="val-mono">1:56:55</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 6. Hero stat: four-up ------------------------------- -->
|
||||
<section class="frame col-span-1 lg:col-span-2">
|
||||
<div class="header">6 · Hero stat — four-up</div>
|
||||
<p class="why">Just the four numbers you actually scan for: Resolution, Bitrate, File Size, Length. Big mono values, tiny caps labels. No caption, no chrome.</p>
|
||||
<div class="mt-3 flex items-end gap-10 flex-wrap">
|
||||
<div>
|
||||
<div class="label-mono mb-1">Resolution</div>
|
||||
<div class="font-mono text-2xl font-semibold tracking-tight text-[var(--color-fg-dim)]">1920×1080</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono mb-1">Bitrate</div>
|
||||
<div class="font-mono text-2xl font-semibold tracking-tight text-[var(--color-fg-dim)]">5.7 Mbps</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono mb-1">File Size</div>
|
||||
<div class="font-mono text-3xl font-semibold tracking-tight">4.90 GB</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono mb-1">Length</div>
|
||||
<div class="font-mono text-3xl font-semibold tracking-tight" style="color: var(--color-cyan);">1:56:55</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 7. Asymmetric — video center stage ------------------ -->
|
||||
<section class="frame">
|
||||
<div class="header">7 · Asymmetric — video front, cover whisper</div>
|
||||
<p class="why">Cover info is rarely the reason you opened the detail page; bury it in a tiny gray caption. Give video the full strip with separators. Recognizes that this is fundamentally a video library.</p>
|
||||
<div class="mt-3 space-y-1">
|
||||
<div class="flex flex-wrap items-baseline gap-x-3 gap-y-1 font-mono">
|
||||
<span class="text-[var(--color-fg)] font-semibold text-[15px]">1920×1080</span>
|
||||
<span class="opacity-30">·</span>
|
||||
<span class="text-[var(--color-fg-dim)] text-[14px]">H.264</span>
|
||||
<span class="opacity-30">·</span>
|
||||
<span class="text-[var(--color-fg-dim)] text-[14px]">5.7 Mbps</span>
|
||||
<span class="opacity-30">·</span>
|
||||
<span class="text-[var(--color-fg-dim)] text-[14px]">4.90 GB</span>
|
||||
<span class="opacity-30">·</span>
|
||||
<span class="text-[var(--color-fg-dim)] text-[14px]">1:56:55</span>
|
||||
<span class="ml-1 pill" style="background: oklch(0.50 0.10 200 / 0.10); color: var(--color-cyan); border: 1px solid oklch(0.50 0.10 200 / 0.30);">2 parts</span>
|
||||
</div>
|
||||
<div class="text-[10px] font-mono text-[var(--color-fg-muted)]">
|
||||
cover · 800×538 · 170 KB · imported 4/29/2026
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 8. Two-column: cover summary, video summary --------- -->
|
||||
<section class="frame">
|
||||
<div class="header">8 · Side-by-side summaries</div>
|
||||
<p class="why">Each side gets its own headed mini-block. Visual parity between cover and video, but they're clearly separated. Same density as current, but the dotted strip becomes structured.</p>
|
||||
<div class="mt-3 grid grid-cols-2 gap-3">
|
||||
<div class="rounded-lg p-3 glass">
|
||||
<div class="label-mono mb-1.5">Cover</div>
|
||||
<div class="font-mono text-[13px]"><span class="font-semibold">800×538</span> <span class="opacity-50 mx-1">·</span> 170 KB</div>
|
||||
<div class="font-mono text-[11px] text-[var(--color-fg-muted)] mt-1">imported 4/29/2026</div>
|
||||
</div>
|
||||
<div class="rounded-lg p-3 glass" style="border-color: oklch(0.50 0.10 200 / 0.30);">
|
||||
<div class="label-mono mb-1.5" style="color: var(--color-cyan);">Video <span class="opacity-60">· 2 parts</span></div>
|
||||
<div class="font-mono text-[13px]"><span class="font-semibold">1920×1080</span> <span class="opacity-50 mx-1">·</span> H.264 <span class="opacity-50 mx-1">·</span> 5.7 Mbps</div>
|
||||
<div class="font-mono text-[11px] text-[var(--color-fg-muted)] mt-1">4.90 GB · 1:56:55</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="text-[11px] text-[var(--color-fg-muted)] mt-6">
|
||||
Pick one (or a frankenstein) and I'll wire it into <code class="text-[var(--color-cyan)]">components/image/ImageDetailView.tsx</code>.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,526 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Detail Page Hero Variants — Pinkudex</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-cyan-deep: oklch(0.65 0.18 200);
|
||||
--color-violet: oklch(0.72 0.22 305);
|
||||
--color-mint: oklch(0.80 0.16 155);
|
||||
--color-coral: oklch(0.72 0.20 25);
|
||||
--color-amber: oklch(0.85 0.16 90);
|
||||
--color-bg: oklch(0.13 0.02 260);
|
||||
--color-bg-1: oklch(0.18 0.025 260);
|
||||
--color-bg-2: oklch(0.22 0.03 260);
|
||||
--color-fg: oklch(0.97 0.01 280);
|
||||
--color-fg-dim: oklch(0.72 0.025 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: color-mix(in oklch, white 6%, transparent);
|
||||
--color-glass-border: color-mix(in oklch, white 14%, transparent);
|
||||
--color-glass-border-strong: color-mix(in oklch, white 22%, transparent);
|
||||
}
|
||||
body {
|
||||
background: var(--color-bg);
|
||||
color: var(--color-fg);
|
||||
font-family: ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
.glass {
|
||||
background: var(--color-glass);
|
||||
backdrop-filter: blur(20px) saturate(140%);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
}
|
||||
.glass-strong {
|
||||
background: color-mix(in oklch, white 10%, transparent);
|
||||
backdrop-filter: blur(24px) saturate(150%);
|
||||
border: 1px solid var(--color-glass-border-strong);
|
||||
}
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 9px;
|
||||
border-radius: 999px;
|
||||
font: 600 10px/1.2 ui-monospace, monospace;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
border: 1px solid;
|
||||
}
|
||||
.pill.cyan { background: oklch(0.82 0.16 200 / 0.10); border-color: oklch(0.82 0.16 200 / 0.40); color: var(--color-cyan); }
|
||||
.pill.amber { background: rgba(251,191,36,0.10); border-color: rgba(251,191,36,0.40); color: rgb(252 211 77); }
|
||||
.pill.mint { background: oklch(0.80 0.16 155 / 0.10); border-color: oklch(0.80 0.16 155 / 0.40); color: var(--color-mint); }
|
||||
.pill.violet{ background: oklch(0.72 0.22 305 / 0.12); border-color: oklch(0.72 0.22 305 / 0.40); color: var(--color-violet); }
|
||||
.pill.muted { background: var(--color-glass); border-color: var(--color-glass-border); color: var(--color-fg-muted); }
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
border: 1px solid var(--color-glass-border);
|
||||
background: var(--color-glass);
|
||||
}
|
||||
.chip.violet { background: color-mix(in oklch, var(--color-violet) 14%, transparent); color: var(--color-violet); border-color: color-mix(in oklch, var(--color-violet) 35%, transparent); }
|
||||
.chip.cyan { background: color-mix(in oklch, var(--color-cyan) 14%, transparent); color: var(--color-cyan); border-color: color-mix(in oklch, var(--color-cyan) 35%, transparent); }
|
||||
.label-mono { font: 600 10px/1 ui-monospace, monospace; letter-spacing: 0.10em; text-transform: uppercase; color: var(--color-fg-muted); }
|
||||
.stars { display: inline-flex; gap: 1px; color: var(--color-cyan); }
|
||||
.star { width: 12px; height: 12px; }
|
||||
.play-btn {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
background: var(--color-cyan); color: black; font-weight: 600;
|
||||
padding: 9px 20px; border-radius: 8px; font-size: 14px;
|
||||
border: 0; cursor: pointer;
|
||||
}
|
||||
.ghost-btn {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
padding: 9px 16px; border-radius: 8px; font-size: 14px;
|
||||
background: var(--color-glass); border: 1px solid var(--color-glass-border);
|
||||
color: var(--color-fg); cursor: pointer;
|
||||
}
|
||||
|
||||
/* Cover frame — locked to the actual cover-image aspect (800/538 ≈ 1.49)
|
||||
and capped at the source dimensions so every variant displays the
|
||||
cover at its true scale or smaller. width:100% lets it fill
|
||||
narrower slots, but max-width prevents it from being scaled UP.
|
||||
*/
|
||||
.cover-frame {
|
||||
aspect-ratio: 800 / 538;
|
||||
max-width: 800px;
|
||||
max-height: 538px;
|
||||
width: 100%;
|
||||
}
|
||||
.cover-ph {
|
||||
background:
|
||||
radial-gradient(60% 80% at 30% 30%, oklch(0.45 0.04 280) 0%, oklch(0.20 0.03 260) 80%),
|
||||
linear-gradient(135deg, oklch(0.30 0.02 260) 0%, oklch(0.18 0.025 260) 100%);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: oklch(0.65 0.01 260 / 0.55);
|
||||
font-weight: 700;
|
||||
user-select: none;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.cover-ph::after {
|
||||
content: "";
|
||||
position: absolute; inset: 0;
|
||||
background:
|
||||
radial-gradient(circle at 70% 80%, color-mix(in oklch, var(--color-violet) 35%, transparent) 0%, transparent 50%),
|
||||
radial-gradient(circle at 25% 70%, color-mix(in oklch, var(--color-cyan) 25%, transparent) 0%, transparent 50%);
|
||||
mix-blend-mode: screen;
|
||||
pointer-events: none;
|
||||
}
|
||||
.cover-ph > span { z-index: 1; font: 700 clamp(20px, 3vw, 36px) ui-monospace, monospace; letter-spacing: 0.05em; }
|
||||
|
||||
/* Backdrop variants */
|
||||
.bd-radial {
|
||||
background:
|
||||
radial-gradient(60% 60% at 30% 30%, color-mix(in oklch, var(--color-violet) 30%, transparent) 0%, transparent 60%),
|
||||
radial-gradient(60% 60% at 80% 70%, color-mix(in oklch, var(--color-cyan) 30%, transparent) 0%, transparent 60%),
|
||||
linear-gradient(180deg, oklch(0.22 0.04 280) 0%, oklch(0.14 0.02 260) 100%);
|
||||
}
|
||||
.bd-violet {
|
||||
background:
|
||||
radial-gradient(70% 70% at 50% 30%, color-mix(in oklch, var(--color-violet) 50%, transparent) 0%, transparent 60%),
|
||||
linear-gradient(180deg, oklch(0.20 0.06 305) 0%, oklch(0.10 0.02 260) 100%);
|
||||
}
|
||||
.bd-cyan {
|
||||
background:
|
||||
radial-gradient(70% 70% at 70% 30%, color-mix(in oklch, var(--color-cyan) 50%, transparent) 0%, transparent 60%),
|
||||
linear-gradient(180deg, oklch(0.18 0.05 200) 0%, oklch(0.10 0.02 260) 100%);
|
||||
}
|
||||
.bd-noir {
|
||||
background:
|
||||
radial-gradient(80% 80% at 50% 0%, oklch(0.22 0.02 260) 0%, transparent 70%),
|
||||
linear-gradient(180deg, oklch(0.10 0.01 260) 0%, oklch(0.05 0.005 260) 100%);
|
||||
}
|
||||
.bd-warm {
|
||||
background:
|
||||
radial-gradient(60% 60% at 30% 30%, color-mix(in oklch, var(--color-coral) 35%, transparent) 0%, transparent 60%),
|
||||
radial-gradient(60% 60% at 80% 70%, color-mix(in oklch, var(--color-amber) 25%, transparent) 0%, transparent 60%),
|
||||
linear-gradient(180deg, oklch(0.20 0.05 35) 0%, oklch(0.10 0.02 35) 100%);
|
||||
}
|
||||
.bd-cinema {
|
||||
background:
|
||||
radial-gradient(80% 50% at 50% 50%, oklch(0.25 0.04 280) 0%, transparent 70%),
|
||||
linear-gradient(180deg, oklch(0.06 0.005 260) 0%, oklch(0.02 0.005 260) 100%);
|
||||
}
|
||||
.bd-poster {
|
||||
background:
|
||||
radial-gradient(circle at 50% 30%, oklch(0.55 0.10 305) 0%, oklch(0.30 0.06 280) 35%, oklch(0.10 0.02 260) 75%);
|
||||
filter: saturate(1.2);
|
||||
}
|
||||
|
||||
.header { color: var(--color-cyan); font: 600 11px/1 ui-monospace, monospace; letter-spacing: 0.10em; text-transform: uppercase; }
|
||||
.why { color: var(--color-fg-muted); font-size: 12px; line-height: 1.5; }
|
||||
.frame { border: 1px solid var(--color-glass-border); border-radius: 18px; overflow: hidden; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen">
|
||||
<main class="max-w-[1700px] mx-auto p-6 space-y-12">
|
||||
<header class="space-y-1">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Detail Page · 10 Hero Variants <span class="text-[var(--color-fg-muted)] text-sm font-normal">· cover capped at 800×538</span></h1>
|
||||
<p class="text-sm text-[var(--color-fg-dim)]">Same record (YUJ-001 · Ichika Matsumoto · 2 parts · VIP). Every cover frame is locked to the source 800×538 aspect and won't scale up beyond it. #07 is the original.</p>
|
||||
</header>
|
||||
|
||||
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
|
||||
<symbol id="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2L9.19 8.63L2 9.24l5.46 4.73L5.82 21z"/></symbol>
|
||||
<symbol id="play" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></symbol>
|
||||
</svg>
|
||||
|
||||
<!-- =========================================================
|
||||
01. POSTER BACKDROP — cover blown up + blurred behind info
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">01 · Poster Backdrop</div>
|
||||
<div class="frame relative" style="min-height: 380px;">
|
||||
<div class="bd-poster absolute inset-0"></div>
|
||||
<div class="absolute inset-0" style="background: linear-gradient(180deg, transparent 0%, oklch(0.06 0.005 260 / 0.85) 70%, oklch(0.06 0.005 260) 100%);"></div>
|
||||
<div class="relative p-8 grid grid-cols-[1fr_320px] gap-6 items-end" style="min-height: 380px;">
|
||||
<div class="space-y-3">
|
||||
<div class="font-mono text-[10px] tracking-[0.25em] text-[var(--color-cyan)]">YUJ-001 · ICHIKA MATSUMOTO</div>
|
||||
<h2 class="text-5xl font-bold leading-[1.05] tracking-tight">Untitled</h2>
|
||||
<div class="flex items-center gap-3 text-[var(--color-fg-dim)]">
|
||||
<span class="stars"><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg></span>
|
||||
<span class="text-xs font-mono opacity-80">5/5 · 1h 56m · 1080p H.264 · 2 parts · 4.90 GB</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1.5 pt-2">
|
||||
<span class="pill cyan">◆ VIP</span>
|
||||
<span class="pill mint">◉ Watched</span>
|
||||
<span class="pill violet">◉ Owned</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 pt-3">
|
||||
<button class="play-btn"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><use href="#play"/></svg> Play</button>
|
||||
<button class="ghost-btn">Edit</button>
|
||||
<span class="chip violet ml-auto">Ichika Matsumoto</span>
|
||||
<span class="chip cyan">📁 Sugar & Spice</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cover-ph cover-frame shadow-2xl"><span>800×538</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">Backdrop is a stylized gradient (faux-poster glow), not the cover itself, so the source image stays at its real 800×538 thumbnail size on the right. Title is the headline. The clearest cinematic feel without ever scaling the cover beyond its source dimensions.</p>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
02. LETTERBOX 21:9 — narrow widescreen hero
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">02 · Letterbox 21:9</div>
|
||||
<div class="frame relative bd-radial" style="aspect-ratio: 21/9;">
|
||||
<div class="absolute inset-x-0 top-0 h-12 bg-black/60"></div>
|
||||
<div class="absolute inset-x-0 bottom-0 h-12 bg-black/60"></div>
|
||||
<div class="absolute inset-0 flex items-center justify-between px-12 py-16">
|
||||
<div class="space-y-2 max-w-[55%]">
|
||||
<div class="font-mono text-[10px] tracking-[0.25em] text-[var(--color-cyan)]">YUJ-001</div>
|
||||
<h2 class="text-4xl font-bold leading-tight">Untitled</h2>
|
||||
<div class="text-xs font-mono text-[var(--color-fg-dim)]">5★ · 1h 56m · 1080p H.264 · 2 parts · Ichika Matsumoto · Sugar & Spice</div>
|
||||
<div class="flex flex-wrap gap-1.5 pt-1">
|
||||
<span class="pill cyan">◆ VIP</span>
|
||||
<span class="pill mint">◉ Watched</span>
|
||||
<span class="pill violet">◉ Owned</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 pt-2">
|
||||
<button class="play-btn"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><use href="#play"/></svg> Play</button>
|
||||
<button class="ghost-btn">Edit</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cover-ph cover-frame shadow-2xl" style="width: 320px;"><span>800×538</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">Cinema-style 21:9 banner with black bars; the cover sits as a 320px thumbnail in the right rail (well under the 800px max). The hero feels wide without forcing the cover to stretch.</p>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
03. GLASS CARD ON BACKDROP — frosted panel floating
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">03 · Glass Card on Backdrop</div>
|
||||
<div class="frame relative bd-violet" style="min-height: 360px;">
|
||||
<div class="absolute inset-0 grid place-items-center p-8">
|
||||
<div class="glass-strong rounded-2xl p-6 w-full max-w-3xl shadow-2xl grid grid-cols-[260px_1fr] gap-5 items-center">
|
||||
<div class="cover-ph cover-frame"><span>800×538</span></div>
|
||||
<div class="space-y-2">
|
||||
<div class="font-mono text-xs tracking-[0.2em] text-[var(--color-cyan)]">YUJ-001</div>
|
||||
<h2 class="text-2xl font-bold leading-tight">Untitled</h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="stars"><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg></span>
|
||||
<span class="text-[11px] font-mono text-[var(--color-fg-dim)]">5/5</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<span class="pill cyan">VIP</span>
|
||||
<span class="pill mint">Watched</span>
|
||||
<span class="pill violet">Owned</span>
|
||||
</div>
|
||||
<div class="text-[12px] font-mono text-[var(--color-fg-dim)]">1080p · H.264 · 5.7 Mbps · 2 parts · 4.90 GB · 1:56:55</div>
|
||||
<div class="flex items-center gap-2 pt-1">
|
||||
<button class="play-btn"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><use href="#play"/></svg> Play</button>
|
||||
<span class="chip violet">Ichika Matsumoto</span>
|
||||
<span class="chip cyan">📁 Sugar & Spice</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">Frosted card floats over a colored backdrop. Cover is 260px wide inside the card, well within max. Reads like a focused dialog. Easy to extend the card vertically as more metadata sections land.</p>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
04. SIDE-BY-SIDE — fixed cover column, info column flows
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">04 · Side-by-Side (cover-locked)</div>
|
||||
<div class="frame relative grid items-center gap-0" style="min-height: 380px; grid-template-columns: 800px 1fr;">
|
||||
<div class="cover-ph cover-frame rounded-none"><span>800×538</span></div>
|
||||
<div class="bd-cyan p-8 flex flex-col justify-center space-y-3 h-full">
|
||||
<div class="font-mono text-[10px] tracking-[0.25em] text-[var(--color-cyan)]">YUJ-001 · ICHIKA MATSUMOTO</div>
|
||||
<h2 class="text-4xl font-bold leading-tight">Untitled</h2>
|
||||
<div class="flex items-center gap-2 text-[var(--color-fg-dim)]">
|
||||
<span class="stars"><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg></span>
|
||||
<span class="text-xs font-mono">5/5 · 1h 56m · 1080p · 2 parts</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<span class="pill cyan">◆ VIP</span>
|
||||
<span class="pill mint">◉ Watched</span>
|
||||
<span class="pill violet">◉ Owned</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button class="play-btn"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><use href="#play"/></svg> Play</button>
|
||||
<button class="ghost-btn">Edit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">Cover column locked to its native 800px width, info column flexes to fill the rest. Cover is shown 1:1 with no scaling. On wide screens the info column gets the extra space; on narrower screens both columns shrink proportionally without distorting the image.</p>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
05. APPLE TV+ BENTO — backdrop + tile grid
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">05 · Apple TV+ Bento</div>
|
||||
<div class="frame relative bd-radial p-6" style="min-height: 380px;">
|
||||
<div class="grid gap-3 h-full" style="grid-template-columns: minmax(0, 800px) 1fr 1fr; grid-template-rows: auto auto;">
|
||||
<div class="row-span-2 cover-ph cover-frame shadow-2xl"><span>800×538</span></div>
|
||||
<div class="glass-strong rounded-xl p-4 col-span-2 space-y-2">
|
||||
<div class="font-mono text-[10px] tracking-[0.25em] text-[var(--color-cyan)]">YUJ-001 · ICHIKA MATSUMOTO</div>
|
||||
<h2 class="text-2xl font-bold leading-tight">Untitled</h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="stars"><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg></span>
|
||||
<span class="text-xs font-mono text-[var(--color-fg-muted)]">5/5</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass-strong rounded-xl p-4 space-y-2">
|
||||
<div class="label-mono">Status</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span class="pill cyan">VIP</span>
|
||||
<span class="pill mint">Watched</span>
|
||||
<span class="pill violet">Owned</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass-strong rounded-xl p-4 space-y-2">
|
||||
<div class="label-mono">Video · 2 parts</div>
|
||||
<div class="text-[12px] font-mono">1080p · H.264 · 5.7 Mbps</div>
|
||||
<div class="text-[11px] text-[var(--color-fg-muted)]">4.90 GB · 1:56:55</div>
|
||||
<button class="play-btn mt-1"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><use href="#play"/></svg> Play</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">Apple TV+ bento. Cover tile capped at 800px (`minmax(0, 800px)`), info tiles fill the rest of the row. On narrower screens the cover shrinks proportionally; never grows. Clean foundation for adding HDR / Audio / Subs tiles later.</p>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
06. STACKED — title hero on top, cover at native size below
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">06 · Stacked (cover follows headline)</div>
|
||||
<div class="frame relative bd-violet">
|
||||
<div class="p-8 space-y-4">
|
||||
<div class="font-mono text-[10px] tracking-[0.3em] text-[var(--color-cyan)]">YUJ-001 · ICHIKA MATSUMOTO · SUGAR & SPICE</div>
|
||||
<h2 class="text-5xl font-bold leading-none tracking-tight">Untitled</h2>
|
||||
<div class="flex items-center gap-3 text-[var(--color-fg-dim)]">
|
||||
<span class="stars"><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg></span>
|
||||
<span class="text-xs font-mono">1h 56m · 1080p H.264 · 2 parts · 4.90 GB</span>
|
||||
<span class="pill cyan">VIP</span>
|
||||
<span class="pill mint">Watched</span>
|
||||
<span class="pill violet">Owned</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button class="play-btn"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><use href="#play"/></svg> Play</button>
|
||||
<button class="ghost-btn">Edit metadata</button>
|
||||
<button class="ghost-btn">Reveal in folder</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-8 pb-8 flex justify-center">
|
||||
<div class="cover-ph cover-frame shadow-2xl"><span>800×538</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">Headline + actions on top, cover anchored below at its real 800×538 size, centered. No banner stretching. Reads top-to-bottom like an article. Plays cleanly when the page is wider than 800px — the cover sits in its own breathing room.</p>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
07. ORIGINAL — kept as reference
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">07 · Original (kept)</div>
|
||||
<div class="frame">
|
||||
<div class="bd-radial relative p-8" style="min-height: 320px;">
|
||||
<div class="absolute right-8 top-8 cover-ph cover-frame" style="width: 320px;"><span>800×538</span></div>
|
||||
<div class="max-w-[60%] space-y-3">
|
||||
<div class="font-mono text-[10px] tracking-[0.2em] text-[var(--color-cyan)]">YUJ-001 · ICHIKA MATSUMOTO</div>
|
||||
<h2 class="text-4xl font-bold leading-tight">Untitled</h2>
|
||||
<div class="flex items-center gap-2 text-[var(--color-fg-dim)]">
|
||||
<span class="stars"><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg></span>
|
||||
<span class="text-xs font-mono">5/5 · 1h 56m · 1080p H.264 · 2 parts</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<span class="pill cyan">◆ VIP</span>
|
||||
<span class="pill mint">◉ Watched</span>
|
||||
<span class="pill violet">◉ Owned</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 pt-2">
|
||||
<button class="play-btn"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><use href="#play"/></svg> Play</button>
|
||||
<button class="ghost-btn">Edit metadata</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-[var(--color-bg-2)] p-5 grid grid-cols-3 gap-6 text-sm">
|
||||
<div>
|
||||
<div class="label-mono mb-2">Cover</div>
|
||||
<div class="font-mono">800×538 · 170 KB</div>
|
||||
<div class="font-mono text-[var(--color-fg-muted)] text-xs">Imported 4/29/2026</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono mb-2">People</div>
|
||||
<span class="chip violet">Ichika Matsumoto</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono mb-2">Collection</div>
|
||||
<span class="chip cyan">📁 Sugar & Spice</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">The original. Cover top-right at 320px native ratio, info column left, secondary metadata strip below. Kept here for direct comparison.</p>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
08. NOIR CINEMA — pure dark, neon accent, cover landscape
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">08 · Noir Cinema</div>
|
||||
<div class="frame relative bd-noir" style="min-height: 380px;">
|
||||
<div class="absolute inset-0 grid items-center gap-8 p-10" style="grid-template-columns: 1fr minmax(0, 460px);">
|
||||
<div class="space-y-4">
|
||||
<div class="font-mono text-[10px] tracking-[0.3em]" style="color: var(--color-cyan-deep);">— YUJ-001 —</div>
|
||||
<h2 class="font-mono text-7xl font-bold leading-none tracking-wide" style="color: var(--color-cyan);">YUJ<span class="text-[var(--color-fg)]">001</span></h2>
|
||||
<div class="text-[var(--color-fg-dim)] italic">Untitled · Ichika Matsumoto</div>
|
||||
<div class="flex items-center gap-3 text-[var(--color-fg-muted)] text-[11px] font-mono uppercase tracking-wider">
|
||||
<span class="stars"><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg></span>
|
||||
<span>·</span>
|
||||
<span>1080p</span>
|
||||
<span>·</span>
|
||||
<span>1:56:55</span>
|
||||
<span>·</span>
|
||||
<span>2 parts</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 pt-1">
|
||||
<button class="play-btn"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><use href="#play"/></svg> Play</button>
|
||||
<button class="ghost-btn">Edit</button>
|
||||
<span class="chip cyan ml-3">VIP</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cover-ph cover-frame shadow-2xl border border-[var(--color-glass-border)]"><span>800×538</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">Pure black, single cyan accent, oversized type as art. Cover restored to its native landscape ratio (was portrait 2:3 in the previous draft, which would've stretched the image). Most restrained variant; loses warmth, wins elegance.</p>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
09. LOBBY CARD — landscape poster with credits
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">09 · Lobby Card (landscape poster with credits)</div>
|
||||
<div class="frame relative bd-warm" style="min-height: 480px;">
|
||||
<div class="grid items-center gap-8 p-8" style="grid-template-columns: minmax(0, 800px) 1fr;">
|
||||
<div class="cover-ph cover-frame shadow-2xl"><span>800×538</span></div>
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-1">
|
||||
<div class="font-mono text-[10px] tracking-[0.3em] text-[var(--color-cyan)]">A PINKUDEX RECORD</div>
|
||||
<div class="font-mono text-3xl font-bold tracking-wide">YUJ-001</div>
|
||||
<div class="text-2xl italic font-light text-[var(--color-fg-dim)]">"Untitled"</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="stars"><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg></span>
|
||||
<span class="text-[11px] font-mono uppercase tracking-wider text-[var(--color-fg-muted)]">5 stars</span>
|
||||
</div>
|
||||
<div class="space-y-1 text-[12px] font-mono text-[var(--color-fg-dim)]">
|
||||
<div><span class="label-mono mr-2">Starring</span> Ichika Matsumoto</div>
|
||||
<div><span class="label-mono mr-2">Collection</span> Sugar & Spice</div>
|
||||
<div><span class="label-mono mr-2">Format</span> 1920×1080 · H.264 · 5.7 Mbps</div>
|
||||
<div><span class="label-mono mr-2">Runtime</span> 1h 56m 55s · 2 parts · 4.90 GB</div>
|
||||
<div><span class="label-mono mr-2">Imported</span> 4/29/2026</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1.5 pt-1">
|
||||
<span class="pill cyan">◆ VIP</span>
|
||||
<span class="pill mint">◉ Watched</span>
|
||||
<span class="pill violet">◉ Owned</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 pt-2 border-t border-[var(--color-glass-border)]">
|
||||
<button class="play-btn"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><use href="#play"/></svg> Play</button>
|
||||
<button class="ghost-btn">Edit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">Renamed from "Movie Poster" — JAV covers are landscape, so this is more like a movie-theater lobby card than a portrait poster. Cover anchored left at native size, info reads as poster credits ("Starring", "Format", "Runtime"). Most info-rich variant while keeping the cinematic frame.</p>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
10. CONTINUE WATCHING — Netflix row card with progress
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">10 · Continue Watching</div>
|
||||
<div class="frame relative" style="min-height: 200px;">
|
||||
<div class="bd-cinema absolute inset-0"></div>
|
||||
<div class="relative p-6 grid gap-6 items-center" style="grid-template-columns: 380px 1fr; min-height: 200px;">
|
||||
<div class="cover-ph cover-frame shadow-2xl"><span>800×538</span></div>
|
||||
<div class="space-y-2">
|
||||
<div class="font-mono text-[10px] tracking-[0.25em] text-[var(--color-cyan)]">YUJ-001 · 2 PARTS · 1080p H.264</div>
|
||||
<h2 class="text-2xl font-bold leading-tight">Untitled <span class="text-[var(--color-fg-muted)] text-base font-normal italic">— Ichika Matsumoto</span></h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="stars"><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg><svg class="star"><use href="#star"/></svg></span>
|
||||
<span class="pill cyan">VIP</span>
|
||||
<span class="pill mint">Watched</span>
|
||||
<span class="pill violet">Owned</span>
|
||||
<span class="text-[11px] font-mono text-[var(--color-fg-muted)]">· 4.90 GB · 1h 56m</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="chip violet">Ichika Matsumoto</span>
|
||||
<span class="chip cyan">📁 Sugar & Spice</span>
|
||||
</div>
|
||||
<div class="space-y-1 pt-1">
|
||||
<div class="flex items-center justify-between text-[10px] font-mono text-[var(--color-fg-muted)]">
|
||||
<span>YOU LEFT OFF · 47:32</span>
|
||||
<span>1:56:55</span>
|
||||
</div>
|
||||
<div class="h-1 rounded-full bg-[var(--color-glass)] overflow-hidden">
|
||||
<div class="h-full bg-[var(--color-cyan)]" style="width: 41%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 pt-1">
|
||||
<button class="play-btn"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><use href="#play"/></svg> Resume</button>
|
||||
<button class="ghost-btn">Restart</button>
|
||||
<button class="ghost-btn ml-auto">Edit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">Netflix-row card. 380px cover thumbnail at native ratio (was 16:9 in the previous draft, which would've cropped the cover). Progress bar prominent, "Resume" as primary action. Most action-oriented; loses headline scale.</p>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,297 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Detail Page — Spacing Pass (Same Design, Symmetric Rhythm)</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-bg-0: oklch(0.16 0.02 280);
|
||||
--color-bg-1: oklch(0.20 0.03 280);
|
||||
--color-fg: oklch(0.96 0.01 280);
|
||||
--color-fg-dim: oklch(0.78 0.02 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: oklch(0.27 0.04 280 / 0.45);
|
||||
--color-glass-strong: oklch(0.30 0.05 280 / 0.65);
|
||||
--color-glass-border: oklch(0.40 0.05 280 / 0.30);
|
||||
--color-glass-border-strong: oklch(0.50 0.06 280 / 0.45);
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-violet: oklch(0.72 0.18 305);
|
||||
--color-coral: oklch(0.74 0.18 25);
|
||||
--color-mint: oklch(0.82 0.14 165);
|
||||
}
|
||||
body {
|
||||
background: var(--color-bg-0);
|
||||
color: var(--color-fg);
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
.glass {
|
||||
background: var(--color-glass);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
}
|
||||
.glass-strong {
|
||||
background: var(--color-glass-strong);
|
||||
border: 1px solid var(--color-glass-border-strong);
|
||||
}
|
||||
.label-mono {
|
||||
font-family: ui-monospace, "JetBrains Mono", monospace;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--color-fg-muted);
|
||||
}
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0 0.78rem;
|
||||
height: 31px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
font-family: ui-monospace, monospace;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
border: 1px solid var(--color-glass-border);
|
||||
background: transparent;
|
||||
color: var(--color-fg-muted);
|
||||
justify-content: center;
|
||||
}
|
||||
.pill.on-mint { color: var(--color-mint); border-color: oklch(0.82 0.14 165 / 0.4); background: oklch(0.82 0.14 165 / 0.10); }
|
||||
.pill.on-cyan { color: var(--color-cyan); border-color: oklch(0.82 0.16 200 / 0.4); background: oklch(0.82 0.16 200 / 0.10); }
|
||||
.pill.on-violet { color: var(--color-violet); border-color: oklch(0.72 0.18 305 / 0.4); background: oklch(0.72 0.18 305 / 0.10); }
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0 0.78rem;
|
||||
height: 29px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
border: 1px solid var(--color-glass-border);
|
||||
color: var(--color-fg-muted);
|
||||
}
|
||||
.chip-violet { color: var(--color-violet); border-color: oklch(0.72 0.18 305 / 0.4); background: oklch(0.72 0.18 305 / 0.14); }
|
||||
.chip-cyan { color: var(--color-cyan); border-color: oklch(0.82 0.16 200 / 0.4); background: oklch(0.82 0.16 200 / 0.14); }
|
||||
.frame-label {
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--color-cyan);
|
||||
}
|
||||
.why { font-size: 12px; color: var(--color-fg-muted); }
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
height: 33px;
|
||||
padding: 0 0.83rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 12px;
|
||||
border: 1px solid var(--color-glass-border);
|
||||
background: var(--color-glass);
|
||||
color: var(--color-fg);
|
||||
}
|
||||
.btn-coral {
|
||||
color: var(--color-coral);
|
||||
background: oklch(0.74 0.18 25 / 0.10);
|
||||
border-color: oklch(0.74 0.18 25 / 0.30);
|
||||
}
|
||||
/* Visual spacing-guide: a faint dotted box around every gap-bearing element. */
|
||||
.show-rhythm > * { outline: 1px dotted oklch(0.55 0.16 200 / 0.30); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="p-6 space-y-8 max-w-[1400px] mx-auto">
|
||||
|
||||
<header class="space-y-1">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Detail Page · Spacing Pass</h1>
|
||||
<p class="why max-w-[820px]">
|
||||
Same design, same components, same colors, same chip/pill/button styles — only margins, paddings, and gaps changed for strict symmetry. One set of spacing tokens applied everywhere.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- ====================================================================
|
||||
Spacing tokens
|
||||
==================================================================== -->
|
||||
<section class="glass rounded-2xl p-5">
|
||||
<div class="frame-label mb-3">Tokens applied throughout (snug + 1)</div>
|
||||
<div class="grid grid-cols-2 sm:grid-cols-4 gap-x-6 gap-y-2 text-[12px] font-mono text-[var(--color-fg-dim)]">
|
||||
<div><span class="text-[var(--color-fg-muted)]">card padding</span> <code class="text-[var(--color-cyan)]">p-[15px]</code> · 15 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">card gap</span> <code class="text-[var(--color-cyan)]">gap-[9px]</code> · 9 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">section gap</span> <code class="text-[var(--color-cyan)]">space-y-[15px]</code> · 15 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">label → content</span> <code class="text-[var(--color-cyan)]">mb-[7px]</code> · 7 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">chip gap</span> <code class="text-[var(--color-cyan)]">gap-[7px]</code> · 7 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">chip height</span> 29 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">pill height</span> 31 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">button height</span> 33 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">card radius</span> <code class="text-[var(--color-cyan)]">rounded-2xl</code> · 16 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">stat label → value</span> <code class="text-[var(--color-cyan)]">mb-[5px]</code> · 5 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">flag pill grid</span> <code class="text-[var(--color-cyan)]">gap-[7px]</code> · 7 px</div>
|
||||
<div><span class="text-[var(--color-fg-muted)]">action btn grid</span> <code class="text-[var(--color-cyan)]">gap-[7px]</code> · 7 px</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================
|
||||
Side-by-side: current vs symmetric
|
||||
==================================================================== -->
|
||||
<section class="grid grid-cols-1 xl:grid-cols-2 gap-8">
|
||||
|
||||
<!-- BEFORE (current) ............................................. -->
|
||||
<div class="space-y-2">
|
||||
<div class="frame-label !text-[var(--color-fg-muted)]">BEFORE · current</div>
|
||||
<div class="space-y-2">
|
||||
<!-- Top card: mixed paddings and dividers -->
|
||||
<div class="glass rounded-2xl p-4 space-y-3">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<div class="font-mono text-[var(--color-cyan)] text-xl font-semibold">YUJ-001</div>
|
||||
<div class="text-sm italic text-[var(--color-fg-muted)] mt-1">Untitled</div>
|
||||
</div>
|
||||
<div class="flex gap-1 text-[var(--color-cyan)]"><span>★</span><span>★</span><span>★</span><span>★</span><span>★</span></div>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 gap-1.5">
|
||||
<button class="pill on-cyan">⬢ VIP</button>
|
||||
<button class="pill">☆ Favorite</button>
|
||||
<button class="pill on-mint">◉ Watched</button>
|
||||
<button class="pill on-violet">▣ Owned</button>
|
||||
</div>
|
||||
<div class="text-[12px] font-mono text-[var(--color-fg-muted)]">
|
||||
<span class="opacity-70 text-[10px] uppercase tracking-wider">RESOLUTION</span> <span class="text-[var(--color-fg)] font-bold">800×538</span>
|
||||
<span class="mx-2 opacity-40">·</span>
|
||||
<span class="opacity-70 text-[10px] uppercase tracking-wider">SIZE</span> <span class="text-[var(--color-fg)] font-bold">170 KB</span>
|
||||
<span class="mx-2 opacity-40">·</span>
|
||||
<span class="opacity-70 text-[10px] uppercase tracking-wider">IMPORTED</span> <span class="text-[var(--color-fg)] font-bold">4/29/2026</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-end justify-center gap-x-10 gap-y-3 pt-2 border-t border-[var(--color-glass-border)]">
|
||||
<div><div class="label-mono mb-1">Resolution</div><div class="font-mono text-2xl font-semibold text-[var(--color-fg-dim)]">1920×1080</div></div>
|
||||
<div><div class="label-mono mb-1">Bitrate</div><div class="font-mono text-2xl font-semibold text-[var(--color-fg-dim)]">5.7 Mbps</div></div>
|
||||
<div><div class="label-mono mb-1">File Size</div><div class="font-mono text-3xl font-semibold">8.65 GB</div></div>
|
||||
<div><div class="label-mono mb-1">Length</div><div class="font-mono text-3xl font-semibold text-[var(--color-cyan)]">3:53:50</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Actresses card alone -->
|
||||
<div class="glass rounded-2xl p-4 space-y-2">
|
||||
<div class="label-mono">Actresses</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<span class="chip chip-violet">Ichika Matsumoto</span>
|
||||
<button class="chip">+ Add</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Tags+Collections fused (inconsistent) -->
|
||||
<div class="glass rounded-2xl p-4 space-y-3">
|
||||
<div>
|
||||
<div class="label-mono mb-1">Tags</div>
|
||||
<button class="chip">+ Add tag</button>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono mb-1">Collections</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<span class="chip chip-cyan">Sugar & Spice ✕</span>
|
||||
<button class="chip">+ Add to collection</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Action row, orphaned -->
|
||||
<div class="grid grid-cols-3 gap-2 px-1">
|
||||
<button class="btn">✎ Edit Metadata</button>
|
||||
<button class="btn">⤓ Import .nfo / JSON</button>
|
||||
<button class="btn btn-coral">🗑 Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="text-[11px] font-mono text-[var(--color-fg-muted)] mt-2 space-y-0.5 list-disc list-inside">
|
||||
<li>card padding alternates p-4 / mixed</li>
|
||||
<li>card vertical gap is 8px (space-y-2) but identity card uses space-y-3 internally</li>
|
||||
<li>flag pill grid gap differs from action button grid gap</li>
|
||||
<li>cover dotted strip uses a different label style than the hero stats</li>
|
||||
<li>Tags + Collections share a card; Actresses doesn't (inconsistent grouping)</li>
|
||||
<li>Action buttons sit OUTSIDE every card → break the rhythm</li>
|
||||
<li>chip + pill + button heights all different and arbitrary</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- AFTER (symmetric, balanced) .................................. -->
|
||||
<div class="space-y-2">
|
||||
<div class="frame-label">AFTER · symmetric · snug+1 · same design</div>
|
||||
<div style="display: flex; flex-direction: column; gap: 9px;">
|
||||
<!-- Top card: 15 px padding, 15 px section gap -->
|
||||
<div class="glass rounded-2xl" style="padding: 15px; display: flex; flex-direction: column; gap: 15px;">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="space-y-1">
|
||||
<div class="font-mono text-[var(--color-cyan)] text-xl font-semibold leading-none">YUJ-001</div>
|
||||
<div class="text-sm italic text-[var(--color-fg-muted)] leading-none">Untitled</div>
|
||||
</div>
|
||||
<div class="flex gap-1 text-[var(--color-cyan)]"><span>★</span><span>★</span><span>★</span><span>★</span><span>★</span></div>
|
||||
</div>
|
||||
<div class="grid grid-cols-4" style="gap: 7px;">
|
||||
<button class="pill on-cyan">⬢ VIP</button>
|
||||
<button class="pill">☆ Favorite</button>
|
||||
<button class="pill on-mint">◉ Watched</button>
|
||||
<button class="pill on-violet">▣ Owned</button>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-baseline justify-center gap-x-5 gap-y-1 border-t border-[var(--color-glass-border)] text-[12px] font-mono" style="padding-top: 15px;">
|
||||
<span><span class="label-mono">Resolution</span> <span class="text-[var(--color-fg)] ml-1.5">800×538</span></span>
|
||||
<span class="opacity-30">·</span>
|
||||
<span><span class="label-mono">Size</span> <span class="text-[var(--color-fg)] ml-1.5">170 KB</span></span>
|
||||
<span class="opacity-30">·</span>
|
||||
<span><span class="label-mono">Imported</span> <span class="text-[var(--color-fg)] ml-1.5">4/29/2026</span></span>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 text-center" style="gap: 13px;">
|
||||
<div><div class="label-mono" style="margin-bottom: 5px;">Resolution</div><div class="font-mono text-2xl font-semibold text-[var(--color-fg-dim)] leading-none">1920×1080</div></div>
|
||||
<div><div class="label-mono" style="margin-bottom: 5px;">Bitrate</div><div class="font-mono text-2xl font-semibold text-[var(--color-fg-dim)] leading-none">5.7 Mbps</div></div>
|
||||
<div><div class="label-mono" style="margin-bottom: 5px;">File Size</div><div class="font-mono text-3xl font-semibold leading-none">8.65 GB</div></div>
|
||||
<div><div class="label-mono" style="margin-bottom: 5px;">Length</div><div class="font-mono text-3xl font-semibold text-[var(--color-cyan)] leading-none">3:53:50</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Actresses -->
|
||||
<div class="glass rounded-2xl" style="padding: 15px; display: flex; flex-direction: column; gap: 7px;">
|
||||
<div class="label-mono">Actresses</div>
|
||||
<div class="flex flex-wrap" style="gap: 7px;">
|
||||
<span class="chip chip-violet">Ichika Matsumoto</span>
|
||||
<button class="chip">+ Add</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Tags -->
|
||||
<div class="glass rounded-2xl" style="padding: 15px; display: flex; flex-direction: column; gap: 7px;">
|
||||
<div class="label-mono">Tags</div>
|
||||
<div class="flex flex-wrap" style="gap: 7px;">
|
||||
<button class="chip">+ Add tag</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Collections -->
|
||||
<div class="glass rounded-2xl" style="padding: 15px; display: flex; flex-direction: column; gap: 7px;">
|
||||
<div class="label-mono">Collections</div>
|
||||
<div class="flex flex-wrap" style="gap: 7px;">
|
||||
<span class="chip chip-cyan">Sugar & Spice ✕</span>
|
||||
<button class="chip">+ Add to collection</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Action buttons — separate from any card. Same 9 px card-gap
|
||||
above and 7 px horizontal gap matching chip clusters. -->
|
||||
<div class="grid grid-cols-3" style="gap: 7px;">
|
||||
<button class="btn">✎ Edit Metadata</button>
|
||||
<button class="btn">⤓ Import .nfo / JSON</button>
|
||||
<button class="btn btn-coral">🗑 Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="text-[11px] font-mono text-[var(--color-fg-muted)] mt-2 space-y-0.5 list-disc list-inside">
|
||||
<li>every card: same <code class="text-[var(--color-cyan)]">p-[15px]</code>, <code class="text-[var(--color-cyan)]">rounded-2xl</code> (16 px)</li>
|
||||
<li>card-to-card gap: <code class="text-[var(--color-cyan)]">gap-[9px]</code></li>
|
||||
<li>internal section gap: <code class="text-[var(--color-cyan)]">space-y-[15px]</code> for blocks, <code class="text-[var(--color-cyan)]">gap-[7px]</code> for label→chips</li>
|
||||
<li>flag pill grid · chip clusters · action btn grid: all <code class="text-[var(--color-cyan)]">gap-[7px]</code></li>
|
||||
<li>chip 29 · pill 31 · button 33 — even 2 px ladder, all bumped +1</li>
|
||||
<li>cover row + hero stats: SAME label-mono treatment, no bold-caps strip</li>
|
||||
<li>Actresses · Tags · Collections each get their own card</li>
|
||||
<li>action buttons sit OUTSIDE any card, on the same <code class="text-[var(--color-cyan)]">gap-[9px]</code> rhythm</li>
|
||||
<li>stat values get <code class="text-[var(--color-cyan)]">leading-none</code> so label-to-value gap is exact across all four</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<footer class="text-[11px] text-[var(--color-fg-muted)]">
|
||||
Same design — only spacing tokens unified. Approve and I'll apply these exact values to <code class="text-[var(--color-cyan)]">components/image/ImageDetailView.tsx</code>.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,352 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex · Merged Filter (Marks + Watch State + Has…)</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0a0710;
|
||||
--bg-1: #14101e;
|
||||
--bg-2: #1c1729;
|
||||
--fg: #e8e6f0;
|
||||
--fg-dim: #a8a4b8;
|
||||
--fg-muted: #6e6a80;
|
||||
--cyan: #22d3ee;
|
||||
--violet: #a78bfa;
|
||||
--mint: #34d399;
|
||||
--coral: #fb7185;
|
||||
--amber: #fbbf24;
|
||||
--glass: rgba(255,255,255,0.04);
|
||||
--glass-strong: rgba(255,255,255,0.08);
|
||||
--border: rgba(255,255,255,0.08);
|
||||
--border-strong: rgba(255,255,255,0.18);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0; background: var(--bg-0); color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.page { max-width: 1100px; margin: 0 auto; padding: 24px; }
|
||||
.page-header {
|
||||
margin-bottom: 24px; padding: 16px 20px; border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(34,211,238,0.08), rgba(167,139,250,0.05));
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.page-header h1 { margin: 0 0 8px 0; font-size: 22px; font-weight: 600; }
|
||||
.page-header p { margin: 0; color: var(--fg-dim); font-size: 13px; }
|
||||
.page-header strong { color: var(--fg); }
|
||||
|
||||
/* Toolbar */
|
||||
.toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; }
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 6px 14px; border-radius: 999px;
|
||||
background: var(--glass); border: 1px solid var(--border-strong);
|
||||
font-size: 13px; color: var(--fg-dim);
|
||||
min-width: 140px; justify-content: space-between;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.pill .lbl { display: inline-flex; align-items: center; gap: 6px; }
|
||||
.pill .cv { font-size: 10px; opacity: 0.5; }
|
||||
.pill.all { background: rgba(34,211,238,0.15); border-color: rgba(34,211,238,0.4); color: var(--cyan); min-width: auto; }
|
||||
.pill.disabled { opacity: 0.45; }
|
||||
.pill.active-cyan {
|
||||
background: rgba(34,211,238,0.10);
|
||||
border-color: rgba(34,211,238,0.4);
|
||||
color: var(--cyan);
|
||||
}
|
||||
.badge {
|
||||
display: inline-grid; place-items: center;
|
||||
min-width: 18px; height: 18px;
|
||||
border-radius: 999px;
|
||||
background: var(--cyan); color: #0a0710;
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 10px; font-weight: 700;
|
||||
padding: 0 4px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
/* Popover */
|
||||
.panel {
|
||||
width: 460px;
|
||||
background: var(--bg-0);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 24px 60px rgba(0,0,0,0.6);
|
||||
overflow: hidden;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.section { padding: 14px 16px; }
|
||||
.section + .section { border-top: 1px solid var(--border); }
|
||||
|
||||
.section-h {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 10px; text-transform: uppercase; letter-spacing: 0.07em;
|
||||
color: var(--cyan);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.section-h .left { display: inline-flex; align-items: center; gap: 6px; }
|
||||
.section-h .left .ico { display: inline-grid; place-items: center; }
|
||||
.section-h .left .ico svg { width: 12px; height: 12px; }
|
||||
.section-h .count {
|
||||
color: var(--cyan); font-weight: 700; margin-left: 4px;
|
||||
}
|
||||
.section-h .reset {
|
||||
color: var(--fg-muted); font-family: ui-monospace, monospace;
|
||||
cursor: pointer; padding: 2px 6px; border-radius: 4px;
|
||||
}
|
||||
.section-h .reset:hover { color: var(--fg); background: var(--glass); }
|
||||
.section-h .reset.dim { opacity: 0.4; pointer-events: none; }
|
||||
.section-foot {
|
||||
display: flex; justify-content: flex-end;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.section-foot .reset {
|
||||
color: var(--fg-muted); font-family: ui-monospace, monospace;
|
||||
font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;
|
||||
cursor: pointer; padding: 2px 6px; border-radius: 4px;
|
||||
}
|
||||
.section-foot .reset:hover { color: var(--fg); background: var(--glass); }
|
||||
.section-foot .reset.dim { opacity: 0.4; pointer-events: none; }
|
||||
|
||||
/* Marks: checkbox rows */
|
||||
.check-row {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 6px 4px; border-radius: 8px; cursor: pointer; font-size: 13px;
|
||||
color: var(--fg-dim);
|
||||
}
|
||||
.check-row:hover { background: var(--glass); color: var(--fg); }
|
||||
.check-row.on.cyan { background: rgba(34,211,238,0.10); color: var(--cyan); }
|
||||
.check-row.on.amber { background: rgba(251,191,36,0.10); color: var(--amber); }
|
||||
.check-row.on.violet { background: rgba(167,139,250,0.10); color: var(--violet); }
|
||||
.check-row.on.muted { background: var(--glass-strong); color: var(--fg); }
|
||||
.cb {
|
||||
width: 16px; height: 16px; border-radius: 4px;
|
||||
display: grid; place-items: center;
|
||||
border: 1px solid var(--border-strong);
|
||||
font-size: 10px; flex-shrink: 0;
|
||||
}
|
||||
.cb.on.cyan { background: rgba(34,211,238,0.30); border-color: var(--cyan); color: var(--cyan); }
|
||||
.cb.on.amber { background: rgba(251,191,36,0.25); border-color: var(--amber); color: var(--amber); }
|
||||
.cb.on.violet { background: rgba(167,139,250,0.30); border-color: var(--violet); color: var(--violet); }
|
||||
.cb.on.muted { background: rgba(168,164,184,0.30); border-color: var(--fg-dim); color: var(--fg); }
|
||||
.check-row .ico { display: inline-grid; place-items: center; }
|
||||
.check-row .ico svg { width: 14px; height: 14px; }
|
||||
.check-row .ico.cyan { color: var(--cyan); }
|
||||
.check-row .ico.amber { color: var(--amber); }
|
||||
.check-row .ico.violet { color: var(--violet); }
|
||||
.check-row .ico.muted { color: var(--fg-muted); }
|
||||
.check-row .name { flex: 1; }
|
||||
|
||||
.mark-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
|
||||
.mark-grid .check-row { padding: 6px 6px; gap: 6px; font-size: 12px; }
|
||||
.mark-grid .check-row .name { white-space: nowrap; }
|
||||
|
||||
/* Tri-state axis */
|
||||
.axis { margin-bottom: 10px; }
|
||||
.axis:last-child { margin-bottom: 0; }
|
||||
.axis-h {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;
|
||||
color: var(--cyan);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.axis-h svg { width: 12px; height: 12px; }
|
||||
.axis-options {
|
||||
display: flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden;
|
||||
}
|
||||
.axis-options .opt {
|
||||
flex: 1; text-align: center; padding: 6px 8px;
|
||||
font-size: 11px; font-family: ui-monospace, monospace;
|
||||
color: var(--fg-muted);
|
||||
border-right: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.axis-options .opt:last-child { border-right: 0; }
|
||||
.axis-options .opt:hover { background: var(--glass); color: var(--fg); }
|
||||
.axis-options .opt.on {
|
||||
background: rgba(34,211,238,0.20);
|
||||
color: var(--cyan);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 10px 16px;
|
||||
background: var(--bg-1);
|
||||
border-top: 1px solid var(--border);
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 11px;
|
||||
color: var(--fg-muted);
|
||||
}
|
||||
.footer .reset-all { color: var(--cyan); cursor: pointer; }
|
||||
.footer .reset-all:hover { text-decoration: underline; }
|
||||
.footer .reset-all.dim { opacity: 0.4; pointer-events: none; }
|
||||
|
||||
/* Scenarios layout */
|
||||
.scenarios { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; margin-top: 24px; }
|
||||
.scenario { display: flex; flex-direction: column; align-items: center; gap: 0; }
|
||||
.scenario-label { color: var(--fg-muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 12px; }
|
||||
|
||||
.pick {
|
||||
margin: 32px 0 12px; padding: 14px 18px;
|
||||
border-radius: 12px; background: rgba(34,211,238,0.07);
|
||||
border: 1px solid rgba(34,211,238,0.25);
|
||||
color: var(--cyan); font-size: 13px;
|
||||
}
|
||||
.pick strong { color: var(--cyan); }
|
||||
.pick em { color: var(--fg); font-style: normal; }
|
||||
.pick ul { margin: 8px 0 0; padding-left: 18px; line-height: 1.7; color: var(--fg-dim); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="page-header">
|
||||
<h1>Merged Filter · Marks + Watch State + Has…</h1>
|
||||
<p>One <strong>Filter</strong> button replaces today's separate <strong>State</strong> and <strong>Filter</strong> popovers. Three sections so the labels stay honest: Tags/Collection are presence facets, not activities. Cyan accent throughout (theme primary), no coral.</p>
|
||||
</div>
|
||||
|
||||
<div class="scenarios">
|
||||
|
||||
<!-- Scenario A: empty -->
|
||||
<div class="scenario">
|
||||
<div class="scenario-label">Nothing active</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span class="lbl">📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill"><span class="lbl">🔖 Filter</span><span class="cv">▾</span></span>
|
||||
<span class="pill disabled"><span class="lbl">🏷 Mark As</span><span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="section">
|
||||
<div class="mark-grid">
|
||||
<div class="check-row"><span class="cb"> </span><span class="ico cyan">◆</span><span class="name">VIP</span></div>
|
||||
<div class="check-row"><span class="cb"> </span><span class="ico amber">★</span><span class="name">Favorite</span></div>
|
||||
<div class="check-row"><span class="cb"> </span><span class="ico violet">▣</span><span class="name">Owned</span></div>
|
||||
<div class="check-row"><span class="cb"> </span><span class="ico muted">⊖</span><span class="name">Unmarked</span></div>
|
||||
</div> </div>
|
||||
|
||||
<div class="section">
|
||||
<div class="axis">
|
||||
<div class="axis-h">👁 Watched</div>
|
||||
<div class="axis-options">
|
||||
<div class="opt on">ALL</div>
|
||||
<div class="opt">Watched</div>
|
||||
<div class="opt">Unwatched</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="axis">
|
||||
<div class="axis-h">★ Rated</div>
|
||||
<div class="axis-options">
|
||||
<div class="opt on">ALL</div>
|
||||
<div class="opt">Rated</div>
|
||||
<div class="opt">No Rating</div>
|
||||
</div>
|
||||
</div> </div>
|
||||
|
||||
<div class="section">
|
||||
<div class="axis">
|
||||
<div class="axis-h">📂 Collection</div>
|
||||
<div class="axis-options">
|
||||
<div class="opt on">ALL</div>
|
||||
<div class="opt">Has</div>
|
||||
<div class="opt">Missing</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="axis">
|
||||
<div class="axis-h">🏷 Tags</div>
|
||||
<div class="axis-options">
|
||||
<div class="opt on">ALL</div>
|
||||
<div class="opt">Has</div>
|
||||
<div class="opt">Missing</div>
|
||||
</div>
|
||||
</div> </div>
|
||||
|
||||
<div class="footer">
|
||||
<span>no filters set</span>
|
||||
<span class="reset-all dim">Reset All</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scenario B: cross-cutting active -->
|
||||
<div class="scenario">
|
||||
<div class="scenario-label">VIP + Owned + Watched + No Tags</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">ALL</span>
|
||||
<span class="pill"><span class="lbl">📁 Browse</span><span class="cv">▾</span></span>
|
||||
<span class="pill active-cyan"><span class="lbl">🔖 Filter <span class="badge">4</span></span><span class="cv">▾</span></span>
|
||||
<span class="pill disabled"><span class="lbl">🏷 Mark As</span><span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="section">
|
||||
<div class="mark-grid">
|
||||
<div class="check-row on cyan"><span class="cb on cyan">✓</span><span class="ico cyan">◆</span><span class="name">VIP</span></div>
|
||||
<div class="check-row"><span class="cb"> </span><span class="ico amber">★</span><span class="name">Favorite</span></div>
|
||||
<div class="check-row on violet"><span class="cb on violet">✓</span><span class="ico violet">▣</span><span class="name">Owned</span></div>
|
||||
<div class="check-row"><span class="cb"> </span><span class="ico muted">⊖</span><span class="name">Unmarked</span></div>
|
||||
</div> </div>
|
||||
|
||||
<div class="section">
|
||||
<div class="axis">
|
||||
<div class="axis-h">👁 Watched</div>
|
||||
<div class="axis-options">
|
||||
<div class="opt">ALL</div>
|
||||
<div class="opt on">Watched</div>
|
||||
<div class="opt">Unwatched</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="axis">
|
||||
<div class="axis-h">★ Rated</div>
|
||||
<div class="axis-options">
|
||||
<div class="opt on">ALL</div>
|
||||
<div class="opt">Rated</div>
|
||||
<div class="opt">No Rating</div>
|
||||
</div>
|
||||
</div> </div>
|
||||
|
||||
<div class="section">
|
||||
<div class="axis">
|
||||
<div class="axis-h">📂 Collection</div>
|
||||
<div class="axis-options">
|
||||
<div class="opt on">ALL</div>
|
||||
<div class="opt">Has</div>
|
||||
<div class="opt">Missing</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="axis">
|
||||
<div class="axis-h">🏷 Tags</div>
|
||||
<div class="axis-options">
|
||||
<div class="opt">ALL</div>
|
||||
<div class="opt">Has</div>
|
||||
<div class="opt on">Missing</div>
|
||||
</div>
|
||||
</div> </div>
|
||||
|
||||
<div class="footer">
|
||||
<span>4 filters · 2 marks · 1 watch · 1 has</span>
|
||||
<span class="reset-all">Reset All</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="pick">
|
||||
<strong>Notes</strong>
|
||||
<ul>
|
||||
<li><em>Three sections</em>: Marks (multi-select OR), Watch State (tri-state per axis), Has… (tri-state presence per axis). Each section AND'd against the others.</li>
|
||||
<li><em>Single cyan accent</em> for the popover chrome — no coral. Per-row colors stay (cyan VIP, amber Favorite, violet Owned) so individual marks remain visually distinct.</li>
|
||||
<li><em>One badge</em> on the Filter pill totals everything; footer breaks it down.</li>
|
||||
<li><em>Per-section reset</em> link in each header; <em>Reset All</em> in the footer for the nuclear option.</li>
|
||||
<li>You can now express composite queries directly: "VIP + Watched", "Favorite + No Tags", etc. — what triggered this.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,712 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex — Filter Bar (Option 2 refined)</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0e0a14;
|
||||
--bg-1: #18121f;
|
||||
--fg: #ece6f5;
|
||||
--fg-dim: #b9b1c6;
|
||||
--fg-muted: #7d7388;
|
||||
--cyan: #4dd9e6;
|
||||
--violet: #b87cf6;
|
||||
--coral: #ff7a8a;
|
||||
--mint: #79e6b2;
|
||||
--amber: #fbbf24;
|
||||
--glass-border: #2a2434;
|
||||
--glass-border-strong: #3d3548;
|
||||
--glass: rgba(40, 32, 56, 0.5);
|
||||
--glass-strong: rgba(56, 46, 76, 0.7);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html, body {
|
||||
margin: 0;
|
||||
background: radial-gradient(1200px 600px at 70% -10%, rgba(184,124,246,0.08), transparent),
|
||||
radial-gradient(800px 500px at 10% 110%, rgba(77,217,230,0.06), transparent),
|
||||
var(--bg-0);
|
||||
color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
.page { max-width: 1600px; margin: 0 auto; padding: 32px 24px 64px; }
|
||||
h1 { font-size: 28px; margin: 0 0 8px; font-weight: 600; letter-spacing: -0.01em; }
|
||||
.lede { color: var(--fg-dim); margin: 0 0 32px; max-width: 820px; line-height: 1.5; }
|
||||
h2 {
|
||||
margin: 32px 0 4px;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--cyan);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
.desc { color: var(--fg-dim); font-size: 13px; margin: 0 0 12px; max-width: 820px; line-height: 1.5; }
|
||||
|
||||
.stage {
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
margin-bottom: 24px;
|
||||
position: relative;
|
||||
}
|
||||
.stage-label {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--fg-muted);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
/* bar */
|
||||
.bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
|
||||
.chip {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 6px 12px; border-radius: 9999px;
|
||||
border: 1px solid var(--glass-border); background: var(--glass);
|
||||
color: var(--fg-dim); font-size: 14px; cursor: pointer;
|
||||
transition: all 150ms ease;
|
||||
font-family: inherit;
|
||||
}
|
||||
.chip:hover { color: var(--fg); border-color: var(--glass-border-strong); }
|
||||
.chip.active { background: rgba(77,217,230,0.15); border-color: rgba(77,217,230,0.4); color: var(--cyan); }
|
||||
.chip .caret { font-size: 10px; opacity: 0.7; }
|
||||
.chip .badge {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
min-width: 16px; height: 16px; padding: 0 5px;
|
||||
border-radius: 9999px; background: var(--cyan); color: #000;
|
||||
font-size: 10px; font-weight: 700; margin-left: 2px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
.right-group { display: flex; align-items: center; gap: 8px; margin-left: auto; }
|
||||
.search { position: relative; width: 224px; }
|
||||
.search input {
|
||||
width: 100%; background: var(--glass); border: 1px solid var(--glass-border);
|
||||
border-radius: 8px; padding: 6px 28px 6px 30px; color: var(--fg);
|
||||
font-size: 13px; outline: none;
|
||||
}
|
||||
.search input::placeholder { color: var(--fg-muted); }
|
||||
.search .ico { position: absolute; left: 9px; top: 50%; transform: translateY(-50%); color: var(--fg-muted); width: 14px; height: 14px; }
|
||||
.sort {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 6px 12px; border-radius: 9999px;
|
||||
border: 1px solid var(--glass-border); color: var(--fg-dim);
|
||||
font-size: 13px; cursor: pointer; background: var(--glass);
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* active criteria strip */
|
||||
.crit-strip {
|
||||
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
|
||||
padding: 10px 12px; border: 1px dashed var(--glass-border-strong);
|
||||
border-radius: 12px; margin-bottom: 12px;
|
||||
background: rgba(77,217,230,0.04);
|
||||
min-height: 48px;
|
||||
}
|
||||
.crit-strip .label {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em;
|
||||
color: var(--fg-muted); margin-right: 4px;
|
||||
}
|
||||
.crit-empty { color: var(--fg-muted); font-size: 12px; font-style: italic; }
|
||||
.crit-pill {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 4px 6px 4px 10px; border-radius: 9999px;
|
||||
background: rgba(77,217,230,0.12); border: 1px solid rgba(77,217,230,0.4);
|
||||
color: var(--cyan); font-size: 12px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
.crit-pill .x {
|
||||
width: 16px; height: 16px; display: inline-flex; align-items: center; justify-content: center;
|
||||
border-radius: 9999px; background: rgba(0,0,0,0.4); color: var(--cyan);
|
||||
font-size: 10px; cursor: pointer;
|
||||
}
|
||||
.crit-pill .x:hover { background: rgba(255,122,138,0.3); color: #fff; }
|
||||
.crit-pill .kind { color: var(--fg-muted); margin-right: 2px; }
|
||||
.crit-pill .conn { color: var(--fg-muted); font-size: 10px; padding: 0 2px; }
|
||||
.clear-all {
|
||||
margin-left: auto;
|
||||
color: var(--fg-muted); font-size: 11px;
|
||||
background: none; border: none; cursor: pointer;
|
||||
text-decoration: underline; padding: 0;
|
||||
font-family: inherit;
|
||||
}
|
||||
.clear-all:hover { color: var(--coral); }
|
||||
|
||||
/* popover */
|
||||
.popup-wrap { position: relative; display: inline-block; }
|
||||
.popup {
|
||||
position: absolute; top: calc(100% + 6px); left: 0;
|
||||
background: var(--bg-0);
|
||||
border: 1px solid var(--glass-border-strong);
|
||||
border-radius: 14px;
|
||||
padding: 12px;
|
||||
box-shadow: 0 24px 60px rgba(0,0,0,0.7);
|
||||
width: 540px;
|
||||
z-index: 20;
|
||||
display: none;
|
||||
}
|
||||
.popup.open { display: block; }
|
||||
|
||||
.tabs {
|
||||
display: flex; gap: 2px; border-bottom: 1px solid var(--glass-border);
|
||||
padding-bottom: 8px; margin-bottom: 10px; flex-wrap: wrap;
|
||||
}
|
||||
.tab {
|
||||
padding: 5px 10px; border-radius: 6px;
|
||||
font-size: 12px; color: var(--fg-muted); cursor: pointer;
|
||||
user-select: none;
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
background: transparent; border: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
.tab:hover { background: var(--glass); color: var(--fg-dim); }
|
||||
.tab.active { background: var(--glass-strong); color: var(--cyan); }
|
||||
.tab .badge {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
min-width: 14px; height: 14px; padding: 0 4px;
|
||||
border-radius: 9999px; background: var(--cyan); color: #000;
|
||||
font-size: 9px; font-weight: 700;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
|
||||
.popup-controls {
|
||||
display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
|
||||
}
|
||||
.popup-search { position: relative; flex: 1; }
|
||||
.popup-search input {
|
||||
width: 100%; background: var(--glass); border: 1px solid var(--glass-border);
|
||||
border-radius: 8px; padding: 6px 10px 6px 28px; color: var(--fg);
|
||||
font-size: 13px; outline: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
.popup-search .ico { position: absolute; left: 8px; top: 50%; transform: translateY(-50%); color: var(--fg-muted); width: 13px; height: 13px; }
|
||||
|
||||
.mode-toggle {
|
||||
display: inline-flex;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
font-size: 11px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
user-select: none;
|
||||
}
|
||||
.mode-toggle .seg {
|
||||
padding: 5px 10px; cursor: pointer;
|
||||
color: var(--fg-muted); background: transparent;
|
||||
border: none; font-family: inherit; font-size: 11px;
|
||||
}
|
||||
.mode-toggle .seg.on { background: var(--cyan); color: #000; font-weight: 700; }
|
||||
|
||||
.popup-list { max-height: 240px; overflow-y: auto; padding: 2px; }
|
||||
.popup-list .row {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 6px 8px; border-radius: 6px; cursor: pointer;
|
||||
font-size: 13px; color: var(--fg-dim);
|
||||
user-select: none;
|
||||
}
|
||||
.popup-list .row:hover { background: var(--glass); color: var(--fg); }
|
||||
.popup-list .row.checked { color: var(--cyan); }
|
||||
.popup-list .row .name { flex: 1; }
|
||||
.popup-list .row .count {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
color: var(--fg-muted); font-size: 11px;
|
||||
}
|
||||
.check {
|
||||
width: 14px; height: 14px; border-radius: 3px;
|
||||
border: 1.5px solid var(--glass-border-strong);
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-size: 9px; color: var(--cyan);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.check.on { background: rgba(77,217,230,0.18); border-color: var(--cyan); }
|
||||
|
||||
.popup-footer {
|
||||
margin-top: 8px; padding-top: 8px;
|
||||
border-top: 1px solid var(--glass-border);
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
font-size: 11px; color: var(--fg-muted);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
.popup-footer button {
|
||||
background: none; border: none; color: var(--coral);
|
||||
font-size: 11px; cursor: pointer; font-family: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* missing popover */
|
||||
.missing-popup {
|
||||
position: absolute; top: calc(100% + 6px); left: 0;
|
||||
background: var(--bg-0);
|
||||
border: 1px solid var(--glass-border-strong);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
box-shadow: 0 16px 40px rgba(0,0,0,0.55);
|
||||
width: 220px;
|
||||
z-index: 20;
|
||||
display: none;
|
||||
}
|
||||
.missing-popup.open { display: block; }
|
||||
.check-row {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 6px 6px; border-radius: 6px; cursor: pointer;
|
||||
color: var(--fg-dim); font-size: 13px;
|
||||
user-select: none;
|
||||
}
|
||||
.check-row:hover { background: var(--glass); color: var(--fg); }
|
||||
.check-row .count {
|
||||
margin-left: auto;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
color: var(--fg-muted); font-size: 11px;
|
||||
}
|
||||
|
||||
/* letter bar */
|
||||
.letters { display: flex; gap: 4px; margin-top: 12px; }
|
||||
.letter {
|
||||
flex: 1; text-align: center;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 8px; padding: 6px 0 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 13px; color: var(--fg-muted); background: var(--glass);
|
||||
}
|
||||
.letter.on { background: var(--cyan); color: #000; border-color: transparent; font-weight: 600; }
|
||||
.letter.dim { opacity: 0.35; border-color: transparent; }
|
||||
.letter .num { display: block; font-size: 9px; margin-top: 2px; }
|
||||
|
||||
.note {
|
||||
margin-top: 14px; font-size: 11px; color: var(--fg-muted);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
code {
|
||||
background: var(--glass); padding: 1px 5px; border-radius: 4px;
|
||||
font-size: 12px; color: var(--cyan);
|
||||
}
|
||||
|
||||
/* SQL preview */
|
||||
.sql-preview {
|
||||
margin-top: 14px;
|
||||
padding: 10px 12px;
|
||||
background: rgba(0,0,0,0.4);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 8px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 11px;
|
||||
color: var(--fg-dim);
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
.sql-preview .kw { color: var(--violet); }
|
||||
.sql-preview .lit { color: var(--mint); }
|
||||
.sql-preview .col { color: var(--cyan); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<h1>Filter Bar — Option 2 refined</h1>
|
||||
<p class="lede">
|
||||
Single <code>Filter by ▾</code> popover with tabs. Each tab supports multi-select and a per-type AND/OR toggle.
|
||||
Active criteria render as removable pills above the letter bar. Single-entity pages (e.g. <code>/actress/aika</code>) stay
|
||||
as-is — selecting an entity here adds an extra criterion on top of whatever you're viewing.
|
||||
</p>
|
||||
|
||||
<div class="stage">
|
||||
<div class="stage-label">Live mockup — click anything</div>
|
||||
|
||||
<div class="bar">
|
||||
<button class="chip active">All</button>
|
||||
|
||||
<div class="popup-wrap" id="filterWrap">
|
||||
<button class="chip" id="filterChip" onclick="togglePopup('filterPopup')">
|
||||
⛓ Filter by <span id="filterBadge" class="badge" style="display:none">0</span>
|
||||
<span class="caret">▾</span>
|
||||
</button>
|
||||
<div class="popup" id="filterPopup">
|
||||
<div class="tabs" id="tabs"></div>
|
||||
|
||||
<div class="popup-controls">
|
||||
<div class="popup-search">
|
||||
<span class="ico">🔍</span>
|
||||
<input id="popupSearch" placeholder="Filter list…" oninput="renderList()" />
|
||||
</div>
|
||||
<div class="mode-toggle" id="modeToggle">
|
||||
<button class="seg on" onclick="setMode('OR')">OR</button>
|
||||
<button class="seg" onclick="setMode('AND')">AND</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="popup-list" id="popupList"></div>
|
||||
|
||||
<div class="popup-footer">
|
||||
<span id="footerHint">tap row to toggle · OR = match any · AND = match all</span>
|
||||
<button onclick="clearTab()">Clear this tab</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="popup-wrap" id="missingWrap">
|
||||
<button class="chip" id="missingChip" onclick="togglePopup('missingPopup')">
|
||||
⊘ Missing <span id="missingBadge" class="badge" style="display:none">0</span>
|
||||
<span class="caret">▾</span>
|
||||
</button>
|
||||
<div class="missing-popup" id="missingPopup">
|
||||
<div class="check-row" onclick="toggleMissing('unwatched')">
|
||||
<span class="check" id="m-unwatched">·</span>Unwatched <span class="count">12</span>
|
||||
</div>
|
||||
<div class="check-row" onclick="toggleMissing('uncollected')">
|
||||
<span class="check" id="m-uncollected">·</span>No Collection <span class="count">8</span>
|
||||
</div>
|
||||
<div class="check-row" onclick="toggleMissing('untagged')">
|
||||
<span class="check" id="m-untagged">·</span>No Tags <span class="count">17</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-group">
|
||||
<div class="search">
|
||||
<span class="ico">🔍</span>
|
||||
<input placeholder="Search Code, Title, Notes…" />
|
||||
</div>
|
||||
<button class="sort">⏱ Newest First ▾</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Active criteria strip -->
|
||||
<div class="crit-strip" id="critStrip">
|
||||
<span class="label">Active</span>
|
||||
<span id="critList"></span>
|
||||
<span id="critEmpty" class="crit-empty">no filters — showing all covers</span>
|
||||
<button id="clearAll" class="clear-all" onclick="clearAll()" style="display:none">clear all</button>
|
||||
</div>
|
||||
|
||||
<!-- Letter bar (untouched) -->
|
||||
<div class="letters">
|
||||
<div class="letter on">ALL<span class="num">26</span></div>
|
||||
<div class="letter">A<span class="num">4</span></div>
|
||||
<div class="letter">B<span class="num">3</span></div>
|
||||
<div class="letter">C<span class="num">1</span></div>
|
||||
<div class="letter dim">D<span class="num">·</span></div>
|
||||
<div class="letter">E<span class="num">1</span></div>
|
||||
<div class="letter">I<span class="num">3</span></div>
|
||||
<div class="letter">K<span class="num">3</span></div>
|
||||
<div class="letter">L<span class="num">1</span></div>
|
||||
<div class="letter">M<span class="num">6</span></div>
|
||||
<div class="letter">N<span class="num">1</span></div>
|
||||
<div class="letter">P<span class="num">1</span></div>
|
||||
<div class="letter">R<span class="num">2</span></div>
|
||||
<div class="letter">S<span class="num">3</span></div>
|
||||
<div class="letter">U<span class="num">1</span></div>
|
||||
<div class="letter">Y<span class="num">1</span></div>
|
||||
<div class="letter dim">Z<span class="num">·</span></div>
|
||||
</div>
|
||||
|
||||
<!-- SQL preview -->
|
||||
<div class="sql-preview" id="sqlPreview"></div>
|
||||
</div>
|
||||
|
||||
<p class="note">URL example with current selection: <code id="urlExample">/?</code></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Mock catalog data
|
||||
const data = {
|
||||
actresses: { icon: "👤", items: [
|
||||
{ id: 1, name: "Aoi Kururugi", count: 3 },
|
||||
{ id: 2, name: "Aika", count: 0 },
|
||||
{ id: 3, name: "Eru Sato", count: 1 },
|
||||
{ id: 4, name: "Ichigo Aoi", count: 2 },
|
||||
{ id: 5, name: "Ichika Matsumoto", count: 5 },
|
||||
{ id: 6, name: "Kana Kusakabe", count: 1 },
|
||||
{ id: 7, name: "Lala Kudo", count: 2 },
|
||||
{ id: 8, name: "Mitsuki Nagisa", count: 1 },
|
||||
{ id: 9, name: "Reika Aiba", count: 2 },
|
||||
{ id: 10, name: "Shuri Atomi", count: 4 },
|
||||
]},
|
||||
studios: { icon: "🏢", items: [
|
||||
{ id: 1, name: "SOD", count: 12 },
|
||||
{ id: 2, name: "S1", count: 8 },
|
||||
{ id: 3, name: "Moodyz", count: 6 },
|
||||
{ id: 4, name: "Madonna", count: 3 },
|
||||
{ id: 5, name: "IdeaPocket", count: 4 },
|
||||
]},
|
||||
series: { icon: "🎬", items: [
|
||||
{ id: 1, name: "First Time", count: 4 },
|
||||
{ id: 2, name: "Schoolgirl Diaries", count: 6 },
|
||||
{ id: 3, name: "Office Lady", count: 3 },
|
||||
]},
|
||||
genres: { icon: "#", items: [
|
||||
{ id: 1, name: "Schoolgirl", count: 12 },
|
||||
{ id: 2, name: "Drama", count: 5 },
|
||||
{ id: 3, name: "Comedy", count: 2 },
|
||||
{ id: 4, name: "Romance", count: 7 },
|
||||
{ id: 5, name: "Solo", count: 3 },
|
||||
]},
|
||||
collections: { icon: "📁", items: [
|
||||
{ id: 1, name: "Watchlist", count: 9 },
|
||||
{ id: 2, name: "Best Of 2025", count: 14 },
|
||||
]},
|
||||
tags: { icon: "🏷", items: [
|
||||
{ id: 1, name: "favorite", count: 11 },
|
||||
{ id: 2, name: "vip", count: 3 },
|
||||
{ id: 3, name: "rewatch", count: 5 },
|
||||
{ id: 4, name: "saved", count: 8 },
|
||||
]},
|
||||
};
|
||||
|
||||
// State
|
||||
const state = {
|
||||
selected: { actresses: new Set(), studios: new Set(), series: new Set(), genres: new Set(), collections: new Set(), tags: new Set() },
|
||||
mode: { actresses: "OR", studios: "OR", series: "OR", genres: "OR", collections: "OR", tags: "OR" },
|
||||
activeTab: "actresses",
|
||||
missing: new Set(),
|
||||
};
|
||||
|
||||
function renderTabs() {
|
||||
const el = document.getElementById("tabs");
|
||||
el.innerHTML = "";
|
||||
for (const key of Object.keys(data)) {
|
||||
const t = data[key];
|
||||
const selectedCount = state.selected[key].size;
|
||||
const cap = key.charAt(0).toUpperCase() + key.slice(1);
|
||||
const btn = document.createElement("button");
|
||||
btn.className = "tab" + (state.activeTab === key ? " active" : "");
|
||||
btn.onclick = () => { state.activeTab = key; document.getElementById("popupSearch").value = ""; renderTabs(); renderList(); renderModeToggle(); };
|
||||
btn.innerHTML = `${t.icon} ${cap}` + (selectedCount > 0 ? ` <span class="badge">${selectedCount}</span>` : "");
|
||||
el.appendChild(btn);
|
||||
}
|
||||
}
|
||||
|
||||
function renderList() {
|
||||
const el = document.getElementById("popupList");
|
||||
const q = (document.getElementById("popupSearch")?.value ?? "").toLowerCase();
|
||||
const tab = data[state.activeTab];
|
||||
const sel = state.selected[state.activeTab];
|
||||
el.innerHTML = "";
|
||||
for (const it of tab.items) {
|
||||
if (q && !it.name.toLowerCase().includes(q)) continue;
|
||||
const row = document.createElement("div");
|
||||
const checked = sel.has(it.id);
|
||||
row.className = "row" + (checked ? " checked" : "");
|
||||
row.onclick = (ev) => {
|
||||
ev.stopPropagation();
|
||||
if (checked) sel.delete(it.id); else sel.add(it.id);
|
||||
renderAll();
|
||||
};
|
||||
row.innerHTML = `
|
||||
<span class="check ${checked ? "on" : ""}">${checked ? "✓" : ""}</span>
|
||||
<span class="name">${it.name}</span>
|
||||
<span class="count">${it.count}</span>
|
||||
`;
|
||||
el.appendChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
function renderModeToggle() {
|
||||
const segs = document.querySelectorAll("#modeToggle .seg");
|
||||
const cur = state.mode[state.activeTab];
|
||||
segs[0].classList.toggle("on", cur === "OR");
|
||||
segs[1].classList.toggle("on", cur === "AND");
|
||||
}
|
||||
|
||||
function setMode(mode) {
|
||||
state.mode[state.activeTab] = mode;
|
||||
renderModeToggle();
|
||||
renderCriteria();
|
||||
renderSql();
|
||||
}
|
||||
|
||||
function clearTab() {
|
||||
state.selected[state.activeTab].clear();
|
||||
renderAll();
|
||||
}
|
||||
|
||||
function clearAll() {
|
||||
for (const k of Object.keys(state.selected)) state.selected[k].clear();
|
||||
state.missing.clear();
|
||||
renderAll();
|
||||
}
|
||||
|
||||
function toggleMissing(key) {
|
||||
if (state.missing.has(key)) state.missing.delete(key); else state.missing.add(key);
|
||||
renderAll();
|
||||
}
|
||||
|
||||
function renderMissing() {
|
||||
for (const k of ["unwatched", "uncollected", "untagged"]) {
|
||||
const el = document.getElementById("m-" + k);
|
||||
const on = state.missing.has(k);
|
||||
el.classList.toggle("on", on);
|
||||
el.textContent = on ? "✓" : "·";
|
||||
}
|
||||
const total = state.missing.size;
|
||||
const badge = document.getElementById("missingBadge");
|
||||
badge.textContent = total;
|
||||
badge.style.display = total > 0 ? "inline-flex" : "none";
|
||||
document.getElementById("missingChip").classList.toggle("active", total > 0);
|
||||
}
|
||||
|
||||
function renderFilterChip() {
|
||||
let total = 0;
|
||||
for (const k of Object.keys(state.selected)) total += state.selected[k].size;
|
||||
const badge = document.getElementById("filterBadge");
|
||||
badge.textContent = total;
|
||||
badge.style.display = total > 0 ? "inline-flex" : "none";
|
||||
document.getElementById("filterChip").classList.toggle("active", total > 0);
|
||||
}
|
||||
|
||||
function renderCriteria() {
|
||||
const list = document.getElementById("critList");
|
||||
const empty = document.getElementById("critEmpty");
|
||||
const clearAllBtn = document.getElementById("clearAll");
|
||||
list.innerHTML = "";
|
||||
let total = 0;
|
||||
|
||||
for (const key of Object.keys(state.selected)) {
|
||||
const sel = state.selected[key];
|
||||
if (sel.size === 0) continue;
|
||||
const items = data[key].items.filter((i) => sel.has(i.id));
|
||||
const mode = state.mode[key];
|
||||
const conn = mode === "AND" ? "AND" : "OR";
|
||||
|
||||
items.forEach((it, i) => {
|
||||
const pill = document.createElement("span");
|
||||
pill.className = "crit-pill";
|
||||
pill.innerHTML = `
|
||||
<span class="kind">${data[key].icon}</span>
|
||||
${it.name}
|
||||
<span class="x" title="Remove">✕</span>
|
||||
`;
|
||||
pill.querySelector(".x").onclick = () => { sel.delete(it.id); renderAll(); };
|
||||
list.appendChild(pill);
|
||||
if (i < items.length - 1) {
|
||||
const connector = document.createElement("span");
|
||||
connector.className = "conn";
|
||||
connector.textContent = conn;
|
||||
list.appendChild(connector);
|
||||
}
|
||||
total++;
|
||||
});
|
||||
|
||||
if (key !== Object.keys(state.selected).filter((k) => state.selected[k].size > 0).slice(-1)[0]) {
|
||||
const cross = document.createElement("span");
|
||||
cross.className = "conn";
|
||||
cross.style.color = "var(--violet)";
|
||||
cross.textContent = "AND";
|
||||
list.appendChild(cross);
|
||||
}
|
||||
}
|
||||
|
||||
for (const m of state.missing) {
|
||||
if (total > 0 || state.missing.size > 1) {
|
||||
// already handled by section connector if applicable
|
||||
}
|
||||
const pill = document.createElement("span");
|
||||
pill.className = "crit-pill";
|
||||
pill.style.background = "rgba(255,122,138,0.12)";
|
||||
pill.style.borderColor = "rgba(255,122,138,0.4)";
|
||||
pill.style.color = "var(--coral)";
|
||||
const labels = { unwatched: "Unwatched", uncollected: "No Collection", untagged: "No Tags" };
|
||||
pill.innerHTML = `
|
||||
<span class="kind">⊘</span>
|
||||
${labels[m]}
|
||||
<span class="x" title="Remove">✕</span>
|
||||
`;
|
||||
pill.querySelector(".x").onclick = () => { state.missing.delete(m); renderAll(); };
|
||||
list.appendChild(pill);
|
||||
total++;
|
||||
}
|
||||
|
||||
empty.style.display = total === 0 ? "inline" : "none";
|
||||
clearAllBtn.style.display = total === 0 ? "none" : "inline-block";
|
||||
}
|
||||
|
||||
function renderSql() {
|
||||
const parts = [];
|
||||
for (const key of Object.keys(state.selected)) {
|
||||
const sel = state.selected[key];
|
||||
if (sel.size === 0) continue;
|
||||
const ids = [...sel];
|
||||
const items = data[key].items.filter((i) => sel.has(i.id));
|
||||
const names = items.map((i) => `'${i.name}'`).join(", ");
|
||||
const mode = state.mode[key];
|
||||
const tableMap = {
|
||||
actresses: "image_actresses ia",
|
||||
studios: "i.studio_id",
|
||||
series: "i.series_id",
|
||||
genres: "image_genres ig",
|
||||
collections: "collection_images ci",
|
||||
tags: "image_tags it",
|
||||
};
|
||||
if (key === "studios" || key === "series") {
|
||||
parts.push(`<span class="kw">AND</span> ${tableMap[key]} <span class="kw">IN</span> (<span class="lit">${names}</span>)`);
|
||||
} else if (mode === "OR") {
|
||||
parts.push(`<span class="kw">AND</span> <span class="col">${key}</span> <span class="kw">IN</span> (<span class="lit">${names}</span>)`);
|
||||
} else {
|
||||
parts.push(`<span class="kw">AND</span> covers w/ <span class="kw">ALL</span> of (<span class="lit">${names}</span>)`);
|
||||
}
|
||||
}
|
||||
for (const m of state.missing) {
|
||||
parts.push(`<span class="kw">AND</span> <span class="col">${m}</span>`);
|
||||
}
|
||||
const sql = parts.length === 0
|
||||
? '<span class="kw">SELECT</span> * <span class="kw">FROM</span> <span class="col">images</span> <span class="kw">WHERE</span> deleted_at <span class="kw">IS NULL</span>'
|
||||
: '<span class="kw">SELECT</span> * <span class="kw">FROM</span> <span class="col">images</span> <span class="kw">WHERE</span> deleted_at <span class="kw">IS NULL</span>\n ' + parts.join("\n ");
|
||||
document.getElementById("sqlPreview").innerHTML = sql;
|
||||
}
|
||||
|
||||
function renderUrl() {
|
||||
const sp = new URLSearchParams();
|
||||
for (const key of Object.keys(state.selected)) {
|
||||
const sel = state.selected[key];
|
||||
if (sel.size === 0) continue;
|
||||
sp.set(key, [...sel].join(","));
|
||||
if (state.mode[key] === "AND") sp.set(`${key}_mode`, "and");
|
||||
}
|
||||
for (const m of state.missing) sp.set(m, "1");
|
||||
const s = sp.toString();
|
||||
document.getElementById("urlExample").textContent = "/" + (s ? "?" + s : "");
|
||||
}
|
||||
|
||||
function renderAll() {
|
||||
renderTabs();
|
||||
renderList();
|
||||
renderModeToggle();
|
||||
renderCriteria();
|
||||
renderFilterChip();
|
||||
renderMissing();
|
||||
renderSql();
|
||||
renderUrl();
|
||||
}
|
||||
|
||||
function togglePopup(id) {
|
||||
const p = document.getElementById(id);
|
||||
const opening = !p.classList.contains("open");
|
||||
document.querySelectorAll(".popup, .missing-popup").forEach((x) => x.classList.remove("open"));
|
||||
if (opening) p.classList.add("open");
|
||||
}
|
||||
|
||||
// Any click inside a popover stays inside — prevents the document listener
|
||||
// from incorrectly closing it when an inner click rebuilds the DOM.
|
||||
document.querySelectorAll(".popup, .missing-popup").forEach((p) => {
|
||||
p.addEventListener("click", (e) => e.stopPropagation());
|
||||
});
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!e.target.closest(".popup-wrap")) {
|
||||
document.querySelectorAll(".popup, .missing-popup").forEach((x) => x.classList.remove("open"));
|
||||
}
|
||||
});
|
||||
|
||||
// Pre-seed a demo selection so the user sees the chips on load.
|
||||
state.selected.actresses.add(2); // Aika
|
||||
state.selected.studios.add(1); // SOD
|
||||
state.selected.genres.add(1);
|
||||
state.selected.genres.add(2); // Schoolgirl OR Drama
|
||||
state.selected.tags.add(1); // favorite
|
||||
|
||||
renderAll();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,267 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Library Page — Spacing Pass (Layout Untouched)</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-bg-0: oklch(0.13 0.025 280);
|
||||
--color-bg-1: oklch(0.17 0.04 285);
|
||||
--color-bg-2: oklch(0.22 0.05 290);
|
||||
--color-fg: oklch(0.97 0.01 280);
|
||||
--color-fg-dim: oklch(0.72 0.025 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: color-mix(in oklch, white 6%, transparent);
|
||||
--color-glass-strong: color-mix(in oklch, white 10%, transparent);
|
||||
--color-glass-border: color-mix(in oklch, white 14%, transparent);
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-violet: oklch(0.72 0.22 305);
|
||||
--color-mint: oklch(0.80 0.16 155);
|
||||
|
||||
--spacing-card: 15px;
|
||||
--spacing-card-gap: 9px;
|
||||
--spacing-section: 15px;
|
||||
--spacing-chip: 7px;
|
||||
--spacing-label: 7px;
|
||||
}
|
||||
body {
|
||||
background: var(--color-bg-0);
|
||||
color: var(--color-fg);
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
.glass { background: var(--color-glass); border: 1px solid var(--color-glass-border); }
|
||||
.glass-strong { background: var(--color-glass-strong); border: 1px solid var(--color-glass-border-strong); }
|
||||
.label { font-family: ui-monospace, monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 8px; }
|
||||
.label.after { color: var(--color-cyan); }
|
||||
.why { font-size: 12px; color: var(--color-fg-muted); }
|
||||
|
||||
.pg {
|
||||
background: oklch(0.10 0.025 280);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pg-h {
|
||||
padding: 12px 16px;
|
||||
background: oklch(0.20 0.04 285);
|
||||
border-bottom: 1px solid var(--color-glass-border);
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 12px;
|
||||
color: var(--color-fg-dim);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.pg-h .tag { color: var(--color-cyan); font-weight: 600; }
|
||||
.pg-body { padding: 18px; }
|
||||
|
||||
/* Faithful page widget styles */
|
||||
.pill-sm {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 0 12px; height: 28px;
|
||||
border-radius: 999px; font-size: 12px;
|
||||
border: 1px solid var(--color-glass-border); color: var(--color-fg-dim);
|
||||
background: var(--color-glass);
|
||||
}
|
||||
.pill-sm.active { color: var(--color-cyan); border-color: oklch(0.82 0.16 200 / 0.4); background: oklch(0.82 0.16 200 / 0.15); }
|
||||
.letter-bar { display: flex; gap: 1px; flex-wrap: wrap; }
|
||||
.letter-bar .l {
|
||||
width: 26px; height: 26px; display: grid; place-items: center;
|
||||
font-family: ui-monospace, monospace; font-size: 11px;
|
||||
color: var(--color-fg-muted); border-radius: 4px;
|
||||
}
|
||||
.letter-bar .l.active { color: var(--color-cyan); background: oklch(0.82 0.16 200 / 0.15); }
|
||||
.thumb {
|
||||
aspect-ratio: 800/538; border-radius: 6px;
|
||||
background: linear-gradient(135deg, oklch(0.30 0.05 290), oklch(0.20 0.04 280));
|
||||
border: 1px solid var(--color-glass-border);
|
||||
position: relative;
|
||||
}
|
||||
.thumb .id { position: absolute; bottom: 4px; left: 6px; font-family: ui-monospace, monospace; font-size: 9px; color: var(--color-cyan); font-weight: 600; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="space-y-6 max-w-[1600px] mx-auto p-6">
|
||||
|
||||
<header class="space-y-1">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Library Page · Spacing Pass</h1>
|
||||
<p class="why max-w-[820px]">
|
||||
Same layout as <code class="text-[var(--color-cyan)]">app/page.tsx</code>: title + stats + UploadCard / FilterBar / LetterBar / Grid.
|
||||
Only the vertical rhythm and the empty-state padding change. No components moved, no columns added, no styles touched.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- ====================================================================
|
||||
Audit table
|
||||
==================================================================== -->
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<div class="label">Spacing changes only — 4 lines total</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-2 text-[12px] font-mono text-[var(--color-fg-dim)]">
|
||||
<div>1 · hero section bottom margin</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">mb-8</code> 32 px → <code class="text-[var(--color-cyan)]">mb-6</code> 24 px</div>
|
||||
<div>2 · empty-state Panel padding</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">p-16</code> 64 px → <code class="text-[var(--color-cyan)]">p-card</code> 15 px</div>
|
||||
<div>3 · empty-state icon → heading margin</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">mb-3</code> 12 px → <code class="text-[var(--color-cyan)]">mb-label</code> 7 px</div>
|
||||
<div>4 · LetterBar wrapper</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">my-6</code> 24 px → unchanged (already on rhythm with the hero gap)</div>
|
||||
</div>
|
||||
<p class="why mt-3">FilterBar internal spacing, MasonryGrid, hero stats inline gap, UploadCard, page outer <code class="text-[var(--color-cyan)]">px-6 py-8</code> — all stay as they are.</p>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================
|
||||
BEFORE
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag">BEFORE</span> · current Library page · <code>mb-8 / p-16 / mb-3</code></span><span>app/page.tsx</span></div>
|
||||
<div class="pg-body">
|
||||
<div style="margin-bottom: 32px;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 320px; gap: 24px; align-items: start;">
|
||||
<div>
|
||||
<h1 class="text-3xl font-semibold tracking-tight">Your <span style="background: linear-gradient(90deg, var(--color-cyan), var(--color-violet)); -webkit-background-clip: text; color: transparent;">Cover Library</span></h1>
|
||||
<p class="text-[var(--color-fg-dim)] mt-2 max-w-prose text-sm">Drop cover images to import. Codes are parsed from filenames; metadata can be filled manually or seeded from a sibling .nfo file.</p>
|
||||
<div class="flex flex-wrap gap-6 mt-6 text-sm">
|
||||
<div><div class="text-2xl font-mono font-semibold">1,247</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Covers</div></div>
|
||||
<div><div class="text-2xl font-mono font-semibold">324</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Actresses</div></div>
|
||||
<div><div class="text-2xl font-mono font-semibold">52</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Studios</div></div>
|
||||
<div><div class="text-2xl font-mono font-semibold">186</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Tags</div></div>
|
||||
<div><div class="text-2xl font-mono font-semibold">14</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Collections</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass rounded-2xl flex items-center justify-center text-center" style="padding: 32px; height: 160px;">
|
||||
<div>
|
||||
<div style="font-size: 32px; opacity: 0.4;">⤓</div>
|
||||
<div class="text-sm mt-2">Drag covers to import</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; flex-wrap: wrap;">
|
||||
<span class="pill-sm active">ALL</span>
|
||||
<span class="pill-sm">+ Multi-filter</span>
|
||||
<span class="pill-sm">+ Merged</span>
|
||||
<span class="pill-sm">⚐ Mark</span>
|
||||
<span style="margin-left: auto; display: flex; gap: 8px;">
|
||||
<span class="pill-sm" style="width: 200px; justify-content: flex-start;">⌕ Search…</span>
|
||||
<span class="pill-sm">Sort: Newest</span>
|
||||
<span class="pill-sm">Landscape</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="margin: 24px 0;">
|
||||
<div class="letter-bar">
|
||||
<div class="l">#</div>
|
||||
<div class="l">A</div><div class="l">B</div><div class="l">C</div><div class="l">D</div><div class="l">E</div><div class="l">F</div><div class="l">G</div>
|
||||
<div class="l">H</div><div class="l active">I</div><div class="l">J</div><div class="l">K</div><div class="l">L</div><div class="l">M</div><div class="l">N</div>
|
||||
<div class="l">O</div><div class="l">P</div><div class="l">Q</div><div class="l">R</div><div class="l">S</div><div class="l">T</div><div class="l">U</div>
|
||||
<div class="l">V</div><div class="l">W</div><div class="l">X</div><div class="l">Y</div><div class="l">Z</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty state (BEFORE: p-16, mb-3) -->
|
||||
<div class="glass rounded-2xl text-center" style="padding: 64px;">
|
||||
<div style="width: 40px; height: 40px; margin: 0 auto; border-radius: 999px; background: oklch(0.82 0.16 200 / 0.2); display: grid; place-items: center; color: var(--color-cyan); margin-bottom: 12px;">⊙</div>
|
||||
<h2 class="text-xl font-medium">Nothing Matches</h2>
|
||||
<p class="text-[var(--color-fg-dim)] mt-1">All filtered out — switch back to All.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
AFTER
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">AFTER</span> · spacing-only · <code>mb-6 / p-card / mb-label</code></span><span>app/page.tsx</span></div>
|
||||
<div class="pg-body">
|
||||
<div style="margin-bottom: 24px;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 320px; gap: 24px; align-items: start;">
|
||||
<div>
|
||||
<h1 class="text-3xl font-semibold tracking-tight">Your <span style="background: linear-gradient(90deg, var(--color-cyan), var(--color-violet)); -webkit-background-clip: text; color: transparent;">Cover Library</span></h1>
|
||||
<p class="text-[var(--color-fg-dim)] mt-2 max-w-prose text-sm">Drop cover images to import. Codes are parsed from filenames; metadata can be filled manually or seeded from a sibling .nfo file.</p>
|
||||
<div class="flex flex-wrap gap-6 mt-6 text-sm">
|
||||
<div><div class="text-2xl font-mono font-semibold">1,247</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Covers</div></div>
|
||||
<div><div class="text-2xl font-mono font-semibold">324</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Actresses</div></div>
|
||||
<div><div class="text-2xl font-mono font-semibold">52</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Studios</div></div>
|
||||
<div><div class="text-2xl font-mono font-semibold">186</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Tags</div></div>
|
||||
<div><div class="text-2xl font-mono font-semibold">14</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Collections</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass rounded-2xl flex items-center justify-center text-center" style="padding: 32px; height: 160px;">
|
||||
<div>
|
||||
<div style="font-size: 32px; opacity: 0.4;">⤓</div>
|
||||
<div class="text-sm mt-2">Drag covers to import</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; flex-wrap: wrap;">
|
||||
<span class="pill-sm active">ALL</span>
|
||||
<span class="pill-sm">+ Multi-filter</span>
|
||||
<span class="pill-sm">+ Merged</span>
|
||||
<span class="pill-sm">⚐ Mark</span>
|
||||
<span style="margin-left: auto; display: flex; gap: 8px;">
|
||||
<span class="pill-sm" style="width: 200px; justify-content: flex-start;">⌕ Search…</span>
|
||||
<span class="pill-sm">Sort: Newest</span>
|
||||
<span class="pill-sm">Landscape</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="margin: 24px 0;">
|
||||
<div class="letter-bar">
|
||||
<div class="l">#</div>
|
||||
<div class="l">A</div><div class="l">B</div><div class="l">C</div><div class="l">D</div><div class="l">E</div><div class="l">F</div><div class="l">G</div>
|
||||
<div class="l">H</div><div class="l active">I</div><div class="l">J</div><div class="l">K</div><div class="l">L</div><div class="l">M</div><div class="l">N</div>
|
||||
<div class="l">O</div><div class="l">P</div><div class="l">Q</div><div class="l">R</div><div class="l">S</div><div class="l">T</div><div class="l">U</div>
|
||||
<div class="l">V</div><div class="l">W</div><div class="l">X</div><div class="l">Y</div><div class="l">Z</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty state (AFTER: p-card, mb-label) -->
|
||||
<div class="glass rounded-2xl text-center" style="padding: var(--spacing-card);">
|
||||
<div style="width: 40px; height: 40px; margin: 0 auto; border-radius: 999px; background: oklch(0.82 0.16 200 / 0.2); display: grid; place-items: center; color: var(--color-cyan); margin-bottom: var(--spacing-label);">⊙</div>
|
||||
<h2 class="text-xl font-medium">Nothing Matches</h2>
|
||||
<p class="text-[var(--color-fg-dim)] mt-1">All filtered out — switch back to All.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
Populated AFTER (sanity check that nothing broke when grid is full)
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">AFTER · with grid populated</span> · same vertical rhythm</span><span>sanity check</span></div>
|
||||
<div class="pg-body">
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h1 class="text-3xl font-semibold tracking-tight">Your <span style="background: linear-gradient(90deg, var(--color-cyan), var(--color-violet)); -webkit-background-clip: text; color: transparent;">Cover Library</span></h1>
|
||||
<div class="flex flex-wrap gap-6 mt-4 text-sm">
|
||||
<div><div class="text-xl font-mono font-semibold">1,247</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Covers</div></div>
|
||||
<div><div class="text-xl font-mono font-semibold">324</div><div class="text-xs uppercase tracking-wider text-[var(--color-fg-muted)]">Actresses</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px;">
|
||||
<span class="pill-sm active">ALL</span><span class="pill-sm">+ Multi-filter</span>
|
||||
</div>
|
||||
<div style="margin: 24px 0;">
|
||||
<div class="letter-bar">
|
||||
<div class="l">A</div><div class="l">B</div><div class="l active">I</div><div class="l">M</div><div class="l">Y</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;">
|
||||
<div class="thumb"><span class="id">YUJ-001</span></div>
|
||||
<div class="thumb"><span class="id">IBW-349</span></div>
|
||||
<div class="thumb"><span class="id">SDDE-617</span></div>
|
||||
<div class="thumb"><span class="id">AOZ-200Z</span></div>
|
||||
<div class="thumb"><span class="id">MIDD-014</span></div>
|
||||
<div class="thumb"><span class="id">JUL-882</span></div>
|
||||
<div class="thumb"><span class="id">ABF-119</span></div>
|
||||
<div class="thumb"><span class="id">JUFE-441</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="text-[11px] text-[var(--color-fg-muted)]">
|
||||
Tell me <strong>go</strong> to apply, or point at anything you want different. Then I'll move on to <strong>Actress</strong>.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,479 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex · Combined Mark popover — variants</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0a0710;
|
||||
--bg-1: #14101e;
|
||||
--bg-2: #1c1729;
|
||||
--fg: #e8e6f0;
|
||||
--fg-dim: #a8a4b8;
|
||||
--fg-muted: #6e6a80;
|
||||
--cyan: #22d3ee;
|
||||
--violet: #a78bfa;
|
||||
--mint: #34d399;
|
||||
--coral: #fb7185;
|
||||
--amber: #fbbf24;
|
||||
--glass: rgba(255,255,255,0.04);
|
||||
--glass-strong: rgba(255,255,255,0.08);
|
||||
--border: rgba(255,255,255,0.08);
|
||||
--border-strong: rgba(255,255,255,0.18);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg-0);
|
||||
color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.page { max-width: 1300px; margin: 0 auto; padding: 24px; }
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
padding: 16px 20px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(34,211,238,0.08), rgba(167,139,250,0.05));
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.page-header h1 { margin: 0 0 8px 0; font-size: 22px; font-weight: 600; }
|
||||
.page-header p { margin: 0; color: var(--fg-dim); font-size: 13px; }
|
||||
|
||||
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
|
||||
.grid.full { grid-template-columns: 1fr; }
|
||||
|
||||
.variant {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--bg-1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.variant-head {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--glass);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.variant-num {
|
||||
width: 26px; height: 26px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--cyan), var(--violet));
|
||||
display: grid; place-items: center;
|
||||
font-weight: 700; font-size: 12px; color: #0a0710;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.variant-title { font-size: 15px; font-weight: 600; }
|
||||
.variant-desc { color: var(--fg-dim); font-size: 12px; }
|
||||
.variant-body {
|
||||
padding: 28px;
|
||||
background: var(--bg-0);
|
||||
min-height: 420px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: var(--bg-1); padding: 8px 10px;
|
||||
border: 1px solid var(--border); border-radius: 12px;
|
||||
}
|
||||
.pill-btn {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 6px 12px; border-radius: 999px;
|
||||
background: var(--glass); border: 1px solid var(--border-strong);
|
||||
font-size: 13px; color: var(--fg-dim);
|
||||
cursor: pointer;
|
||||
}
|
||||
.pill-btn:hover { background: var(--glass-strong); color: var(--fg); }
|
||||
.pill-btn.active {
|
||||
background: rgba(34,211,238,0.12);
|
||||
border-color: rgba(34,211,238,0.4);
|
||||
color: var(--cyan);
|
||||
}
|
||||
.badge {
|
||||
background: var(--cyan); color: #0a0710;
|
||||
font-size: 10px; font-weight: 700;
|
||||
padding: 1px 6px; border-radius: 999px;
|
||||
font-family: ui-monospace, monospace;
|
||||
}
|
||||
|
||||
.menu {
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 16px 40px rgba(0,0,0,0.5);
|
||||
overflow: hidden;
|
||||
width: 280px;
|
||||
}
|
||||
.menu-section-h {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 10px 14px 6px;
|
||||
font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em;
|
||||
color: var(--fg-muted);
|
||||
}
|
||||
.menu-section-h .meta { font-family: ui-monospace, monospace; }
|
||||
.menu-row {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 8px 14px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.menu-row:hover { background: var(--glass); }
|
||||
.menu-row.active { background: var(--glass-strong); }
|
||||
.menu-row .ico { width: 16px; height: 16px; display: inline-grid; place-items: center; }
|
||||
.menu-row .ico svg { width: 14px; height: 14px; }
|
||||
.menu-row .label { flex: 1; }
|
||||
.menu-row .check { color: var(--cyan); }
|
||||
.menu-row .count { color: var(--fg-muted); font-family: ui-monospace, monospace; font-size: 11px; }
|
||||
.menu-divider { border: 0; border-top: 1px solid var(--border); margin: 4px 0; }
|
||||
.menu-row.disabled { opacity: 0.4; cursor: not-allowed; }
|
||||
|
||||
.vip { color: var(--cyan); }
|
||||
.fav { color: var(--amber); }
|
||||
.own { color: var(--violet); }
|
||||
.mint { color: var(--mint); }
|
||||
.muted { color: var(--fg-muted); }
|
||||
|
||||
.swatch-cyan { background: rgba(34,211,238,0.18); color: var(--cyan); }
|
||||
.swatch-amber { background: rgba(251,191,36,0.18); color: var(--amber); }
|
||||
.swatch-violet { background: rgba(167,139,250,0.18); color: var(--violet); }
|
||||
.swatch-fg { background: rgba(168,164,184,0.18); color: var(--fg-dim); }
|
||||
.swatch {
|
||||
width: 22px; height: 22px; border-radius: 6px;
|
||||
display: grid; place-items: center;
|
||||
}
|
||||
|
||||
.pills-row { display: flex; gap: 6px; padding: 10px 12px; flex-wrap: wrap; }
|
||||
.quick-pill {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 5px 10px; border-radius: 999px;
|
||||
background: var(--bg-2); border: 1px solid var(--border);
|
||||
font-size: 12px; cursor: pointer;
|
||||
}
|
||||
.quick-pill svg { width: 12px; height: 12px; }
|
||||
.quick-pill.cyan { color: var(--cyan); border-color: rgba(34,211,238,0.4); }
|
||||
.quick-pill.amber { color: var(--amber); border-color: rgba(251,191,36,0.4); }
|
||||
.quick-pill.violet { color: var(--violet); border-color: rgba(167,139,250,0.4); }
|
||||
.quick-pill.muted { color: var(--fg-dim); }
|
||||
|
||||
.ctx-row {
|
||||
color: var(--fg-muted); font-size: 11px; font-family: ui-monospace, monospace;
|
||||
padding: 8px 14px; border-bottom: 1px solid var(--border);
|
||||
background: rgba(255,255,255,0.02);
|
||||
}
|
||||
|
||||
.toggle-track {
|
||||
display: inline-flex; padding: 3px;
|
||||
background: var(--bg-2); border: 1px solid var(--border);
|
||||
border-radius: 999px; gap: 2px;
|
||||
}
|
||||
.toggle-track .opt {
|
||||
padding: 4px 12px; font-size: 11px; border-radius: 999px;
|
||||
color: var(--fg-muted); cursor: pointer;
|
||||
}
|
||||
.toggle-track .opt.active { background: var(--glass-strong); color: var(--fg); }
|
||||
|
||||
.scenario-label {
|
||||
text-align: center; color: var(--fg-muted); font-size: 11px;
|
||||
margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.05em;
|
||||
}
|
||||
.stack { display: flex; flex-direction: column; align-items: center; gap: 16px; min-width: 320px; }
|
||||
|
||||
.picked {
|
||||
margin: 24px 0 0; padding: 12px 16px;
|
||||
border-radius: 10px; background: rgba(52,211,153,0.07);
|
||||
border: 1px solid rgba(52,211,153,0.25);
|
||||
color: var(--mint); font-size: 13px;
|
||||
}
|
||||
|
||||
/* tiny inline svg helpers */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="page-header">
|
||||
<h1>Combined Mark popover — variants</h1>
|
||||
<p>Single button in the toolbar. Top half always filters; bottom half marks the current selection. When nothing is selected, the lower half disables itself but stays visible — so the mental model is "Mark" = both verbs in one place. Three layouts:</p>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
|
||||
<!-- ──────────── Variant A ──────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">A</div>
|
||||
<div>
|
||||
<div class="variant-title">Two clearly labeled sections</div>
|
||||
<div class="variant-desc">"Filter to…" on top, "Mark selected as…" on bottom. Selection count shown as a section meta.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
|
||||
<div class="stack">
|
||||
<div class="scenario-label">No selection</div>
|
||||
<div class="toolbar">
|
||||
<button class="pill-btn">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
|
||||
Mark
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:11px;height:11px;opacity:0.6"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<div class="menu-section-h"><span>Filter to</span><span class="meta">all</span></div>
|
||||
<div class="menu-row active">
|
||||
<span class="swatch swatch-fg">●</span>
|
||||
<span class="label">All covers</span>
|
||||
<span class="check">✓</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-cyan">◆</span>
|
||||
<span class="label">VIP</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-amber">★</span>
|
||||
<span class="label">Favorite</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-violet">▣</span>
|
||||
<span class="label">Owned</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-fg">⊖</span>
|
||||
<span class="label">Unmarked</span>
|
||||
</div>
|
||||
<hr class="menu-divider" />
|
||||
<div class="menu-section-h"><span>Mark selected as</span><span class="meta muted">no selection</span></div>
|
||||
<div class="menu-row disabled">
|
||||
<span class="swatch swatch-cyan">◆</span>
|
||||
<span class="label">VIP</span>
|
||||
</div>
|
||||
<div class="menu-row disabled">
|
||||
<span class="swatch swatch-amber">★</span>
|
||||
<span class="label">Favorite</span>
|
||||
</div>
|
||||
<div class="menu-row disabled">
|
||||
<span class="swatch swatch-violet">▣</span>
|
||||
<span class="label">Owned</span>
|
||||
</div>
|
||||
<div class="menu-row disabled">
|
||||
<span class="swatch swatch-fg">⊖</span>
|
||||
<span class="label">Unmark</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<div class="scenario-label">12 selected</div>
|
||||
<div class="toolbar">
|
||||
<button class="pill-btn active">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
|
||||
Mark
|
||||
<span class="badge">12</span>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:11px;height:11px;opacity:0.6"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<div class="menu-section-h"><span>Filter to</span><span class="meta">all</span></div>
|
||||
<div class="menu-row active">
|
||||
<span class="swatch swatch-fg">●</span>
|
||||
<span class="label">All covers</span>
|
||||
<span class="check">✓</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-cyan">◆</span>
|
||||
<span class="label">VIP</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-amber">★</span>
|
||||
<span class="label">Favorite</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-violet">▣</span>
|
||||
<span class="label">Owned</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-fg">⊖</span>
|
||||
<span class="label">Unmarked</span>
|
||||
</div>
|
||||
<hr class="menu-divider" />
|
||||
<div class="menu-section-h"><span>Mark 12 selected as</span><span class="meta">— mixed</span></div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-cyan">◆</span>
|
||||
<span class="label">VIP</span>
|
||||
<span class="count">3 / 12</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-amber">★</span>
|
||||
<span class="label">Favorite</span>
|
||||
<span class="count">0 / 12</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-violet">▣</span>
|
||||
<span class="label">Owned</span>
|
||||
<span class="count">7 / 12</span>
|
||||
</div>
|
||||
<div class="menu-row">
|
||||
<span class="swatch swatch-fg">⊖</span>
|
||||
<span class="label">Unmark all</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ──────────── Variant B ──────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">B</div>
|
||||
<div>
|
||||
<div class="variant-title">Two-column "Filter | Mark As"</div>
|
||||
<div class="variant-desc">Side-by-side columns share the same icons. Eye-tracking left for filter, right for action. Compact.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
|
||||
<div class="stack">
|
||||
<div class="scenario-label">12 selected</div>
|
||||
<div class="toolbar">
|
||||
<button class="pill-btn active">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
|
||||
Mark
|
||||
<span class="badge">12</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu" style="width: 360px;">
|
||||
<div style="display:grid; grid-template-columns: 1fr 1fr; border-bottom: 1px solid var(--border); font-size: 10px; text-transform: uppercase; letter-spacing:0.06em; color: var(--fg-muted);">
|
||||
<div style="padding: 10px 14px;">Filter</div>
|
||||
<div style="padding: 10px 14px; border-left: 1px solid var(--border);">Mark 12 As</div>
|
||||
</div>
|
||||
<div style="display:grid; grid-template-columns: 1fr 1fr;">
|
||||
<div>
|
||||
<div class="menu-row active"><span class="swatch swatch-cyan">◆</span><span class="label">VIP</span></div>
|
||||
<div class="menu-row"><span class="swatch swatch-amber">★</span><span class="label">Fav</span></div>
|
||||
<div class="menu-row"><span class="swatch swatch-violet">▣</span><span class="label">Owned</span></div>
|
||||
<div class="menu-row"><span class="swatch swatch-fg">⊖</span><span class="label">Unmarked</span></div>
|
||||
<div class="menu-row muted"><span class="swatch swatch-fg">●</span><span class="label">Reset</span></div>
|
||||
</div>
|
||||
<div style="border-left: 1px solid var(--border);">
|
||||
<div class="menu-row"><span class="swatch swatch-cyan">◆</span><span class="label">VIP</span><span class="count">3</span></div>
|
||||
<div class="menu-row"><span class="swatch swatch-amber">★</span><span class="label">Fav</span><span class="count">0</span></div>
|
||||
<div class="menu-row"><span class="swatch swatch-violet">▣</span><span class="label">Owned</span><span class="count">7</span></div>
|
||||
<div class="menu-row"><span class="swatch swatch-fg">⊖</span><span class="label">Unmark</span></div>
|
||||
<div class="menu-row" style="opacity:0;pointer-events:none;"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<div class="scenario-label">No selection</div>
|
||||
<div class="toolbar">
|
||||
<button class="pill-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg> Mark</button>
|
||||
</div>
|
||||
<div class="menu" style="width: 360px;">
|
||||
<div style="display:grid; grid-template-columns: 1fr 1fr; border-bottom: 1px solid var(--border); font-size: 10px; text-transform: uppercase; letter-spacing:0.06em; color: var(--fg-muted);">
|
||||
<div style="padding: 10px 14px;">Filter</div>
|
||||
<div style="padding: 10px 14px; border-left: 1px solid var(--border);">Mark as <span style="text-transform:none; color: var(--fg-muted);">— select first</span></div>
|
||||
</div>
|
||||
<div style="display:grid; grid-template-columns: 1fr 1fr;">
|
||||
<div>
|
||||
<div class="menu-row active"><span class="swatch swatch-fg">●</span><span class="label">All</span><span class="check">✓</span></div>
|
||||
<div class="menu-row"><span class="swatch swatch-cyan">◆</span><span class="label">VIP</span></div>
|
||||
<div class="menu-row"><span class="swatch swatch-amber">★</span><span class="label">Fav</span></div>
|
||||
<div class="menu-row"><span class="swatch swatch-violet">▣</span><span class="label">Owned</span></div>
|
||||
<div class="menu-row"><span class="swatch swatch-fg">⊖</span><span class="label">Unmarked</span></div>
|
||||
</div>
|
||||
<div style="border-left: 1px solid var(--border);">
|
||||
<div class="menu-row disabled"><span class="swatch swatch-cyan">◆</span><span class="label">VIP</span></div>
|
||||
<div class="menu-row disabled"><span class="swatch swatch-amber">★</span><span class="label">Fav</span></div>
|
||||
<div class="menu-row disabled"><span class="swatch swatch-violet">▣</span><span class="label">Owned</span></div>
|
||||
<div class="menu-row disabled"><span class="swatch swatch-fg">⊖</span><span class="label">Unmark</span></div>
|
||||
<div class="menu-row" style="opacity:0;pointer-events:none;"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ──────────── Variant C — full width ──────────── -->
|
||||
<div class="grid full" style="margin-top: 24px;">
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">C</div>
|
||||
<div>
|
||||
<div class="variant-title">Mode toggle on top, then a single icon row</div>
|
||||
<div class="variant-desc">A compact pill-toggle ("Filter" ↔ "Mark As") flips the verb. Below: one row of icons, big and clickable. The selected count is the trigger badge. When nothing is selected, "Mark As" tab is disabled.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body" style="gap: 60px;">
|
||||
|
||||
<div class="stack">
|
||||
<div class="scenario-label">12 selected · "Mark As" tab</div>
|
||||
<div class="toolbar">
|
||||
<button class="pill-btn active">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
|
||||
Mark
|
||||
<span class="badge">12</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu" style="width: 320px;">
|
||||
<div style="display:flex; justify-content: center; padding: 12px;">
|
||||
<div class="toggle-track">
|
||||
<span class="opt">Filter</span>
|
||||
<span class="opt active">Mark 12 As</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="menu-divider" style="margin: 0;" />
|
||||
<div class="pills-row" style="justify-content: center; padding: 16px 12px;">
|
||||
<button class="quick-pill cyan">◆ VIP <span class="count">3</span></button>
|
||||
<button class="quick-pill amber">★ Fav <span class="count">0</span></button>
|
||||
<button class="quick-pill violet">▣ Owned <span class="count">7</span></button>
|
||||
<button class="quick-pill muted">⊖ Unmark</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<div class="scenario-label">No selection · "Filter" tab</div>
|
||||
<div class="toolbar">
|
||||
<button class="pill-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg> Mark</button>
|
||||
</div>
|
||||
<div class="menu" style="width: 320px;">
|
||||
<div style="display:flex; justify-content: center; padding: 12px;">
|
||||
<div class="toggle-track">
|
||||
<span class="opt active">Filter</span>
|
||||
<span class="opt" style="opacity: 0.4;">Mark As</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="menu-divider" style="margin: 0;" />
|
||||
<div class="pills-row" style="justify-content: center; padding: 16px 12px;">
|
||||
<button class="quick-pill" style="background: var(--glass-strong); color: var(--fg);">● All</button>
|
||||
<button class="quick-pill cyan">◆ VIP</button>
|
||||
<button class="quick-pill amber">★ Fav</button>
|
||||
<button class="quick-pill violet">▣ Owned</button>
|
||||
<button class="quick-pill muted">⊖ Unmarked</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="picked">
|
||||
<strong>My pick: A</strong> — labeled sections beat hidden modes. The user always sees both verbs at once, no toggling, no "is it filter mode or mark mode" confusion. Selection count appears in the section header so it's obvious which half acts on what. Tri-state counts ("3 / 12") on the action rows mirror the right-click context menu pattern you already use.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,408 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Pinkudex Netflix Hero Concepts</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #090913;
|
||||
--panel: rgba(18, 19, 31, .74);
|
||||
--text: #f4f1fb;
|
||||
--muted: #a6a0b4;
|
||||
--line: rgba(255,255,255,.15);
|
||||
--cyan: #00d9f6;
|
||||
--green: #43d488;
|
||||
--violet: #a774f5;
|
||||
--red: #ff5d64;
|
||||
--amber: #ffbd5a;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: radial-gradient(circle at 15% 0%, #241832 0, transparent 34%), var(--bg);
|
||||
color: var(--text);
|
||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
}
|
||||
.topbar {
|
||||
height: 58px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28px;
|
||||
padding: 0 24px;
|
||||
background: rgba(8, 8, 15, .86);
|
||||
border-bottom: 1px solid #272436;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
.brand { color: var(--cyan); font-weight: 800; font-size: 18px; }
|
||||
.nav { display: flex; gap: 24px; color: #aaa5b8; font-size: 14px; }
|
||||
.search { margin-left: auto; width: 320px; height: 36px; border: 1px solid #343348; border-radius: 10px; color: #777386; display: flex; align-items: center; padding: 0 14px; }
|
||||
main { padding: 28px 24px 70px; }
|
||||
.intro { margin-bottom: 22px; }
|
||||
.intro h1 { margin: 0 0 8px; font-size: 24px; }
|
||||
.intro p { margin: 0; color: var(--muted); max-width: 850px; line-height: 1.45; }
|
||||
.concepts { display: grid; gap: 28px; }
|
||||
.concept {
|
||||
border: 1px solid rgba(255,255,255,.16);
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
background: #10101d;
|
||||
box-shadow: 0 18px 48px rgba(0,0,0,.32);
|
||||
}
|
||||
.concept-head {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 16px 18px;
|
||||
border-bottom: 1px solid rgba(255,255,255,.11);
|
||||
background: rgba(255,255,255,.035);
|
||||
}
|
||||
.concept-head h2 { margin: 0 0 4px; font-size: 18px; }
|
||||
.concept-head p { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.38; }
|
||||
.tag {
|
||||
flex: 0 0 auto;
|
||||
border: 1px solid rgba(0,217,246,.35);
|
||||
color: var(--cyan);
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
font: 11px ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .08em;
|
||||
}
|
||||
.hero {
|
||||
min-height: 560px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(circle at 72% 42%, rgba(0,217,246,.10), transparent 34%),
|
||||
linear-gradient(135deg, #11111e, #191625);
|
||||
}
|
||||
.art {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
width: min(800px, calc(100% - 48px));
|
||||
aspect-ratio: 800 / 538;
|
||||
max-height: 538px;
|
||||
border-radius: 16px;
|
||||
background: #d7d7da;
|
||||
border: 1px solid rgba(255,255,255,.20);
|
||||
box-shadow: 0 28px 80px rgba(0,0,0,.45);
|
||||
overflow: hidden;
|
||||
}
|
||||
.art:before {
|
||||
content: "798 × 537";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: rgba(20, 20, 25, .22);
|
||||
font-size: clamp(42px, 6vw, 96px);
|
||||
letter-spacing: .05em;
|
||||
font-weight: 300;
|
||||
}
|
||||
.art:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hero:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.copy {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
max-width: 620px;
|
||||
}
|
||||
.eyebrow {
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .14em;
|
||||
font: 11px ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
.code {
|
||||
color: var(--cyan);
|
||||
font: 800 clamp(34px, 5vw, 68px) / .92 ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
letter-spacing: .03em;
|
||||
}
|
||||
.title { color: #ded9ea; font-size: 22px; font-style: italic; }
|
||||
.facts { display: flex; flex-wrap: wrap; gap: 8px; color: #e8e4f2; font: 13px ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
padding: 0 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(255,255,255,.20);
|
||||
background: rgba(255,255,255,.08);
|
||||
font-size: 11px;
|
||||
color: #ddd8e8;
|
||||
}
|
||||
.pill.cyan { color: var(--cyan); border-color: rgba(0,217,246,.42); background: rgba(0,217,246,.10); }
|
||||
.pill.green { color: var(--green); border-color: rgba(67,212,136,.38); background: rgba(67,212,136,.10); }
|
||||
.pill.violet { color: var(--violet); border-color: rgba(167,116,245,.40); background: rgba(167,116,245,.12); }
|
||||
.pill.amber { color: var(--amber); border-color: rgba(255,189,90,.38); background: rgba(255,189,90,.11); }
|
||||
.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 4px; }
|
||||
.play, .ghost, .danger {
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
padding: 0 16px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
font-weight: 750;
|
||||
font-size: 14px;
|
||||
}
|
||||
.play { background: var(--text); color: #0a0a12; }
|
||||
.ghost { background: rgba(255,255,255,.13); border: 1px solid rgba(255,255,255,.22); color: var(--text); }
|
||||
.danger { background: rgba(255,93,100,.10); border: 1px solid rgba(255,93,100,.38); color: var(--red); }
|
||||
.shelf {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.card {
|
||||
border: 1px solid rgba(255,255,255,.14);
|
||||
background: rgba(18, 19, 31, .70);
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
backdrop-filter: blur(12px);
|
||||
min-height: 76px;
|
||||
}
|
||||
.label { color: #8d879c; text-transform: uppercase; letter-spacing: .12em; font: 10px ui-monospace, SFMono-Regular, Menlo, monospace; margin-bottom: 8px; }
|
||||
.value { color: #f0edf8; font-size: 13px; }
|
||||
|
||||
.h1 {
|
||||
background: radial-gradient(circle at 72% 42%, rgba(0,217,246,.10), transparent 34%), linear-gradient(135deg, #11111e, #191625);
|
||||
}
|
||||
.h1 .art { right: 42px; top: 42px; }
|
||||
.h1 .art:after { background: linear-gradient(90deg, rgba(8,8,15,.80), rgba(8,8,15,.05) 45%, rgba(8,8,15,.20)); }
|
||||
.h1 .copy { padding: 70px 0 0 44px; }
|
||||
.h1 .shelf { position: absolute; left: 44px; right: 44px; bottom: 28px; grid-template-columns: 1.2fr .8fr .8fr; }
|
||||
|
||||
.h2 {
|
||||
background: radial-gradient(circle at 50% 30%, rgba(167,116,245,.11), transparent 36%), linear-gradient(135deg, #11111e, #171423);
|
||||
}
|
||||
.h2 .art { left: 50%; top: 24px; transform: translateX(-50%); }
|
||||
.h2 .art:after { background: linear-gradient(0deg, rgba(8,8,15,.76), rgba(8,8,15,.04) 52%); }
|
||||
.h2 .copy { position: absolute; left: 36px; right: 36px; bottom: 118px; max-width: none; }
|
||||
.h2 .shelf { position: absolute; left: 36px; right: 36px; bottom: 24px; grid-template-columns: repeat(4, minmax(0,1fr)); }
|
||||
|
||||
.h3 {
|
||||
background: radial-gradient(circle at 76% 48%, rgba(67,212,136,.10), transparent 34%), linear-gradient(135deg, #11111e, #181625);
|
||||
}
|
||||
.h3 .art { right: 360px; top: 42px; }
|
||||
.h3 .art:after { background: linear-gradient(90deg, rgba(8,8,15,.76), rgba(8,8,15,.03) 55%); }
|
||||
.h3 .copy { padding: 54px 0 0 40px; width: 45%; }
|
||||
.h3 .shelf { position: absolute; top: 28px; right: 28px; width: 300px; }
|
||||
|
||||
.h4 {
|
||||
display: grid;
|
||||
grid-template-columns: 360px 1fr;
|
||||
background: #10101d;
|
||||
}
|
||||
.h4 .art { left: 390px; top: 42px; }
|
||||
.h4 .art:after { background: linear-gradient(90deg, rgba(16,16,29,.58), rgba(16,16,29,.04)); }
|
||||
.h4 .left-panel { position: relative; z-index: 1; background: rgba(13,13,24,.96); border-right: 1px solid rgba(255,255,255,.12); padding: 36px 28px; display: grid; align-content: start; gap: 18px; }
|
||||
.h4 .copy { padding: 0; }
|
||||
.h4 .shelf { margin-top: 10px; }
|
||||
|
||||
.h5 {
|
||||
background: radial-gradient(circle at 64% 38%, rgba(0,217,246,.11), transparent 34%), linear-gradient(135deg, #11111e, #191625);
|
||||
}
|
||||
.h5 .art { left: 360px; top: 36px; width: min(720px, calc(100% - 580px)); }
|
||||
.h5 .art:after { background: linear-gradient(90deg, rgba(8,8,15,.64), rgba(8,8,15,.06)); }
|
||||
.h5 .copy { position: absolute; left: 38px; top: 38px; max-width: 500px; }
|
||||
.h5 .parts { position: absolute; z-index: 1; right: 28px; top: 28px; bottom: 28px; width: 170px; display: grid; gap: 10px; align-content: start; }
|
||||
.part { height: 82px; border-radius: 10px; border: 1px solid rgba(255,255,255,.17); background: rgba(16,17,28,.72); padding: 10px; backdrop-filter: blur(8px); }
|
||||
.part.active { outline: 2px solid var(--cyan); }
|
||||
|
||||
.h6 {
|
||||
background: radial-gradient(circle at 50% 30%, rgba(0,217,246,.09), transparent 35%), linear-gradient(135deg, #11111e, #191625);
|
||||
}
|
||||
.h6 .art { left: 50%; top: 26px; transform: translateX(-50%); opacity: .72; }
|
||||
.h6 .art:after { background: linear-gradient(0deg, rgba(8,8,15,.88), rgba(8,8,15,.25)); }
|
||||
.h6 .copy { margin: 0 auto; padding-top: 72px; text-align: center; max-width: 760px; }
|
||||
.h6 .facts, .h6 .actions { justify-content: center; }
|
||||
.h6 .shelf { position: absolute; left: 50%; bottom: 26px; transform: translateX(-50%); width: min(900px, calc(100% - 64px)); grid-template-columns: repeat(3, 1fr); }
|
||||
|
||||
.h7 {
|
||||
background: radial-gradient(circle at 54% 42%, rgba(167,116,245,.10), transparent 36%), linear-gradient(135deg, #11111e, #191625);
|
||||
}
|
||||
.h7 .art { left: 340px; top: 34px; width: min(720px, calc(100% - 760px)); }
|
||||
.h7 .art:after { background: linear-gradient(90deg, rgba(8,8,15,.48), rgba(8,8,15,.06)); }
|
||||
.h7 .copy { position: absolute; left: 36px; bottom: 36px; }
|
||||
.h7 .quick { position: absolute; z-index: 1; right: 28px; bottom: 28px; width: 360px; display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
.metric { border: 1px solid rgba(255,255,255,.14); background: rgba(18,19,31,.74); border-radius: 12px; padding: 13px; backdrop-filter: blur(10px); }
|
||||
.metric strong { display: block; color: #fff; font-size: 18px; margin-top: 3px; }
|
||||
|
||||
.h8 {
|
||||
background: radial-gradient(circle at 68% 38%, rgba(67,212,136,.10), transparent 34%), linear-gradient(135deg, #11111e, #191625);
|
||||
}
|
||||
.h8 .art { right: 36px; top: 28px; }
|
||||
.h8 .art:after { background: linear-gradient(90deg, rgba(8,8,15,.76), rgba(8,8,15,.04)); }
|
||||
.h8 .copy { padding: 46px 0 0 36px; }
|
||||
.h8 .editor { position: absolute; z-index: 1; left: 36px; right: 36px; bottom: 28px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
|
||||
|
||||
.h9 {
|
||||
background: radial-gradient(circle at 70% 42%, rgba(0,217,246,.10), transparent 34%), linear-gradient(135deg, #11111e, #191625);
|
||||
}
|
||||
.h9 .art { right: 42px; top: 34px; }
|
||||
.h9 .art:after { background: linear-gradient(90deg, rgba(8,8,15,.76), rgba(8,8,15,.04)); }
|
||||
.h9 .copy { padding: 56px 0 0 42px; }
|
||||
.h9 .timeline { position: absolute; z-index: 1; left: 42px; right: 42px; bottom: 34px; height: 80px; display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
|
||||
.frame { border-radius: 10px; background: rgba(214,214,218,.80); border: 1px solid rgba(255,255,255,.20); display: grid; place-items: end start; padding: 8px; color: rgba(20,20,25,.65); font: 11px ui-monospace, monospace; }
|
||||
|
||||
.h10 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 420px;
|
||||
background: radial-gradient(circle at 34% 42%, rgba(167,116,245,.10), transparent 36%), linear-gradient(135deg, #11111e, #191625);
|
||||
}
|
||||
.h10 .art { left: 36px; top: 42px; width: min(800px, calc(100% - 504px)); }
|
||||
.h10 .art:after { background: linear-gradient(90deg, rgba(8,8,15,.22), rgba(8,8,15,.62)); }
|
||||
.h10 .dock { position: relative; z-index: 1; grid-column: 2; background: rgba(13,13,24,.95); border-left: 1px solid rgba(255,255,255,.13); padding: 36px 28px; display: grid; gap: 14px; align-content: start; }
|
||||
.h10 .copy { padding: 0; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.nav, .search { display: none; }
|
||||
.hero { min-height: 650px; }
|
||||
.art { left: 20px !important; right: auto !important; top: 22px !important; width: calc(100% - 40px) !important; transform: none !important; }
|
||||
.h1 .copy, .h3 .copy, .h5 .copy, .h8 .copy, .h9 .copy { width: auto; max-width: none; padding: 34px 24px 0; left: 0; right: 0; }
|
||||
.h1 .shelf, .h2 .shelf, .h6 .shelf, .h8 .editor { grid-template-columns: 1fr; left: 20px; right: 20px; width: auto; transform: none; }
|
||||
.h4, .h10 { display: block; }
|
||||
.h4 .left-panel, .h10 .dock { min-height: 650px; }
|
||||
.h5 .parts, .h7 .quick { left: 20px; right: 20px; top: auto; bottom: 22px; width: auto; }
|
||||
.h7 .quick { grid-template-columns: 1fr 1fr; }
|
||||
.h9 .timeline { grid-template-columns: repeat(2, 1fr); height: auto; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<div class="brand">◎ Pinkudex</div>
|
||||
<div class="nav"><span>Library</span><span>Actress</span><span>Database</span><span>Categories</span><span>Tags</span><span>Collection</span></div>
|
||||
<div class="search">⌕ Search Code, Title, Notes...</div>
|
||||
</div>
|
||||
<main>
|
||||
<div class="intro">
|
||||
<h1>10 Netflix-Style Hero Concepts</h1>
|
||||
<p>Each sample uses the same page content but explores a different cinematic hierarchy: play-first, metadata-first, multipart-first, editing-first, and gallery-first.</p>
|
||||
</div>
|
||||
|
||||
<section class="concepts">
|
||||
<article class="concept">
|
||||
<div class="concept-head"><div><h2>1. Classic Left Gradient</h2><p>Most Netflix-like: strong left title block, media facts, play/edit actions, and a glass metadata shelf.</p></div><span class="tag">default</span></div>
|
||||
<div class="hero h1">
|
||||
<div class="art"></div>
|
||||
<div class="copy">
|
||||
<div class="eyebrow">Now in library</div><div class="code">YUJ-001</div><div class="title">Untitled</div>
|
||||
<div class="facts"><span>1920x1080</span><span>·</span><span>H.264</span><span>·</span><span>5.7 Mbps</span><span>·</span><span>4.90 GB</span><span>·</span><span>1:56:55</span></div>
|
||||
<div class="facts"><span class="pill cyan">VIP</span><span class="pill green">Watched</span><span class="pill violet">Owned</span><span class="pill amber">2 parts</span></div>
|
||||
<div class="actions"><span class="play">▶ Play</span><span class="ghost">✎ Edit Metadata</span><span class="ghost">⇩ Import</span></div>
|
||||
</div>
|
||||
<div class="shelf"><div class="card"><div class="label">Actress</div><div class="value">Ichika Matsumoto</div></div><div class="card"><div class="label">Collection</div><div class="value">Sugar & Spice</div></div><div class="card"><div class="label">Cover</div><div class="value">800x538 · 170 KB</div></div></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="concept">
|
||||
<div class="concept-head"><div><h2>2. Bottom Dock Hero</h2><p>Lets the artwork breathe, with the UI collected at the bottom like a streaming detail overlay.</p></div><span class="tag">cinematic</span></div>
|
||||
<div class="hero h2">
|
||||
<div class="art"></div>
|
||||
<div class="copy"><div class="eyebrow">Ready to play</div><div class="code">YUJ-001</div><div class="title">Untitled</div><div class="actions"><span class="play">▶ Play</span><span class="ghost">+ Add Tag</span><span class="ghost">⤴ Reveal</span><span class="danger">Delete</span></div></div>
|
||||
<div class="shelf"><div class="card"><div class="label">Video</div><div class="value">1920x1080 · H.264</div></div><div class="card"><div class="label">Bitrate</div><div class="value">5.7 Mbps</div></div><div class="card"><div class="label">Size</div><div class="value">4.90 GB</div></div><div class="card"><div class="label">Length</div><div class="value">1:56:55</div></div></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="concept">
|
||||
<div class="concept-head"><div><h2>3. Floating Inspector</h2><p>Hero stays immersive while a compact right inspector carries actionable metadata and organization.</p></div><span class="tag">organized</span></div>
|
||||
<div class="hero h3">
|
||||
<div class="art"></div>
|
||||
<div class="copy"><div class="eyebrow">Featured cover</div><div class="code">YUJ-001</div><div class="title">Untitled</div><div class="facts"><span class="pill cyan">VIP</span><span class="pill green">Watched</span><span class="pill violet">Owned</span></div><div class="actions"><span class="play">▶ Play</span><span class="ghost">Edit</span></div></div>
|
||||
<div class="shelf"><div class="card"><div class="label">Video file</div><div class="value">1920x1080 · H.264<br>5.7 Mbps · 4.90 GB · 1:56:55</div></div><div class="card"><div class="label">Cover file</div><div class="value">800x538 · 170 KB</div></div><div class="card"><div class="label">Organize</div><div class="value">Ichika Matsumoto<br>Sugar & Spice</div></div></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="concept">
|
||||
<div class="concept-head"><div><h2>4. Left Control Panel</h2><p>A fixed panel gives a streaming-app feel while staying practical for metadata editing.</p></div><span class="tag">control</span></div>
|
||||
<div class="hero h4">
|
||||
<div class="art"></div>
|
||||
<div class="left-panel">
|
||||
<div class="copy"><div class="eyebrow">Pinkudex detail</div><div class="code">YUJ-001</div><div class="title">Untitled</div><div class="facts"><span>1920x1080</span><span>·</span><span>H.264</span><span>·</span><span>1:56:55</span></div><div class="actions"><span class="play">▶ Play</span><span class="ghost">Edit</span></div></div>
|
||||
<div class="shelf"><div class="card"><div class="label">Cover</div><div class="value">800x538 · 170 KB</div></div><div class="card"><div class="label">Video</div><div class="value">5.7 Mbps · 4.90 GB</div></div><div class="card"><div class="label">Status</div><div class="value">VIP · Watched · Owned</div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="concept">
|
||||
<div class="concept-head"><div><h2>5. Multipart Rail Hero</h2><p>Designed for multi-part videos, with per-part chips always visible on the right.</p></div><span class="tag">multipart</span></div>
|
||||
<div class="hero h5">
|
||||
<div class="art"></div>
|
||||
<div class="copy"><div class="eyebrow">2 part video</div><div class="code">YUJ-001</div><div class="title">Untitled</div><div class="facts"><span>Current part</span><span>·</span><span>1920x1080</span><span>·</span><span>H.264</span><span>·</span><span>5.7 Mbps</span></div><div class="actions"><span class="play">▶ Play Part 1</span><span class="ghost">Edit Metadata</span></div></div>
|
||||
<div class="parts"><div class="part active"><div class="label">Part 1</div><div class="value">1080p · H.264<br>1:02:11</div></div><div class="part"><div class="label">Part 2</div><div class="value">1080p · H.264<br>54:44</div></div><div class="part"><div class="label">Cover</div><div class="value">800x538 · 170 KB</div></div></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="concept">
|
||||
<div class="concept-head"><div><h2>6. Center Stage</h2><p>More theatrical and less utilitarian, useful if the detail page should feel like a playback destination.</p></div><span class="tag">theater</span></div>
|
||||
<div class="hero h6">
|
||||
<div class="art"></div>
|
||||
<div class="copy"><div class="eyebrow">Tonight's pick</div><div class="code">YUJ-001</div><div class="title">Untitled</div><div class="facts"><span>1920x1080</span><span>·</span><span>H.264</span><span>·</span><span>5.7 Mbps</span><span>·</span><span>4.90 GB</span><span>·</span><span>1:56:55</span></div><div class="actions"><span class="play">▶ Play</span><span class="ghost">Edit</span><span class="ghost">Import</span></div></div>
|
||||
<div class="shelf"><div class="card"><div class="label">Actress</div><div class="value">Ichika Matsumoto</div></div><div class="card"><div class="label">Collection</div><div class="value">Sugar & Spice</div></div><div class="card"><div class="label">Status</div><div class="value">★★★★★ · VIP · Watched</div></div></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="concept">
|
||||
<div class="concept-head"><div><h2>7. Metrics Overlay</h2><p>Hero plus a visible metadata dashboard; strongest when video facts are the star of the redesign.</p></div><span class="tag">metadata</span></div>
|
||||
<div class="hero h7">
|
||||
<div class="art"></div>
|
||||
<div class="copy"><div class="eyebrow">File verified</div><div class="code">YUJ-001</div><div class="title">Untitled</div><div class="actions"><span class="play">▶ Play</span><span class="ghost">Reveal</span></div></div>
|
||||
<div class="quick"><div class="metric"><div class="label">Resolution</div><strong>1920x1080</strong></div><div class="metric"><div class="label">Codec</div><strong>H.264</strong></div><div class="metric"><div class="label">Bitrate</div><strong>5.7 Mbps</strong></div><div class="metric"><div class="label">Length</div><strong>1:56:55</strong></div></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="concept">
|
||||
<div class="concept-head"><div><h2>8. Editing Dock</h2><p>Keeps the hero visual but gives edit/import/delete equal-width controls below the core identity.</p></div><span class="tag">editing</span></div>
|
||||
<div class="hero h8">
|
||||
<div class="art"></div>
|
||||
<div class="copy"><div class="eyebrow">Library item</div><div class="code">YUJ-001</div><div class="title">Untitled</div><div class="facts"><span class="pill cyan">VIP</span><span class="pill green">Watched</span><span class="pill violet">Owned</span></div><div class="actions"><span class="play">▶ Play</span></div></div>
|
||||
<div class="editor"><div class="card"><div class="label">Cover file</div><div class="value">800x538 · 170 KB</div></div><div class="card"><div class="label">Video file</div><div class="value">1920x1080 · H.264 · 5.7 Mbps · 4.90 GB · 1:56:55</div></div><div class="card"><div class="label">Actions</div><div class="actions"><span class="ghost">Edit Metadata</span><span class="ghost">Import</span><span class="danger">Delete</span></div></div></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="concept">
|
||||
<div class="concept-head"><div><h2>9. Preview Timeline</h2><p>Feels like a streaming player detail page with visual chapter/thumb placeholders along the bottom.</p></div><span class="tag">gallery</span></div>
|
||||
<div class="hero h9">
|
||||
<div class="art"></div>
|
||||
<div class="copy"><div class="eyebrow">Preview strip</div><div class="code">YUJ-001</div><div class="title">Untitled</div><div class="facts"><span>1920x1080</span><span>·</span><span>H.264</span><span>·</span><span>1:56:55</span></div><div class="actions"><span class="play">▶ Play</span><span class="ghost">Edit</span></div></div>
|
||||
<div class="timeline"><div class="frame">00:00</div><div class="frame">20:00</div><div class="frame">40:00</div><div class="frame">1:00:00</div><div class="frame">1:30:00</div></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="concept">
|
||||
<div class="concept-head"><div><h2>10. Right Dock Detail</h2><p>Opposite of the classic Netflix layout: full artwork with a right-side metadata dock for quick scanning.</p></div><span class="tag">right dock</span></div>
|
||||
<div class="hero h10">
|
||||
<div class="art"></div>
|
||||
<div class="dock">
|
||||
<div class="copy"><div class="eyebrow">Selected cover</div><div class="code">YUJ-001</div><div class="title">Untitled</div><div class="actions"><span class="play">▶ Play</span><span class="ghost">Edit</span></div></div>
|
||||
<div class="card"><div class="label">Video</div><div class="value">1920x1080 · H.264<br>5.7 Mbps · 4.90 GB · 1:56:55</div></div>
|
||||
<div class="card"><div class="label">Cover</div><div class="value">800x538 · 170 KB</div></div>
|
||||
<div class="card"><div class="label">Organize</div><div class="value">Ichika Matsumoto<br>Sugar & Spice</div></div>
|
||||
<div class="actions"><span class="ghost">Import .nfo / JSON</span><span class="danger">Delete</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,311 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Pinkudex — Spacing Pass per Archetype (No Layout Changes)</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-bg-0: oklch(0.13 0.025 280);
|
||||
--color-bg-1: oklch(0.17 0.04 285);
|
||||
--color-bg-2: oklch(0.22 0.05 290);
|
||||
--color-fg: oklch(0.97 0.01 280);
|
||||
--color-fg-dim: oklch(0.72 0.025 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: color-mix(in oklch, white 6%, transparent);
|
||||
--color-glass-strong: color-mix(in oklch, white 10%, transparent);
|
||||
--color-glass-border: color-mix(in oklch, white 14%, transparent);
|
||||
--color-glass-border-strong: color-mix(in oklch, white 22%, transparent);
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-violet: oklch(0.72 0.22 305);
|
||||
--color-mint: oklch(0.80 0.16 155);
|
||||
--color-coral: oklch(0.72 0.20 25);
|
||||
|
||||
--spacing-card: 15px;
|
||||
--spacing-card-gap: 9px;
|
||||
--spacing-section: 15px;
|
||||
--spacing-chip: 7px;
|
||||
--spacing-label: 7px;
|
||||
}
|
||||
body {
|
||||
background: var(--color-bg-0);
|
||||
color: var(--color-fg);
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
.glass { background: var(--color-glass); border: 1px solid var(--color-glass-border); }
|
||||
.glass-strong { background: var(--color-glass-strong); border: 1px solid var(--color-glass-border-strong); }
|
||||
.label-mono {
|
||||
font-family: ui-monospace, monospace; font-size: 10px;
|
||||
text-transform: uppercase; letter-spacing: 0.1em;
|
||||
color: var(--color-fg-muted);
|
||||
}
|
||||
.archetype-frame {
|
||||
background: oklch(0.10 0.025 280);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.archetype-header {
|
||||
padding: 14px 18px;
|
||||
background: oklch(0.20 0.04 285);
|
||||
border-bottom: 1px solid var(--color-glass-border);
|
||||
display: flex; align-items: baseline; justify-content: space-between;
|
||||
}
|
||||
.archetype-header h2 {
|
||||
font-size: 14px; font-weight: 600;
|
||||
font-family: ui-monospace, monospace;
|
||||
text-transform: uppercase; letter-spacing: 0.08em;
|
||||
color: var(--color-cyan);
|
||||
}
|
||||
.archetype-header .desc { font-size: 12px; color: var(--color-fg-muted); }
|
||||
.archetype-body { padding: 24px; }
|
||||
.why { font-size: 12px; color: var(--color-fg-muted); }
|
||||
.ba-grid {
|
||||
display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start;
|
||||
}
|
||||
.ba-label {
|
||||
font-family: ui-monospace, monospace; font-size: 11px;
|
||||
text-transform: uppercase; letter-spacing: 0.08em;
|
||||
color: var(--color-fg-muted);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.ba-label.after { color: var(--color-cyan); }
|
||||
.thumb {
|
||||
aspect-ratio: 800/538; border-radius: 6px;
|
||||
background: linear-gradient(135deg, oklch(0.30 0.05 290), oklch(0.20 0.04 280));
|
||||
border: 1px solid var(--color-glass-border);
|
||||
position: relative;
|
||||
}
|
||||
.thumb .id { position: absolute; bottom: 4px; left: 6px; font-family: ui-monospace, monospace; font-size: 9px; color: var(--color-cyan); font-weight: 600; }
|
||||
.stat-num { font-family: ui-monospace, monospace; font-size: 22px; font-weight: 600; tabular-nums: tabular-nums; }
|
||||
.stat-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-fg-muted); }
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
padding: 0 10px; height: 28px;
|
||||
border-radius: 999px; font-size: 11px; font-family: ui-monospace, monospace;
|
||||
text-transform: uppercase; letter-spacing: 0.06em;
|
||||
border: 1px solid var(--color-glass-border); color: var(--color-fg-muted);
|
||||
}
|
||||
.pill.on { color: var(--color-cyan); border-color: oklch(0.82 0.16 200 / 0.4); background: oklch(0.82 0.16 200 / 0.10); }
|
||||
.btn {
|
||||
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
||||
height: 32px; padding: 0 12px;
|
||||
border-radius: 8px; font-size: 12px;
|
||||
border: 1px solid var(--color-glass-border); background: var(--color-glass);
|
||||
color: var(--color-fg);
|
||||
}
|
||||
.btn-coral { color: var(--color-coral); background: oklch(0.72 0.20 25 / 0.10); border-color: oklch(0.72 0.20 25 / 0.30); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="space-y-8 max-w-[1600px] mx-auto p-6">
|
||||
|
||||
<header class="space-y-1">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Spacing-Only Pass · Per Archetype</h1>
|
||||
<p class="why max-w-[820px]">
|
||||
<strong class="text-[var(--color-fg)]">No layout changes</strong>. Each archetype is reproduced as-is from the actual app, then the AFTER column applies only the snug+1 spacing tokens — padding, gaps, margins. Same components, same columns, same visual hierarchy. Only the rhythm changes.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- ====================================================================
|
||||
1 · INDEX (home, /collection/[slug], /actress/[slug], etc.)
|
||||
Real layout: stats inline + UploadCard right · FilterBar · LetterBar · MasonryGrid
|
||||
OR an empty-state Panel when no items match.
|
||||
==================================================================== -->
|
||||
<section class="archetype-frame">
|
||||
<div class="archetype-header">
|
||||
<h2>1 · Index / Grid</h2>
|
||||
<span class="desc">Stats + upload (top) · FilterBar · LetterBar · Masonry — or empty-state Panel</span>
|
||||
</div>
|
||||
<div class="archetype-body space-y-6">
|
||||
<p class="why">Spacing changes here are limited: the masonry grid, FilterBar, and LetterBar already have their own (correct) rhythm. The only thing that adopts the new tokens is the <strong>empty-state Panel</strong> shown when nothing matches.</p>
|
||||
|
||||
<div class="ba-grid">
|
||||
<!-- BEFORE empty state -->
|
||||
<div>
|
||||
<div class="ba-label">BEFORE — empty state · <code class="text-[var(--color-cyan)]">p-16</code> + <code class="text-[var(--color-cyan)]">mb-3</code></div>
|
||||
<div class="glass rounded-2xl text-center" style="padding: 64px;">
|
||||
<div style="width: 40px; height: 40px; margin: 0 auto; border-radius: 999px; background: oklch(0.82 0.16 200 / 0.2); display: grid; place-items: center; color: var(--color-cyan); margin-bottom: 12px;">⊙</div>
|
||||
<h2 class="text-xl font-medium">Nothing Matches</h2>
|
||||
<p class="text-[var(--color-fg-dim)] mt-1">All filtered out — switch back to All.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- AFTER empty state -->
|
||||
<div>
|
||||
<div class="ba-label after">AFTER — Panel · <code class="text-[var(--color-cyan)]">p-card</code> + <code class="text-[var(--color-cyan)]">mb-label</code></div>
|
||||
<div class="glass rounded-2xl text-center" style="padding: var(--spacing-card);">
|
||||
<div style="width: 40px; height: 40px; margin: 0 auto; border-radius: 999px; background: oklch(0.82 0.16 200 / 0.2); display: grid; place-items: center; color: var(--color-cyan); margin-bottom: var(--spacing-label);">⊙</div>
|
||||
<h2 class="text-xl font-medium">Nothing Matches</h2>
|
||||
<p class="text-[var(--color-fg-dim)] mt-1">All filtered out — switch back to All.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="text-[11px] font-mono text-[var(--color-fg-muted)] space-y-0.5 list-disc list-inside">
|
||||
<li>FilterBar, LetterBar, MasonryGrid: <strong>NOT changed</strong> — they have their own rhythm</li>
|
||||
<li>only the empty-state card uses Panel tokens (one-line edit per page route)</li>
|
||||
<li>top stats / UploadCard section: <strong>NOT changed</strong> — page intro, not a panel</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================
|
||||
2 · DETAIL (already migrated — for reference)
|
||||
Real layout: 800px cover left + aside right with stacked Panels.
|
||||
==================================================================== -->
|
||||
<section class="archetype-frame">
|
||||
<div class="archetype-header">
|
||||
<h2>2 · Detail</h2>
|
||||
<span class="desc">Already shipped on /id/[code] — reference only</span>
|
||||
</div>
|
||||
<div class="archetype-body space-y-3">
|
||||
<p class="why">No further changes. Showing the spec for cross-checking.</p>
|
||||
<ul class="text-[11px] font-mono text-[var(--color-fg-muted)] space-y-0.5 list-disc list-inside">
|
||||
<li>aside = PanelStack with <code class="text-[var(--color-cyan)]">gap-card-gap</code> (9 px) between Panels</li>
|
||||
<li>each Panel = <code class="text-[var(--color-cyan)]">p-card</code> (15 px) interior</li>
|
||||
<li>identity Panel uses PanelSection internally with <code class="text-[var(--color-cyan)]">gap-section</code> (15 px)</li>
|
||||
<li>chip rows = <code class="text-[var(--color-cyan)]">gap-chip</code> (7 px)</li>
|
||||
<li>label → content = <code class="text-[var(--color-cyan)]">mb-label</code> (7 px)</li>
|
||||
<li>action bar (Edit / Import / Delete) lives outside Panels but inside the same PanelStack rhythm</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================
|
||||
3 · SETTINGS — sidebar nav + scrollable Card stack on right
|
||||
Real layout: <SettingsPanel> sidebar + content. Card helper renders
|
||||
glass rounded-2xl p-5 with a 'text-sm font-medium mb-3' header.
|
||||
==================================================================== -->
|
||||
<section class="archetype-frame">
|
||||
<div class="archetype-header">
|
||||
<h2>3 · Settings</h2>
|
||||
<span class="desc">Existing sidebar + Card stack · only Card padding/header spacing changes</span>
|
||||
</div>
|
||||
<div class="archetype-body space-y-4">
|
||||
<p class="why">Layout stays exactly as-is — sidebar nav on the left, stacked cards on the right. The only thing that changes is the <code class="text-[var(--color-cyan)]">Card</code> helper inside <code class="text-[var(--color-cyan)]">SettingsPanel.tsx</code>: <code class="text-[var(--color-cyan)]">p-5</code> → <code class="text-[var(--color-cyan)]">p-card</code> (20→15) and header margin <code class="text-[var(--color-cyan)]">mb-3</code> → <code class="text-[var(--color-cyan)]">mb-label</code> (12→7). Card-to-card stacking gap also tightens to <code class="text-[var(--color-cyan)]">gap-card-gap</code> (9 px) from the existing <code class="text-[var(--color-cyan)]">space-y-4</code> (16 px).</p>
|
||||
|
||||
<div class="ba-grid">
|
||||
<!-- BEFORE -->
|
||||
<div>
|
||||
<div class="ba-label">BEFORE — <code class="text-[var(--color-cyan)]">p-5 · mb-3 · space-y-4</code></div>
|
||||
<div style="display: flex; flex-direction: column; gap: 16px;">
|
||||
<div class="glass rounded-2xl" style="padding: 20px;">
|
||||
<div style="font-size: 14px; font-weight: 500; margin-bottom: 12px;">General</div>
|
||||
<div style="display: flex; flex-direction: column; gap: 12px;">
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: 8px;">Default sort</div>
|
||||
<div style="display: flex; gap: 6px; flex-wrap: wrap;">
|
||||
<span class="pill on">Newest ↓</span><span class="pill">Oldest</span><span class="pill">Title</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: 8px;">Fade transitions</div>
|
||||
<div style="display: flex; gap: 6px;"><span class="pill on">On</span><span class="pill">Off</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass rounded-2xl" style="padding: 20px;">
|
||||
<div style="font-size: 14px; font-weight: 500; margin-bottom: 12px;">Display</div>
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: 8px;">Cover columns</div>
|
||||
<div style="display: flex; gap: 6px;"><span class="pill">2</span><span class="pill on">3</span><span class="pill">4</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- AFTER -->
|
||||
<div>
|
||||
<div class="ba-label after">AFTER — <code class="text-[var(--color-cyan)]">p-card · mb-label · gap-card-gap</code></div>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--spacing-card-gap);">
|
||||
<div class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<div style="font-size: 14px; font-weight: 500; margin-bottom: var(--spacing-label);">General</div>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--spacing-section);">
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: var(--spacing-label);">Default sort</div>
|
||||
<div style="display: flex; gap: var(--spacing-chip); flex-wrap: wrap;">
|
||||
<span class="pill on">Newest ↓</span><span class="pill">Oldest</span><span class="pill">Title</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: var(--spacing-label);">Fade transitions</div>
|
||||
<div style="display: flex; gap: var(--spacing-chip);"><span class="pill on">On</span><span class="pill">Off</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<div style="font-size: 14px; font-weight: 500; margin-bottom: var(--spacing-label);">Display</div>
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: var(--spacing-label);">Cover columns</div>
|
||||
<div style="display: flex; gap: var(--spacing-chip);"><span class="pill">2</span><span class="pill on">3</span><span class="pill">4</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="text-[11px] font-mono text-[var(--color-fg-muted)] space-y-0.5 list-disc list-inside">
|
||||
<li>same Card helper, same sidebar — only the spacing constants change</li>
|
||||
<li><code class="text-[var(--color-cyan)]">p-5</code> (20px) → <code class="text-[var(--color-cyan)]">p-card</code> (15px)</li>
|
||||
<li><code class="text-[var(--color-cyan)]">mb-3</code> (12px) → <code class="text-[var(--color-cyan)]">mb-label</code> (7px) on the title</li>
|
||||
<li><code class="text-[var(--color-cyan)]">space-y-4</code> (16px) → <code class="text-[var(--color-cyan)]">gap-card-gap</code> (9px) between Cards</li>
|
||||
<li>internal <code class="text-[var(--color-cyan)]">space-y-3</code> blocks → <code class="text-[var(--color-cyan)]">gap-section</code> (15px) — wider, since these are full sub-sections</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================
|
||||
4 · LIST (trash, queue) — header card + grid/list rows.
|
||||
Real trash layout: header strip with title + close, then the body
|
||||
is either an empty state Panel or a TrashGrid (uses MasonryGrid).
|
||||
Real queue layout: header summary + list rows.
|
||||
Spacing-only tweak: empty-state Panel padding + outer body padding.
|
||||
==================================================================== -->
|
||||
<section class="archetype-frame">
|
||||
<div class="archetype-header">
|
||||
<h2>4 · List (trash, queue)</h2>
|
||||
<span class="desc">Header strip + body · only empty-state Panel padding changes</span>
|
||||
</div>
|
||||
<div class="archetype-body space-y-4">
|
||||
<p class="why">Same layout as today: header at the top of the modal/page, scrollable body below. Body is either an empty-state Panel or a grid. Only the empty-state Panel adopts the new tokens (<code class="text-[var(--color-cyan)]">p-16</code> → <code class="text-[var(--color-cyan)]">p-card</code>). Header strip and grid stay untouched.</p>
|
||||
|
||||
<div class="ba-grid">
|
||||
<!-- BEFORE -->
|
||||
<div>
|
||||
<div class="ba-label">BEFORE — empty trash · <code class="text-[var(--color-cyan)]">p-16</code></div>
|
||||
<div class="glass rounded-2xl text-center max-w-md mx-auto" style="padding: 64px;">
|
||||
<div style="width: 40px; height: 40px; margin: 0 auto; border-radius: 999px; background: oklch(0.20 0.04 285); display: grid; place-items: center; color: var(--color-fg-dim); margin-bottom: 12px;">🗑</div>
|
||||
<h3 class="text-xl font-medium">Trash Is Empty</h3>
|
||||
<p class="text-[var(--color-fg-dim)] mt-1 text-sm">Deleted images appear here for 30 days before they're gone for good.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- AFTER -->
|
||||
<div>
|
||||
<div class="ba-label after">AFTER — Panel · <code class="text-[var(--color-cyan)]">p-card</code></div>
|
||||
<div class="glass rounded-2xl text-center max-w-md mx-auto" style="padding: var(--spacing-card);">
|
||||
<div style="width: 40px; height: 40px; margin: 0 auto; border-radius: 999px; background: oklch(0.20 0.04 285); display: grid; place-items: center; color: var(--color-fg-dim); margin-bottom: var(--spacing-label);">🗑</div>
|
||||
<h3 class="text-xl font-medium">Trash Is Empty</h3>
|
||||
<p class="text-[var(--color-fg-dim)] mt-1 text-sm">Deleted images appear here for 30 days before they're gone for good.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="text-[11px] font-mono text-[var(--color-fg-muted)] space-y-0.5 list-disc list-inside">
|
||||
<li>trash modal header strip: <strong>NOT changed</strong></li>
|
||||
<li>TrashGrid / queue rows: <strong>NOT changed</strong> — they have their own rhythm</li>
|
||||
<li>only the empty-state Panel inherits the tokens (one line per file)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="text-[11px] text-[var(--color-fg-muted)]">
|
||||
Net effect of this pass:
|
||||
<ul class="list-disc list-inside mt-1 space-y-0.5">
|
||||
<li>11 empty-state cards across the app: <code class="text-[var(--color-cyan)]">p-12</code>/<code class="text-[var(--color-cyan)]">p-16</code> → <code class="text-[var(--color-cyan)]">p-card</code></li>
|
||||
<li>Settings panel Card helper: <code class="text-[var(--color-cyan)]">p-5</code> + <code class="text-[var(--color-cyan)]">mb-3</code> + <code class="text-[var(--color-cyan)]">space-y-4</code> → tokens</li>
|
||||
<li>Detail page: already shipped</li>
|
||||
<li>List (trash/queue) header strips, rows, grids: untouched</li>
|
||||
<li>FilterBar / LetterBar / MasonryGrid: untouched</li>
|
||||
</ul>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,509 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Pinkudex — Pagination behavior options (functional)</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0b0d10;
|
||||
--bg-1: #14171c;
|
||||
--bg-2: #1c2027;
|
||||
--fg: #e6e8ec;
|
||||
--fg-dim: #a4abb6;
|
||||
--fg-muted: #6b7380;
|
||||
--cyan: #22d3ee;
|
||||
--mint: #34d399;
|
||||
--amber: #fbbf24;
|
||||
--coral: #f87171;
|
||||
--violet: #a78bfa;
|
||||
--glass: rgba(255,255,255,0.04);
|
||||
--glass-border: rgba(255,255,255,0.10);
|
||||
--glass-border-strong: rgba(255,255,255,0.18);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body {
|
||||
background: var(--bg-0);
|
||||
color: var(--fg);
|
||||
font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI";
|
||||
min-height: 100vh;
|
||||
}
|
||||
.page { max-width: 1100px; margin: 0 auto; padding: 24px; }
|
||||
|
||||
header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px; gap: 24px; }
|
||||
h1 { font-weight: 500; font-size: 20px; margin: 0 0 4px; }
|
||||
.sub { color: var(--fg-muted); font-size: 13px; }
|
||||
|
||||
.modes {
|
||||
display: flex; gap: 4px; padding: 4px;
|
||||
background: var(--bg-1); border: 1px solid var(--glass-border);
|
||||
border-radius: 10px;
|
||||
}
|
||||
.modes button {
|
||||
background: transparent; border: 0; color: var(--fg-dim);
|
||||
font-family: inherit; font-size: 12px; font-weight: 500;
|
||||
padding: 8px 14px; border-radius: 7px; cursor: pointer;
|
||||
transition: background 120ms, color 120ms;
|
||||
}
|
||||
.modes button:hover { color: var(--fg); }
|
||||
.modes button.active {
|
||||
background: rgba(34,211,238,0.15);
|
||||
color: var(--cyan);
|
||||
box-shadow: inset 0 0 0 1px rgba(34,211,238,0.4);
|
||||
}
|
||||
|
||||
/* Hud panel */
|
||||
.hud {
|
||||
display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;
|
||||
background: var(--bg-1); border: 1px solid var(--glass-border);
|
||||
border-radius: 12px; padding: 14px 16px; margin-bottom: 14px;
|
||||
font-family: ui-monospace, "SF Mono", monospace; font-size: 12px;
|
||||
}
|
||||
.hud .item { display: flex; flex-direction: column; gap: 4px; }
|
||||
.hud .label { color: var(--fg-muted); font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; }
|
||||
.hud .value { color: var(--fg); font-weight: 500; }
|
||||
.hud .value .accent { color: var(--cyan); }
|
||||
|
||||
.description {
|
||||
background: var(--bg-1); border: 1px solid var(--glass-border);
|
||||
border-radius: 12px; padding: 14px 16px; margin-bottom: 14px;
|
||||
font-size: 13px; color: var(--fg-dim); line-height: 1.55;
|
||||
}
|
||||
.description b { color: var(--fg); }
|
||||
|
||||
/* Event log */
|
||||
.log {
|
||||
background: rgba(0,0,0,0.35); border: 1px solid var(--glass-border);
|
||||
border-radius: 10px; padding: 10px 12px; margin-bottom: 14px;
|
||||
font-family: ui-monospace, "SF Mono", monospace; font-size: 11.5px;
|
||||
height: 110px; overflow-y: auto;
|
||||
}
|
||||
.log .line { padding: 1px 0; color: var(--fg-dim); }
|
||||
.log .line .t { color: var(--fg-muted); margin-right: 8px; }
|
||||
.log .line .k { font-weight: 600; margin-right: 8px; }
|
||||
.log .line .k.url { color: var(--coral); }
|
||||
.log .line .k.scroll { color: var(--mint); }
|
||||
.log .line .k.fetch { color: var(--amber); }
|
||||
.log .line .k.click { color: var(--cyan); }
|
||||
.log .line .k.remount { color: var(--violet); }
|
||||
.log .line .k.note { color: var(--fg-muted); }
|
||||
|
||||
/* Grid viewport — emulates a virtualized scroll area */
|
||||
.viewport {
|
||||
background: var(--bg-1); border: 1px solid var(--glass-border);
|
||||
border-radius: 14px; height: 540px; overflow-y: auto;
|
||||
scroll-behavior: smooth;
|
||||
position: relative;
|
||||
}
|
||||
.viewport-inner { padding: 16px; }
|
||||
|
||||
.grid {
|
||||
display: grid; grid-template-columns: repeat(5, 1fr);
|
||||
gap: 8px;
|
||||
}
|
||||
.card {
|
||||
aspect-ratio: 0.7; border-radius: 8px;
|
||||
background: linear-gradient(135deg, var(--bg-2), #232831);
|
||||
border: 1px solid var(--glass-border);
|
||||
display: flex; flex-direction: column; justify-content: flex-end;
|
||||
padding: 6px 8px;
|
||||
font-family: ui-monospace, monospace; font-size: 10px;
|
||||
color: var(--fg-muted);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
animation: fadeIn 200ms ease-out both;
|
||||
}
|
||||
.card::before {
|
||||
content: ""; position: absolute; inset: 0;
|
||||
background: radial-gradient(circle at 30% 30%, rgba(34,211,238,0.06), transparent 60%);
|
||||
}
|
||||
.card.flash { animation: flashCard 600ms ease-out; }
|
||||
@keyframes flashCard {
|
||||
0% { box-shadow: inset 0 0 0 2px var(--cyan); }
|
||||
100% { box-shadow: inset 0 0 0 0 transparent; }
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(4px); }
|
||||
to { opacity: 1; transform: none; }
|
||||
}
|
||||
|
||||
.page-marker {
|
||||
grid-column: 1 / -1;
|
||||
margin: 18px 0 8px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
background: rgba(34,211,238,0.06);
|
||||
border-left: 3px solid var(--cyan);
|
||||
font-family: ui-monospace, monospace; font-size: 11px;
|
||||
color: var(--cyan); font-weight: 600;
|
||||
text-transform: uppercase; letter-spacing: 0.08em;
|
||||
}
|
||||
.page-marker.current { background: rgba(34,211,238,0.14); }
|
||||
|
||||
/* Pagination bar */
|
||||
.bar {
|
||||
display: flex; justify-content: center; align-items: center; gap: 10px;
|
||||
margin-top: 16px; padding: 12px;
|
||||
background: var(--bg-1); border: 1px solid var(--glass-border);
|
||||
border-radius: 12px;
|
||||
}
|
||||
.bar button {
|
||||
font-family: inherit; font-size: 13px; padding: 7px 14px;
|
||||
border-radius: 8px; border: 1px solid var(--glass-border-strong);
|
||||
background: var(--glass); color: var(--fg); cursor: pointer;
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
transition: background 120ms;
|
||||
}
|
||||
.bar button:hover:not(:disabled) { background: rgba(255,255,255,0.07); }
|
||||
.bar button:disabled { opacity: 0.35; cursor: not-allowed; }
|
||||
.bar .label {
|
||||
font-family: ui-monospace, monospace; font-size: 13px;
|
||||
color: var(--fg-dim); padding: 0 14px; min-width: 200px; text-align: center;
|
||||
}
|
||||
.bar .label .num { color: var(--cyan); font-weight: 600; }
|
||||
.bar .jump {
|
||||
margin-left: 8px; display: inline-flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.bar .jump input {
|
||||
width: 56px; padding: 6px 8px; border-radius: 6px;
|
||||
border: 1px solid var(--glass-border-strong);
|
||||
background: var(--bg-0); color: var(--fg);
|
||||
font-family: ui-monospace, monospace; font-size: 12px; text-align: center;
|
||||
outline: none;
|
||||
}
|
||||
.bar .jump input:focus { border-color: var(--cyan); }
|
||||
|
||||
.load-more {
|
||||
width: 100%; margin-top: 10px;
|
||||
padding: 10px; border-radius: 8px;
|
||||
background: rgba(52,211,153,0.08); color: var(--mint);
|
||||
border: 1px dashed rgba(52,211,153,0.4); cursor: pointer;
|
||||
font-family: inherit; font-size: 12px;
|
||||
text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600;
|
||||
transition: background 120ms;
|
||||
}
|
||||
.load-more:hover { background: rgba(52,211,153,0.14); }
|
||||
.load-more:disabled { opacity: 0.3; cursor: not-allowed; }
|
||||
|
||||
.toast {
|
||||
position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
|
||||
background: var(--bg-2); border: 1px solid var(--glass-border-strong);
|
||||
border-radius: 10px; padding: 8px 14px;
|
||||
font-family: ui-monospace, monospace; font-size: 12px; color: var(--fg);
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.5);
|
||||
opacity: 0; pointer-events: none;
|
||||
transition: opacity 200ms, transform 200ms;
|
||||
z-index: 100;
|
||||
}
|
||||
.toast.show { opacity: 1; transform: translateX(-50%) translateY(-4px); }
|
||||
.toast .tag {
|
||||
display: inline-block; padding: 1px 6px; border-radius: 4px;
|
||||
font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;
|
||||
margin-right: 8px; font-weight: 700;
|
||||
}
|
||||
.toast.url .tag { background: rgba(248,113,113,0.2); color: var(--coral); }
|
||||
.toast.scroll .tag { background: rgba(52,211,153,0.2); color: var(--mint); }
|
||||
.toast.fetch .tag { background: rgba(251,191,36,0.2); color: var(--amber); }
|
||||
|
||||
.remount-flash {
|
||||
position: absolute; inset: 0; pointer-events: none;
|
||||
background: rgba(248,113,113,0.08);
|
||||
opacity: 0; transition: opacity 120ms;
|
||||
}
|
||||
.remount-flash.on { opacity: 1; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="page">
|
||||
<header>
|
||||
<div>
|
||||
<h1>Pagination behavior — functional comparison</h1>
|
||||
<div class="sub">Click <b>Prev / Next / Jump</b> with each mode active and watch the HUD + event log.</div>
|
||||
</div>
|
||||
<div class="modes" role="tablist">
|
||||
<button id="mode-1" class="active">1 · Always URL</button>
|
||||
<button id="mode-2">2 · Scroll + prefetch</button>
|
||||
<button id="mode-3">3 · Two affordances</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="hud">
|
||||
<div class="item">
|
||||
<span class="label">URL</span>
|
||||
<span class="value" id="hud-url">/?page=1</span>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span class="label">Logical page</span>
|
||||
<span class="value"><span class="accent" id="hud-page">1</span> / <span id="hud-total">12</span></span>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span class="label">Loaded buffer</span>
|
||||
<span class="value">page <span id="hud-buf-lo">1</span> – <span id="hud-buf-hi">1</span></span>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span class="label">DOM cards</span>
|
||||
<span class="value" id="hud-card-count">25</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="description" id="mode-desc"></div>
|
||||
|
||||
<div class="log" id="log"></div>
|
||||
|
||||
<div class="viewport" id="viewport">
|
||||
<div class="remount-flash" id="remount-flash"></div>
|
||||
<div class="viewport-inner" id="viewport-inner"></div>
|
||||
</div>
|
||||
|
||||
<div class="bar">
|
||||
<button id="prev-btn">← Prev</button>
|
||||
<span class="label">Page <span class="num" id="bar-page">1</span> of <span id="bar-total">12</span></span>
|
||||
<button id="next-btn">Next →</button>
|
||||
<form class="jump" id="jump-form" onsubmit="return false;">
|
||||
<span style="color: var(--fg-muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em;">Jump</span>
|
||||
<input id="jump-input" type="text" inputmode="numeric" placeholder="1-12">
|
||||
<button type="submit">→</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="load-more" id="load-more" style="display: none;">↓ Load more (extend buffer)</button>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast"></div>
|
||||
|
||||
<script>
|
||||
// ---------- model ----------
|
||||
const TOTAL_PAGES = 12;
|
||||
const PAGE_SIZE = 25; // cards per page (5×5)
|
||||
const FETCH_LATENCY_MS = 280;
|
||||
|
||||
let mode = 1; // 1 | 2 | 3
|
||||
let currentPage = 1; // logical page
|
||||
let initialPage = 1; // page that's anchored as the "SSR" first page
|
||||
let bufLo = 1; // first loaded page
|
||||
let bufHi = 1; // last loaded page
|
||||
let inflight = false;
|
||||
|
||||
// ---------- helpers ----------
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const viewportEl = $("viewport");
|
||||
const innerEl = $("viewport-inner");
|
||||
const logEl = $("log");
|
||||
|
||||
function fmtTime() {
|
||||
const d = new Date();
|
||||
return `${String(d.getHours()).padStart(2,"0")}:${String(d.getMinutes()).padStart(2,"0")}:${String(d.getSeconds()).padStart(2,"0")}.${String(d.getMilliseconds()).padStart(3,"0")}`;
|
||||
}
|
||||
function log(kind, msg) {
|
||||
const line = document.createElement("div");
|
||||
line.className = "line";
|
||||
line.innerHTML = `<span class="t">${fmtTime()}</span><span class="k ${kind}">${kind.toUpperCase()}</span>${msg}`;
|
||||
logEl.appendChild(line);
|
||||
logEl.scrollTop = logEl.scrollHeight;
|
||||
while (logEl.children.length > 50) logEl.removeChild(logEl.firstChild);
|
||||
}
|
||||
function toast(kind, msg) {
|
||||
const el = $("toast");
|
||||
el.className = `toast ${kind}`;
|
||||
el.innerHTML = `<span class="tag">${kind}</span>${msg}`;
|
||||
requestAnimationFrame(() => el.classList.add("show"));
|
||||
clearTimeout(toast._t);
|
||||
toast._t = setTimeout(() => el.classList.remove("show"), 1400);
|
||||
}
|
||||
|
||||
function buildCard(pageNum, idx) {
|
||||
const el = document.createElement("div");
|
||||
el.className = "card";
|
||||
el.dataset.page = String(pageNum);
|
||||
el.innerHTML = `<div>p${pageNum}·#${idx+1}</div>`;
|
||||
return el;
|
||||
}
|
||||
|
||||
function renderBuffer({ animate = false } = {}) {
|
||||
innerEl.innerHTML = "";
|
||||
const grid = document.createElement("div");
|
||||
grid.className = "grid";
|
||||
|
||||
for (let p = bufLo; p <= bufHi; p++) {
|
||||
const marker = document.createElement("div");
|
||||
marker.className = "page-marker" + (p === currentPage ? " current" : "");
|
||||
marker.textContent = `Page ${p}` + (p === currentPage ? " · current" : "");
|
||||
marker.dataset.pageMarker = String(p);
|
||||
grid.appendChild(marker);
|
||||
for (let i = 0; i < PAGE_SIZE; i++) {
|
||||
const c = buildCard(p, i);
|
||||
if (!animate) c.style.animation = "none";
|
||||
grid.appendChild(c);
|
||||
}
|
||||
}
|
||||
innerEl.appendChild(grid);
|
||||
updateHud();
|
||||
}
|
||||
|
||||
function updateHud() {
|
||||
$("hud-url").textContent = currentPage === 1 ? "/" : `/?page=${currentPage}`;
|
||||
$("hud-page").textContent = String(currentPage);
|
||||
$("hud-total").textContent = String(TOTAL_PAGES);
|
||||
$("hud-buf-lo").textContent = String(bufLo);
|
||||
$("hud-buf-hi").textContent = String(bufHi);
|
||||
$("hud-card-count").textContent = String((bufHi - bufLo + 1) * PAGE_SIZE);
|
||||
$("bar-page").textContent = String(currentPage);
|
||||
$("bar-total").textContent = String(TOTAL_PAGES);
|
||||
$("prev-btn").disabled = currentPage <= 1;
|
||||
$("next-btn").disabled = currentPage >= TOTAL_PAGES;
|
||||
const lm = $("load-more");
|
||||
lm.disabled = bufHi >= TOTAL_PAGES;
|
||||
lm.textContent = bufHi >= TOTAL_PAGES
|
||||
? "(end of library)"
|
||||
: `↓ Load page ${bufHi + 1} (extend buffer)`;
|
||||
}
|
||||
|
||||
function flashRemount() {
|
||||
const f = $("remount-flash");
|
||||
f.classList.add("on");
|
||||
setTimeout(() => f.classList.remove("on"), 200);
|
||||
}
|
||||
|
||||
function flashPage(p) {
|
||||
[...innerEl.querySelectorAll(`.card[data-page="${p}"]`)].forEach((el) => {
|
||||
el.classList.remove("flash");
|
||||
void el.offsetWidth;
|
||||
el.classList.add("flash");
|
||||
});
|
||||
}
|
||||
|
||||
function scrollToPageMarker(p) {
|
||||
const m = innerEl.querySelector(`[data-page-marker="${p}"]`);
|
||||
if (!m) return false;
|
||||
const target = m.offsetTop - 12;
|
||||
viewportEl.scrollTo({ top: target, behavior: "smooth" });
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------- the three modes ----------
|
||||
|
||||
// Mode 1: always URL nav. Buffer is reset to {target}.
|
||||
function mode1Nav(target) {
|
||||
log("click", `Prev/Next clicked, target=${target}`);
|
||||
log("url", `router.push(?page=${target})`);
|
||||
log("remount", `LibraryGrid remounts (key includes effectivePage)`);
|
||||
flashRemount();
|
||||
initialPage = target;
|
||||
bufLo = target; bufHi = target;
|
||||
currentPage = target;
|
||||
renderBuffer({ animate: true });
|
||||
viewportEl.scrollTop = 0;
|
||||
toast("url", `Navigated to page ${target} (remount)`);
|
||||
}
|
||||
|
||||
// Mode 2: scroll if in buffer; else fetch+append; backward beyond initial → URL.
|
||||
async function mode2Nav(target) {
|
||||
log("click", `Prev/Next clicked, target=${target}`);
|
||||
|
||||
if (target < initialPage) {
|
||||
log("note", `target < initialPage (${target} < ${initialPage}) — fall back to URL`);
|
||||
log("url", `router.push(?page=${target})`);
|
||||
log("remount", `LibraryGrid remounts`);
|
||||
flashRemount();
|
||||
initialPage = target;
|
||||
bufLo = target; bufHi = target;
|
||||
currentPage = target;
|
||||
renderBuffer({ animate: true });
|
||||
viewportEl.scrollTop = 0;
|
||||
toast("url", `Backward across buffer → URL nav (page ${target})`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target > bufHi) {
|
||||
log("note", `target > bufHi (${target} > ${bufHi}) — prefetching`);
|
||||
if (inflight) return;
|
||||
inflight = true;
|
||||
for (let p = bufHi + 1; p <= target; p++) {
|
||||
log("fetch", `fetch page ${p}…`);
|
||||
await new Promise((r) => setTimeout(r, FETCH_LATENCY_MS));
|
||||
bufHi = p;
|
||||
renderBuffer({ animate: false });
|
||||
log("note", `appended page ${p}, buffer now ${bufLo}–${bufHi}`);
|
||||
}
|
||||
inflight = false;
|
||||
}
|
||||
|
||||
currentPage = target;
|
||||
log("scroll", `scrollToIndex(rowOf(page ${target}))`);
|
||||
renderBuffer({ animate: false });
|
||||
requestAnimationFrame(() => scrollToPageMarker(target));
|
||||
flashPage(target);
|
||||
toast("scroll", `Scrolled to page ${target} (in-buffer)`);
|
||||
}
|
||||
|
||||
// Mode 3: Prev/Next is always URL. Load-more button does the buffer growth.
|
||||
function mode3Nav(target) {
|
||||
// Same as mode 1 for Prev/Next.
|
||||
mode1Nav(target);
|
||||
}
|
||||
async function mode3LoadMore() {
|
||||
if (bufHi >= TOTAL_PAGES) return;
|
||||
log("click", "Load-more clicked");
|
||||
log("fetch", `fetch page ${bufHi + 1}…`);
|
||||
inflight = true;
|
||||
await new Promise((r) => setTimeout(r, FETCH_LATENCY_MS));
|
||||
bufHi += 1;
|
||||
inflight = false;
|
||||
renderBuffer({ animate: false });
|
||||
log("note", `appended page ${bufHi}, buffer now ${bufLo}–${bufHi}`);
|
||||
toast("fetch", `Buffer extended to page ${bufHi}`);
|
||||
}
|
||||
|
||||
function navTo(target) {
|
||||
target = Math.max(1, Math.min(TOTAL_PAGES, target));
|
||||
if (target === currentPage && target >= bufLo && target <= bufHi) return;
|
||||
if (mode === 1) mode1Nav(target);
|
||||
else if (mode === 2) mode2Nav(target);
|
||||
else mode3Nav(target);
|
||||
}
|
||||
|
||||
// ---------- mode switching ----------
|
||||
const MODE_DESC = {
|
||||
1: `<b>Mode 1 — Always URL.</b> Every Prev/Next click pushes a new URL. The grid
|
||||
remounts (red flash). Buffer always reduces to a single page. Predictable but you
|
||||
lose the smooth in-buffer jump.`,
|
||||
2: `<b>Mode 2 — Scroll + prefetch.</b> If the target is already in the buffer it
|
||||
scrolls (green flash). Past <code>bufHi</code> it fetches missing pages then scrolls.
|
||||
Backward past <code>initialPage</code> still falls back to URL nav (residual seam).
|
||||
Try Next a few times then Prev.`,
|
||||
3: `<b>Mode 3 — Two affordances.</b> Prev/Next behaves like Mode 1 (always URL).
|
||||
A separate <span style="color: var(--mint)">Load more</span> button below extends
|
||||
the buffer in place. Each affordance has exactly one behavior.`,
|
||||
};
|
||||
function setMode(n) {
|
||||
mode = n;
|
||||
document.querySelectorAll(".modes button").forEach((b) => b.classList.remove("active"));
|
||||
$(`mode-${n}`).classList.add("active");
|
||||
$("mode-desc").innerHTML = MODE_DESC[n];
|
||||
$("load-more").style.display = n === 3 ? "block" : "none";
|
||||
// Reset state on mode change so each demo starts clean.
|
||||
currentPage = 1; initialPage = 1; bufLo = 1; bufHi = 1;
|
||||
renderBuffer({ animate: true });
|
||||
viewportEl.scrollTop = 0;
|
||||
log("note", `Mode switched to ${n}`);
|
||||
}
|
||||
|
||||
// ---------- wire up ----------
|
||||
$("mode-1").addEventListener("click", () => setMode(1));
|
||||
$("mode-2").addEventListener("click", () => setMode(2));
|
||||
$("mode-3").addEventListener("click", () => setMode(3));
|
||||
$("prev-btn").addEventListener("click", () => navTo(currentPage - 1));
|
||||
$("next-btn").addEventListener("click", () => navTo(currentPage + 1));
|
||||
$("jump-form").addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
const v = Number($("jump-input").value);
|
||||
if (Number.isFinite(v) && v >= 1 && v <= TOTAL_PAGES) navTo(v);
|
||||
$("jump-input").value = "";
|
||||
});
|
||||
$("load-more").addEventListener("click", mode3LoadMore);
|
||||
|
||||
setMode(1);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,579 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Part Suffix Pattern UI — Mockups</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-violet: oklch(0.72 0.22 305);
|
||||
--color-mint: oklch(0.80 0.16 155);
|
||||
--color-coral: oklch(0.72 0.20 25);
|
||||
--color-amber: oklch(0.85 0.16 90);
|
||||
--color-bg: oklch(0.13 0.02 260);
|
||||
--color-bg-1: oklch(0.18 0.025 260);
|
||||
--color-bg-2: oklch(0.22 0.03 260);
|
||||
--color-fg: oklch(0.97 0.01 280);
|
||||
--color-fg-dim: oklch(0.72 0.025 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: color-mix(in oklch, white 6%, transparent);
|
||||
--color-glass-strong: color-mix(in oklch, white 10%, transparent);
|
||||
--color-glass-border: color-mix(in oklch, white 14%, transparent);
|
||||
--color-glass-border-strong: color-mix(in oklch, white 22%, transparent);
|
||||
}
|
||||
body { background: var(--color-bg); color: var(--color-fg); font-family: ui-sans-serif, system-ui, sans-serif; }
|
||||
.glass { background: var(--color-glass); backdrop-filter: blur(20px) saturate(140%); border: 1px solid var(--color-glass-border); }
|
||||
.glass-strong { background: color-mix(in oklch, white 10%, transparent); backdrop-filter: blur(24px); border: 1px solid var(--color-glass-border-strong); }
|
||||
.label-mono { font: 600 10px/1 ui-monospace, monospace; letter-spacing: 0.10em; text-transform: uppercase; color: var(--color-fg-muted); }
|
||||
.header { color: var(--color-cyan); font: 600 11px/1 ui-monospace, monospace; letter-spacing: 0.10em; text-transform: uppercase; }
|
||||
.why { color: var(--color-fg-muted); font-size: 12px; line-height: 1.5; }
|
||||
.frame { border: 1px solid var(--color-glass-border); border-radius: 18px; padding: 22px; background: linear-gradient(180deg, oklch(0.20 0.025 260) 0%, oklch(0.16 0.02 260) 100%); }
|
||||
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 2px 8px; border-radius: 999px;
|
||||
font: 600 10px/1.2 ui-monospace, monospace; letter-spacing: 0.06em; text-transform: uppercase;
|
||||
border: 1px solid;
|
||||
}
|
||||
.pill.cyan { background: oklch(0.82 0.16 200 / 0.12); border-color: oklch(0.82 0.16 200 / 0.40); color: var(--color-cyan); }
|
||||
.pill.violet { background: oklch(0.72 0.22 305 / 0.14); border-color: oklch(0.72 0.22 305 / 0.40); color: var(--color-violet); }
|
||||
.pill.mint { background: oklch(0.80 0.16 155 / 0.12); border-color: oklch(0.80 0.16 155 / 0.40); color: var(--color-mint); }
|
||||
.pill.amber { background: rgba(251,191,36,0.12); border-color: rgba(251,191,36,0.40); color: rgb(252 211 77); }
|
||||
.pill.muted { background: var(--color-glass); border-color: var(--color-glass-border); color: var(--color-fg-muted); }
|
||||
|
||||
.input {
|
||||
background: var(--color-glass);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
color: var(--color-fg);
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
font: 500 13px/1.4 ui-monospace, monospace;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
}
|
||||
.input:focus { border-color: var(--color-cyan); }
|
||||
|
||||
.icon-btn {
|
||||
width: 26px; height: 26px;
|
||||
display: grid; place-items: center;
|
||||
border-radius: 6px;
|
||||
background: var(--color-glass);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
color: var(--color-fg-muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
.icon-btn:hover { color: var(--color-fg); background: var(--color-glass-strong); }
|
||||
|
||||
.ok { color: var(--color-mint); }
|
||||
.miss { color: var(--color-fg-muted); }
|
||||
.bad { color: var(--color-coral); }
|
||||
.warn { color: rgb(252 211 77); }
|
||||
|
||||
.part-tab {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 6px 12px; border-radius: 8px;
|
||||
font: 600 12px/1 ui-monospace, monospace;
|
||||
border: 1px solid var(--color-glass-border);
|
||||
background: var(--color-glass);
|
||||
color: var(--color-fg-dim);
|
||||
cursor: pointer; user-select: none;
|
||||
}
|
||||
.part-tab.active {
|
||||
background: var(--color-cyan);
|
||||
color: black;
|
||||
border-color: var(--color-cyan);
|
||||
}
|
||||
.part-tab.variant {
|
||||
background: color-mix(in oklch, var(--color-violet) 14%, transparent);
|
||||
border-color: color-mix(in oklch, var(--color-violet) 35%, transparent);
|
||||
color: var(--color-violet);
|
||||
}
|
||||
.part-tab.variant.active {
|
||||
background: var(--color-violet);
|
||||
color: white;
|
||||
border-color: var(--color-violet);
|
||||
}
|
||||
|
||||
/* Multi-file record summary bar — visualizes part/variant ratio */
|
||||
.ratio-bar {
|
||||
display: flex; height: 4px; border-radius: 2px; overflow: hidden;
|
||||
background: var(--color-glass);
|
||||
}
|
||||
.ratio-bar > .seg-part { background: var(--color-cyan); }
|
||||
.ratio-bar > .seg-variant { background: var(--color-violet); }
|
||||
.ratio-bar > .seg-skip { background: var(--color-coral); }
|
||||
|
||||
/* Disclosure carat */
|
||||
details > summary { list-style: none; cursor: pointer; }
|
||||
details > summary::-webkit-details-marker { display: none; }
|
||||
details > summary .carat { display: inline-block; transition: transform 0.15s; }
|
||||
details[open] > summary .carat { transform: rotate(90deg); }
|
||||
|
||||
.row { display: grid; grid-template-columns: 22px 1fr auto; gap: 10px; align-items: center; padding: 4px 0; font: 500 12px/1.3 ui-monospace, monospace; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen">
|
||||
<main class="max-w-[1400px] mx-auto p-6 space-y-12">
|
||||
<header class="space-y-1">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Part Suffix Patterns — Settings & Player UI</h1>
|
||||
<p class="text-sm text-[var(--color-fg-dim)]">Updated: live preview scales to thousands of files via summary-first grouping; player C4 moves filename out of the strip.</p>
|
||||
</header>
|
||||
|
||||
<!-- =========================================================
|
||||
Section A: SETTINGS UI — three syntax styles (kept for reference)
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">A · Settings UI · Three syntax styles</div>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
|
||||
<div class="frame space-y-3">
|
||||
<div class="flex items-baseline justify-between">
|
||||
<h3 class="text-sm font-medium">A1 · Token-based <span class="text-[var(--color-cyan)] text-[10px] ml-1">RECOMMENDED</span></h3>
|
||||
</div>
|
||||
<p class="why">Use <code class="text-[var(--color-cyan)]">{N}</code> for digits, <code class="text-[var(--color-cyan)]">{L}</code> for a single letter. Anything else is a literal. Matches at the end of the filename, after the code.</p>
|
||||
<ol class="space-y-2">
|
||||
<li class="flex items-center gap-2"><input class="input" value="-cd{N}"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value=".part{N}"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value="_{N}"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value="_{L}"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
</ol>
|
||||
<div class="flex items-center gap-2">
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg glass">+ Add pattern</button>
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg glass text-[var(--color-fg-muted)]">Reset to defaults</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="frame space-y-3">
|
||||
<h3 class="text-sm font-medium">A2 · Glob-based</h3>
|
||||
<p class="why">Familiar shell-glob style. <code class="text-[var(--color-cyan)]">*</code> = anything, <code class="text-[var(--color-cyan)]">[0-9]</code> = digit, <code class="text-[var(--color-cyan)]">[A-Z]</code> = letter.</p>
|
||||
<ol class="space-y-2">
|
||||
<li class="flex items-center gap-2"><input class="input" value="*-cd[0-9]*"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value="*.part[0-9]*"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value="*_[0-9]"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value="*_[A-Z]"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
</ol>
|
||||
<div class="flex items-center gap-2">
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg glass">+ Add pattern</button>
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg glass text-[var(--color-fg-muted)]">Reset to defaults</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="frame space-y-3">
|
||||
<h3 class="text-sm font-medium">A3 · Raw regex</h3>
|
||||
<p class="why">Anchored to end of filename stem. The first capture group is the part identifier (used for sort order).</p>
|
||||
<ol class="space-y-2">
|
||||
<li class="flex items-center gap-2"><input class="input" value="-cd(\d+)$"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value="\.part(\d+)$"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value="_(\d+)$"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value="_([A-Z])$"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
</ol>
|
||||
<div class="flex items-center gap-2">
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg glass">+ Add pattern</button>
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg glass text-[var(--color-fg-muted)]">Reset to defaults</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
Section B: LIVE PREVIEW — at scale, two layouts
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">B · Live Preview · Scales to thousands of files</div>
|
||||
|
||||
<!-- B1. Summary-first with collapsible drill-down -->
|
||||
<div class="frame space-y-3">
|
||||
<div class="flex items-baseline justify-between">
|
||||
<h3 class="text-sm font-medium">B1 · Summary-first · default view</h3>
|
||||
<span class="text-[10px] font-mono text-[var(--color-fg-muted)]">2,047 files · 28 multi-file records</span>
|
||||
</div>
|
||||
<p class="why">Most rows in your library don’t affect this decision — single-file records classify trivially. Default view shows only multi-file records as collapsed groups, so you scan ~30 rows instead of 2,000. Click to drill in.</p>
|
||||
|
||||
<div class="flex items-center gap-2 pt-1">
|
||||
<input class="input flex-1" placeholder="Search code or filename…">
|
||||
<label class="flex items-center gap-1.5 text-xs text-[var(--color-fg-muted)]"><input type="checkbox" checked> only multi-file</label>
|
||||
<label class="flex items-center gap-1.5 text-xs text-[var(--color-fg-muted)]"><input type="checkbox"> mixed/ambiguous only</label>
|
||||
</div>
|
||||
|
||||
<div class="bg-black/40 rounded-lg p-3 max-h-[460px] overflow-auto space-y-1">
|
||||
|
||||
<!-- Group: YUJ-001 — all variants (problem case) -->
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-2 rounded hover:bg-[var(--color-glass)]">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[13px] text-[var(--color-cyan)]">YUJ-001</span>
|
||||
<div class="ratio-bar w-20"><div class="seg-variant" style="flex: 4 1 0%"></div></div>
|
||||
<span class="text-[11px] font-mono text-[var(--color-fg-muted)]">4 files</span>
|
||||
<span class="ml-auto flex gap-1.5">
|
||||
<span class="pill violet">4 var</span>
|
||||
</span>
|
||||
</summary>
|
||||
<div class="pl-7 pb-2 pt-1 space-y-0.5">
|
||||
<div class="row"><span class="miss">—</span><span>YUJ-001 - Ichika Matsumoto [1080p].mp4</span><span class="pill violet">Variant</span></div>
|
||||
<div class="row"><span class="miss">—</span><span>YUJ-001 - Ichika Matsumoto [1080p].fixed.mp4</span><span class="pill violet">Variant</span></div>
|
||||
<div class="row"><span class="miss">—</span><span>YUJ-001 - Ichika Matsumoto [1080p].nobf.mp4</span><span class="pill violet">Variant</span></div>
|
||||
<div class="row"><span class="miss">—</span><span>YUJ-001 - Ichika Matsumoto [1080p].restored.mp4</span><span class="pill violet">Variant</span></div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Group: IBW-349 — proper parts (expanded for visual) -->
|
||||
<details open>
|
||||
<summary class="flex items-center gap-2 px-2 py-2 rounded hover:bg-[var(--color-glass)]">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[13px] text-[var(--color-cyan)]">IBW-349</span>
|
||||
<div class="ratio-bar w-20"><div class="seg-part" style="flex: 2 1 0%"></div></div>
|
||||
<span class="text-[11px] font-mono text-[var(--color-fg-muted)]">2 files</span>
|
||||
<span class="ml-auto flex gap-1.5">
|
||||
<span class="pill cyan">2 part</span>
|
||||
</span>
|
||||
</summary>
|
||||
<div class="pl-7 pb-2 pt-1 space-y-0.5">
|
||||
<div class="row"><span class="ok">✓</span><span>IBW-349-cd1.mp4</span><span class="pill cyan">Part 1</span></div>
|
||||
<div class="row"><span class="ok">✓</span><span>IBW-349-cd2.mp4</span><span class="pill cyan">Part 2</span></div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Group: IBW-341 -->
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-2 rounded hover:bg-[var(--color-glass)]">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[13px] text-[var(--color-cyan)]">IBW-341</span>
|
||||
<div class="ratio-bar w-20"><div class="seg-part" style="flex: 2 1 0%"></div></div>
|
||||
<span class="text-[11px] font-mono text-[var(--color-fg-muted)]">2 files</span>
|
||||
<span class="ml-auto flex gap-1.5">
|
||||
<span class="pill cyan">2 part</span>
|
||||
</span>
|
||||
</summary>
|
||||
</details>
|
||||
|
||||
<!-- Group: IBW-342 -->
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-2 rounded hover:bg-[var(--color-glass)]">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[13px] text-[var(--color-cyan)]">IBW-342</span>
|
||||
<div class="ratio-bar w-20"><div class="seg-part" style="flex: 2 1 0%"></div></div>
|
||||
<span class="text-[11px] font-mono text-[var(--color-fg-muted)]">2 files</span>
|
||||
<span class="ml-auto flex gap-1.5">
|
||||
<span class="pill cyan">2 part</span>
|
||||
</span>
|
||||
</summary>
|
||||
</details>
|
||||
|
||||
<!-- Group: IBW-348 (lettered) -->
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-2 rounded hover:bg-[var(--color-glass)]">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[13px] text-[var(--color-cyan)]">IBW-348</span>
|
||||
<div class="ratio-bar w-20"><div class="seg-part" style="flex: 2 1 0%"></div></div>
|
||||
<span class="text-[11px] font-mono text-[var(--color-fg-muted)]">2 files</span>
|
||||
<span class="ml-auto flex gap-1.5">
|
||||
<span class="pill cyan">2 part</span>
|
||||
</span>
|
||||
</summary>
|
||||
</details>
|
||||
|
||||
<!-- Group: AMBIGUOUS / mixed (highlighted) -->
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-2 rounded hover:bg-[var(--color-glass)] border border-amber-400/30 bg-amber-400/5">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[13px] text-[var(--color-cyan)]">SDDE-617</span>
|
||||
<div class="ratio-bar w-20"><div class="seg-part" style="flex: 2 1 0%"></div><div class="seg-variant" style="flex: 1 1 0%"></div></div>
|
||||
<span class="text-[11px] font-mono text-[var(--color-fg-muted)]">3 files</span>
|
||||
<span class="ml-auto flex gap-1.5">
|
||||
<span class="pill cyan">2 part</span>
|
||||
<span class="pill violet">1 var</span>
|
||||
<span class="pill amber">⚠ mixed</span>
|
||||
</span>
|
||||
</summary>
|
||||
<div class="pl-7 pb-2 pt-1 space-y-0.5">
|
||||
<div class="row"><span class="ok">✓</span><span>SDDE-617-cd1.mp4</span><span class="pill cyan">Part 1</span></div>
|
||||
<div class="row"><span class="ok">✓</span><span>SDDE-617-cd2.mp4</span><span class="pill cyan">Part 2</span></div>
|
||||
<div class="row"><span class="miss">—</span><span>SDDE-617.restored.mp4</span><span class="pill violet">Variant</span></div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Collapsed remaining groups -->
|
||||
<div class="text-[11px] text-[var(--color-fg-muted)] italic px-2 py-2">… 22 more multi-file records</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats footer -->
|
||||
<div class="grid grid-cols-4 gap-3 pt-2 border-t border-[var(--color-glass-border)] text-[12px] font-mono">
|
||||
<div class="space-y-0.5">
|
||||
<div class="label-mono">Total scanned</div>
|
||||
<div>2,047 files</div>
|
||||
</div>
|
||||
<div class="space-y-0.5">
|
||||
<div class="label-mono">Multi-file records</div>
|
||||
<div><span class="text-[var(--color-cyan)]">24 part groups</span> · <span class="text-[var(--color-violet)]">18 variant groups</span></div>
|
||||
</div>
|
||||
<div class="space-y-0.5">
|
||||
<div class="label-mono">Standalone</div>
|
||||
<div>1,949 files</div>
|
||||
</div>
|
||||
<div class="space-y-0.5">
|
||||
<div class="label-mono"><span class="warn">⚠ Mixed</span></div>
|
||||
<div><span class="warn">3 records</span> · review</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- B2. Stats-only (compact alternative) -->
|
||||
<div class="frame space-y-3">
|
||||
<div class="flex items-baseline justify-between">
|
||||
<h3 class="text-sm font-medium">B2 · Stats-only · alternative for small panels</h3>
|
||||
</div>
|
||||
<p class="why">If the settings panel is cramped, skip the file list entirely and show per-pattern hit counts. Click any pattern to see the matching files in a side drawer.</p>
|
||||
<div class="bg-black/40 rounded-lg p-4 space-y-3">
|
||||
<div class="flex items-center gap-3 py-2 border-b border-[var(--color-glass-border)]">
|
||||
<code class="text-[13px] text-[var(--color-cyan)] font-mono w-[120px]">-cd{N}</code>
|
||||
<div class="ratio-bar flex-1 h-1.5"><div class="seg-part" style="flex: 14 1 0%"></div><div class="seg-skip" style="flex: 0 1 0%"></div></div>
|
||||
<span class="font-mono text-xs"><span class="ok">14 matches</span> · 7 records</span>
|
||||
<button class="text-[10px] text-[var(--color-cyan)] hover:underline">view files →</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 py-2 border-b border-[var(--color-glass-border)]">
|
||||
<code class="text-[13px] text-[var(--color-cyan)] font-mono w-[120px]">.part{N}</code>
|
||||
<div class="ratio-bar flex-1 h-1.5"><div class="seg-part" style="flex: 6 1 0%"></div></div>
|
||||
<span class="font-mono text-xs"><span class="ok">6 matches</span> · 3 records</span>
|
||||
<button class="text-[10px] text-[var(--color-cyan)] hover:underline">view files →</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 py-2 border-b border-[var(--color-glass-border)]">
|
||||
<code class="text-[13px] text-[var(--color-cyan)] font-mono w-[120px]">_{N}</code>
|
||||
<div class="ratio-bar flex-1 h-1.5"><div class="seg-part" style="flex: 22 1 0%"></div></div>
|
||||
<span class="font-mono text-xs"><span class="ok">22 matches</span> · 11 records</span>
|
||||
<button class="text-[10px] text-[var(--color-cyan)] hover:underline">view files →</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 py-2">
|
||||
<code class="text-[13px] text-[var(--color-cyan)] font-mono w-[120px]">_{L}</code>
|
||||
<div class="ratio-bar flex-1 h-1.5"><div class="seg-part" style="flex: 6 1 0%"></div></div>
|
||||
<span class="font-mono text-xs"><span class="ok">6 matches</span> · 3 records</span>
|
||||
<button class="text-[10px] text-[var(--color-cyan)] hover:underline">view files →</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 py-2 border-t border-[var(--color-glass-border)] pt-3 text-[var(--color-violet)]">
|
||||
<code class="text-[13px] font-mono w-[120px]">unmatched</code>
|
||||
<div class="ratio-bar flex-1 h-1.5"><div class="seg-variant" style="flex: 87 1 0%"></div></div>
|
||||
<span class="font-mono text-xs">87 files · 18 records (variants)</span>
|
||||
<button class="text-[10px] text-[var(--color-violet)] hover:underline">view files →</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">Stats-only fits in narrow panels. The grouped view (B1) is better for diagnosing why a record didn’t classify the way you expected.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
Section C: PLAYER UI — C4 with filename in header
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">C · Player UI · C4 with filename moved to header</div>
|
||||
|
||||
<!-- C4 redesigned — filename in player header -->
|
||||
<div class="frame space-y-3">
|
||||
<div class="flex items-baseline justify-between">
|
||||
<h3 class="text-sm font-medium">C4 · Parts strip + variants dropdown · filename in player header</h3>
|
||||
<span class="text-[10px] font-mono text-[var(--color-fg-muted)]">SDDE-617 · 2 parts · 1 variant</span>
|
||||
</div>
|
||||
|
||||
<!-- Mock player header -->
|
||||
<div class="rounded-lg border border-[var(--color-glass-border-strong)] overflow-hidden">
|
||||
<!-- Header row -->
|
||||
<div class="flex items-center justify-between px-5 py-3 border-b border-[var(--color-glass-border)] bg-[var(--color-bg-1)]">
|
||||
<div class="min-w-0">
|
||||
<div class="text-[10px] uppercase tracking-wider font-mono text-[var(--color-fg-muted)]">Playing</div>
|
||||
<div class="flex items-baseline gap-2 min-w-0">
|
||||
<span class="font-mono text-[var(--color-cyan)] font-semibold shrink-0">SDDE-617</span>
|
||||
<span class="text-sm text-[var(--color-fg-dim)] truncate">Some Actress, Other Actress</span>
|
||||
</div>
|
||||
<!-- Filename moved here -->
|
||||
<div class="font-mono text-[11px] text-[var(--color-fg-muted)] truncate mt-0.5" title="SDDE-617-cd1.mp4">
|
||||
<span class="text-[var(--color-cyan)]">▶</span> SDDE-617-cd1.mp4
|
||||
<span class="ml-1 opacity-60">· 4.27 GB · 1080p H.264</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="pill cyan">▷ DIRECT</span>
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg glass">Reveal in Folder</button>
|
||||
<button class="p-1 rounded glass">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Video area placeholder -->
|
||||
<div class="bg-black aspect-video grid place-items-center text-[var(--color-fg-muted)] text-xs font-mono">
|
||||
[ video element ]
|
||||
</div>
|
||||
|
||||
<!-- Controls strip — parts + variants dropdown -->
|
||||
<div class="px-5 py-2 border-t border-[var(--color-glass-border)] flex items-center gap-2 bg-[var(--color-bg-1)]">
|
||||
<span class="label-mono">Parts</span>
|
||||
<button class="part-tab active">1</button>
|
||||
<button class="part-tab">2</button>
|
||||
|
||||
<!-- Right side: variants dropdown -->
|
||||
<div class="ml-auto relative">
|
||||
<button class="part-tab variant">⋯ 1 variant ▾</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="why"><strong>What changed</strong>: filename moved from the parts strip into the player header (third line under code/actresses), prefixed with <span class="text-[var(--color-cyan)]">▶</span> to mark the active file. Adds tech badges (size, resolution, codec) inline. The strip below becomes purely navigation. Truncates with ellipsis on narrow viewports; full filename available via title-tooltip on hover.</p>
|
||||
</div>
|
||||
|
||||
<!-- Variant: with both parts and variants present, dropdown open -->
|
||||
<div class="frame space-y-3">
|
||||
<div class="flex items-baseline justify-between">
|
||||
<h3 class="text-sm font-medium">C4-open · variants dropdown expanded</h3>
|
||||
<span class="text-[10px] font-mono text-[var(--color-fg-muted)]">user clicked the variants dropdown</span>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border border-[var(--color-glass-border-strong)] overflow-hidden">
|
||||
<div class="flex items-center justify-between px-5 py-3 border-b border-[var(--color-glass-border)] bg-[var(--color-bg-1)]">
|
||||
<div class="min-w-0">
|
||||
<div class="text-[10px] uppercase tracking-wider font-mono text-[var(--color-fg-muted)]">Playing</div>
|
||||
<div class="flex items-baseline gap-2 min-w-0">
|
||||
<span class="font-mono text-[var(--color-cyan)] font-semibold shrink-0">YUJ-001</span>
|
||||
<span class="text-sm text-[var(--color-fg-dim)] truncate">Ichika Matsumoto</span>
|
||||
</div>
|
||||
<div class="font-mono text-[11px] text-[var(--color-fg-muted)] truncate mt-0.5">
|
||||
<span class="text-[var(--color-violet)]">▶</span> YUJ-001 - Ichika Matsumoto [1080p].fixed.mp4
|
||||
<span class="ml-1 opacity-60">· 4.90 GB · 1080p H.264 · with B-frames</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="pill cyan">⚙ TRANSCODE</span>
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg glass">Reveal in Folder</button>
|
||||
<button class="p-1 rounded glass">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-black aspect-video grid place-items-center text-[var(--color-fg-muted)] text-xs font-mono">
|
||||
[ video element ]
|
||||
</div>
|
||||
|
||||
<div class="px-5 py-2 border-t border-[var(--color-glass-border)] flex items-center gap-2 bg-[var(--color-bg-1)]">
|
||||
<span class="label-mono">Variants</span>
|
||||
<span class="text-[11px] font-mono text-[var(--color-fg-muted)] italic">no parts on this record</span>
|
||||
|
||||
<div class="ml-auto relative">
|
||||
<button class="part-tab variant active">.fixed ▾</button>
|
||||
<div class="absolute right-0 top-full mt-1 glass-strong rounded-lg p-1 w-[260px] shadow-2xl" style="z-index:5;">
|
||||
<div class="px-2 pt-1 pb-2 text-[10px] label-mono border-b border-[var(--color-glass-border)]">Switch encode</div>
|
||||
<button class="w-full text-left px-2 py-1.5 rounded text-[12px] font-mono hover:bg-[var(--color-glass)] flex items-center justify-between">
|
||||
<span>original .mp4</span>
|
||||
<span class="text-[10px] text-[var(--color-fg-muted)]">5.25 GB</span>
|
||||
</button>
|
||||
<button class="w-full text-left px-2 py-1.5 rounded text-[12px] font-mono bg-[var(--color-violet)]/15 text-[var(--color-violet)] flex items-center justify-between">
|
||||
<span>.fixed ●</span>
|
||||
<span class="text-[10px] opacity-80">5.25 GB</span>
|
||||
</button>
|
||||
<button class="w-full text-left px-2 py-1.5 rounded text-[12px] font-mono hover:bg-[var(--color-glass)] flex items-center justify-between">
|
||||
<span>.nobf</span>
|
||||
<span class="text-[10px] text-[var(--color-fg-muted)]">6.96 GB</span>
|
||||
</button>
|
||||
<button class="w-full text-left px-2 py-1.5 rounded text-[12px] font-mono hover:bg-[var(--color-glass)] flex items-center justify-between">
|
||||
<span>.restored</span>
|
||||
<span class="text-[10px] text-[var(--color-fg-muted)]">4.02 GB</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="why">Dropdown open: each variant labeled by its suffix, file size shown so you can pick the right one. The bullet (●) marks the currently-selected variant. When a record has no real parts (like YUJ-001), the parts strip shows an italic placeholder rather than empty space.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- =========================================================
|
||||
Section D: full settings panel composition (refreshed)
|
||||
========================================================= -->
|
||||
<section class="space-y-3">
|
||||
<div class="header">D · Full settings panel · token editor + scaled preview</div>
|
||||
<div class="frame">
|
||||
<div class="grid grid-cols-[1fr_1.4fr] gap-6">
|
||||
<div class="space-y-3">
|
||||
<div class="space-y-1">
|
||||
<h3 class="text-sm font-medium">Part Suffix Patterns</h3>
|
||||
<p class="why">Files matching get grouped as sequential <span class="pill cyan inline-flex">Parts</span>. Files with no match are <span class="pill violet inline-flex">Variants</span> — alternate encodes of the same content. Use <code class="text-[var(--color-cyan)]">{N}</code> for a number, <code class="text-[var(--color-cyan)]">{L}</code> for a single letter. Match is anchored to the end of the filename, after the code.</p>
|
||||
</div>
|
||||
<ol class="space-y-2">
|
||||
<li class="flex items-center gap-2"><input class="input" value="-cd{N}"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value=".part{N}"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value="_{N}"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
<li class="flex items-center gap-2"><input class="input" value="_{L}"><button class="icon-btn">↑</button><button class="icon-btn">↓</button><button class="icon-btn">✕</button></li>
|
||||
</ol>
|
||||
<div class="flex items-center gap-2">
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg glass">+ Add pattern</button>
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg glass text-[var(--color-fg-muted)]">Reset</button>
|
||||
<button class="text-xs px-3 py-1.5 rounded-lg bg-[var(--color-cyan)] text-black font-medium ml-auto">Save & Rescan</button>
|
||||
</div>
|
||||
<div class="text-[11px] font-mono text-[var(--color-fg-muted)] pt-3 border-t border-[var(--color-glass-border)]">
|
||||
Advanced: <button class="text-[var(--color-cyan)] hover:underline">Switch to raw regex</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-baseline justify-between">
|
||||
<h3 class="text-sm font-medium">Live preview</h3>
|
||||
<span class="text-[10px] font-mono text-[var(--color-fg-muted)]">2,047 files · 28 multi-file records</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="input flex-1" placeholder="Search code or filename…">
|
||||
<label class="flex items-center gap-1.5 text-[11px] text-[var(--color-fg-muted)] whitespace-nowrap"><input type="checkbox" checked> only multi-file</label>
|
||||
</div>
|
||||
<div class="bg-black/40 rounded-lg p-2 space-y-1 max-h-[260px] overflow-auto">
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-1.5 rounded hover:bg-[var(--color-glass)]">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[12px] text-[var(--color-cyan)]">YUJ-001</span>
|
||||
<span class="ml-auto pill violet">4 var</span>
|
||||
</summary>
|
||||
</details>
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-1.5 rounded hover:bg-[var(--color-glass)]">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[12px] text-[var(--color-cyan)]">IBW-349</span>
|
||||
<span class="ml-auto pill cyan">2 part</span>
|
||||
</summary>
|
||||
</details>
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-1.5 rounded hover:bg-[var(--color-glass)]">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[12px] text-[var(--color-cyan)]">IBW-341</span>
|
||||
<span class="ml-auto pill cyan">2 part</span>
|
||||
</summary>
|
||||
</details>
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-1.5 rounded hover:bg-[var(--color-glass)]">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[12px] text-[var(--color-cyan)]">IBW-342</span>
|
||||
<span class="ml-auto pill cyan">2 part</span>
|
||||
</summary>
|
||||
</details>
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-1.5 rounded hover:bg-[var(--color-glass)]">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[12px] text-[var(--color-cyan)]">IBW-348</span>
|
||||
<span class="ml-auto pill cyan">2 part</span>
|
||||
</summary>
|
||||
</details>
|
||||
<details>
|
||||
<summary class="flex items-center gap-2 px-2 py-1.5 rounded hover:bg-[var(--color-glass)] border border-amber-400/30 bg-amber-400/5">
|
||||
<span class="carat text-[var(--color-fg-muted)]">▶</span>
|
||||
<span class="font-mono text-[12px] text-[var(--color-cyan)]">SDDE-617</span>
|
||||
<span class="ml-auto flex gap-1"><span class="pill cyan">2 part</span><span class="pill violet">1 var</span></span>
|
||||
</summary>
|
||||
</details>
|
||||
<div class="text-[11px] text-[var(--color-fg-muted)] italic px-2 py-1.5">… 22 more</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-2 text-[11px] font-mono pt-1">
|
||||
<div><span class="text-[var(--color-cyan)]">24</span> part groups</div>
|
||||
<div><span class="text-[var(--color-violet)]">18</span> variant groups</div>
|
||||
<div><span class="warn">⚠ 3</span> mixed (review)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="why">This is the production shape. Token editor on the left, summary preview on the right. Mixed records flagged amber so they bubble to the user's attention. “Save & Rescan” commits the patterns and triggers a video index refresh.</p>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,362 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Play Button Mockups — v7 Variants</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-bg: oklch(0.18 0.02 260);
|
||||
--color-card: oklch(0.22 0.025 260);
|
||||
--color-card-2: oklch(0.16 0.02 260);
|
||||
}
|
||||
body { background: var(--color-bg); color: #e5e7eb; font-family: ui-sans-serif, system-ui, sans-serif; }
|
||||
.card {
|
||||
position: relative;
|
||||
aspect-ratio: 4 / 3;
|
||||
border-radius: 14px;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(120% 80% at 50% 30%, oklch(0.28 0.03 260) 0%, var(--color-card) 60%, var(--color-card-2) 100%);
|
||||
border: 1px solid oklch(1 0 0 / 0.06);
|
||||
}
|
||||
.id-bg {
|
||||
position: absolute; inset: 0;
|
||||
display: grid; place-items: center;
|
||||
font-weight: 700;
|
||||
font-size: clamp(48px, 9vw, 88px);
|
||||
color: oklch(0.55 0.01 260 / 0.45);
|
||||
letter-spacing: 0.02em;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
.vip {
|
||||
position: absolute; top: 10px; left: 10px;
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 3px 9px;
|
||||
border-radius: 999px;
|
||||
background: rgba(0,0,0,0.8);
|
||||
color: var(--color-cyan);
|
||||
border: 1px solid color-mix(in oklch, var(--color-cyan) 60%, transparent);
|
||||
font: 600 11px/1 ui-monospace, monospace;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
z-index: 6;
|
||||
}
|
||||
.checkbox {
|
||||
position: absolute; top: 10px; right: 10px;
|
||||
width: 28px; height: 28px;
|
||||
border-radius: 8px;
|
||||
background: rgba(0,0,0,0.4);
|
||||
border: 2px solid rgba(255,255,255,0.5);
|
||||
display: grid; place-items: center;
|
||||
color: transparent;
|
||||
backdrop-filter: blur(6px);
|
||||
opacity: 0; transition: opacity .2s;
|
||||
z-index: 6;
|
||||
}
|
||||
.card:hover .checkbox { opacity: 1; }
|
||||
.meta {
|
||||
position: absolute; left: 12px; right: 12px; bottom: 10px;
|
||||
display: flex; flex-direction: column; gap: 2px;
|
||||
z-index: 5;
|
||||
}
|
||||
.meta .code { color: var(--color-cyan); font: 700 14px/1.1 ui-monospace, monospace; letter-spacing: 0.06em; }
|
||||
.meta .name { color: #d1d5db; font-size: 12px; }
|
||||
.grad-bottom {
|
||||
position: absolute; inset: auto 0 0 0; height: 60%;
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.85), rgba(0,0,0,0));
|
||||
pointer-events: none;
|
||||
}
|
||||
.actions {
|
||||
position: absolute; right: 10px; bottom: 10px;
|
||||
display: flex; gap: 6px;
|
||||
opacity: 0; transition: opacity .2s;
|
||||
z-index: 7;
|
||||
}
|
||||
.card:hover .actions { opacity: 1; }
|
||||
.actions button {
|
||||
width: 28px; height: 28px; border-radius: 8px;
|
||||
background: rgba(0,0,0,0.6);
|
||||
border: 1px solid rgba(255,255,255,0.18);
|
||||
color: rgba(255,255,255,0.9);
|
||||
display: grid; place-items: center;
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
/* ---------- v7 base (all variants share position/size) ---------- */
|
||||
.play {
|
||||
position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
|
||||
width: 64px; height: 44px; border-radius: 6px;
|
||||
display: grid; place-items: center;
|
||||
color: rgba(255,255,255,0.95);
|
||||
transition: transform .2s, border-color .2s, color .2s, background .2s, box-shadow .2s;
|
||||
}
|
||||
.play svg { width: 18px; height: 18px; }
|
||||
.play:hover {
|
||||
animation: pulse 1.2s ease-out infinite;
|
||||
border-color: var(--color-cyan);
|
||||
color: var(--color-cyan);
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--color-cyan) 50%, transparent); }
|
||||
100% { box-shadow: 0 0 0 14px color-mix(in oklch, var(--color-cyan) 0%, transparent); }
|
||||
}
|
||||
|
||||
/* A — Glass dome (current refined) */
|
||||
.vA .play {
|
||||
background: radial-gradient(circle at 50% 35%, rgba(255,255,255,0.05), rgba(0,0,0,0.15) 80%);
|
||||
border: 1.5px solid rgba(0,0,0,0.35);
|
||||
box-shadow:
|
||||
0 2px 8px rgba(0,0,0,0.45),
|
||||
inset 0 1px 0 rgba(255,255,255,0.18),
|
||||
inset 0 -1px 2px rgba(0,0,0,0.4);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
/* B — Outline only (no fill, thin ring) */
|
||||
.vB .play {
|
||||
background: transparent;
|
||||
border: 1.5px solid rgba(255,255,255,0.55);
|
||||
}
|
||||
|
||||
/* C — Inset / pressed (flat dark fill, strong inner shadow) */
|
||||
.vC .play {
|
||||
background: rgba(0,0,0,0.45);
|
||||
border: 1px solid rgba(0,0,0,0.5);
|
||||
box-shadow:
|
||||
inset 0 2px 4px rgba(0,0,0,0.6),
|
||||
inset 0 -1px 0 rgba(255,255,255,0.08);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
/* D — Cyan glow ring */
|
||||
.vD .play {
|
||||
background: rgba(0,0,0,0.25);
|
||||
border: 1.5px solid color-mix(in oklch, var(--color-cyan) 65%, transparent);
|
||||
color: var(--color-cyan);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(0,0,0,0.25),
|
||||
0 0 14px color-mix(in oklch, var(--color-cyan) 30%, transparent);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
/* E — Double ring */
|
||||
.vE .play {
|
||||
background: rgba(0,0,0,0.30);
|
||||
border: 1.5px solid rgba(255,255,255,0.35);
|
||||
box-shadow:
|
||||
inset 0 0 0 4px rgba(0,0,0,0),
|
||||
inset 0 0 0 5px rgba(255,255,255,0.18),
|
||||
0 1px 4px rgba(0,0,0,0.35);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
/* F — Heavy frost (strong glass) */
|
||||
.vF .play {
|
||||
background: rgba(255,255,255,0.10);
|
||||
border: 1px solid rgba(255,255,255,0.25);
|
||||
box-shadow:
|
||||
0 4px 16px rgba(0,0,0,0.35),
|
||||
inset 0 1px 0 rgba(255,255,255,0.25);
|
||||
backdrop-filter: blur(14px) saturate(1.2);
|
||||
}
|
||||
|
||||
/* G — Beveled metal (vertical gradient) */
|
||||
.vG .play {
|
||||
background: linear-gradient(180deg,
|
||||
rgba(255,255,255,0.18) 0%,
|
||||
rgba(255,255,255,0.04) 45%,
|
||||
rgba(0,0,0,0.25) 100%);
|
||||
border: 1px solid rgba(0,0,0,0.45);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255,255,255,0.30),
|
||||
inset 0 -1px 0 rgba(0,0,0,0.35),
|
||||
0 2px 6px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
/* H — Floating disc (drop shadow only, no border) */
|
||||
.vH .play {
|
||||
background: rgba(20,20,28,0.75);
|
||||
border: 0;
|
||||
box-shadow:
|
||||
0 6px 16px rgba(0,0,0,0.55),
|
||||
0 1px 2px rgba(0,0,0,0.5);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
/* I — Etched (no fill, inset shadow + highlight, looks engraved) */
|
||||
.vI .play {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
box-shadow:
|
||||
inset 0 2px 3px rgba(0,0,0,0.55),
|
||||
inset 0 -1px 1px rgba(255,255,255,0.15),
|
||||
0 1px 0 rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
/* J — Dashed ring (technical aesthetic) */
|
||||
.vJ .play {
|
||||
background: rgba(0,0,0,0.20);
|
||||
border: 1.5px dashed rgba(255,255,255,0.45);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.label { color: var(--color-cyan); font: 600 12px/1 ui-monospace, monospace; letter-spacing: 0.08em; text-transform: uppercase; }
|
||||
.why { color: #9ca3af; font-size: 12px; line-height: 1.4; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen">
|
||||
<main class="max-w-7xl mx-auto p-8">
|
||||
<header class="mb-8">
|
||||
<h1 class="text-2xl font-bold text-white">Play Button — v7 Concentric Ring · 9 Variants</h1>
|
||||
<p class="text-gray-400 mt-1 text-sm">Same 52px circle, position, and icon. Only fill, border, and shadow change. Hover any card to reveal action slots.</p>
|
||||
</header>
|
||||
|
||||
<section class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
|
||||
<!-- A -->
|
||||
<div>
|
||||
<div class="label mb-2">A · Glass dome</div>
|
||||
<div class="card vA group">
|
||||
<div class="id-bg">502 337</div>
|
||||
<span class="vip">◆ VIP</span><span class="checkbox">✓</span>
|
||||
<button type="button" class="play"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
|
||||
<div class="grad-bottom"></div>
|
||||
<div class="meta"><span class="code">YUJ-001</span><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="actions"><button>♥</button><button>⋯</button></div>
|
||||
</div>
|
||||
<p class="why mt-2">Soft radial top-light, dark thin border, inset highlight + drop shadow. Subtle dome.</p>
|
||||
</div>
|
||||
|
||||
<!-- B -->
|
||||
<div>
|
||||
<div class="label mb-2">B · Outline only</div>
|
||||
<div class="card vB group">
|
||||
<div class="id-bg">502 337</div>
|
||||
<span class="vip">◆ VIP</span><span class="checkbox">✓</span>
|
||||
<button type="button" class="play"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
|
||||
<div class="grad-bottom"></div>
|
||||
<div class="meta"><span class="code">YUJ-001</span><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="actions"><button>♥</button><button>⋯</button></div>
|
||||
</div>
|
||||
<p class="why mt-2">Thin white ring, transparent inside. Lightest possible touch.</p>
|
||||
</div>
|
||||
|
||||
<!-- C -->
|
||||
<div>
|
||||
<div class="label mb-2">C · Inset / pressed</div>
|
||||
<div class="card vC group">
|
||||
<div class="id-bg">502 337</div>
|
||||
<span class="vip">◆ VIP</span><span class="checkbox">✓</span>
|
||||
<button type="button" class="play"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
|
||||
<div class="grad-bottom"></div>
|
||||
<div class="meta"><span class="code">YUJ-001</span><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="actions"><button>♥</button><button>⋯</button></div>
|
||||
</div>
|
||||
<p class="why mt-2">Flat dark fill, strong inner shadow. Looks recessed into the card.</p>
|
||||
</div>
|
||||
|
||||
<!-- D -->
|
||||
<div>
|
||||
<div class="label mb-2">D · Cyan glow ring</div>
|
||||
<div class="card vD group">
|
||||
<div class="id-bg">502 337</div>
|
||||
<span class="vip">◆ VIP</span><span class="checkbox">✓</span>
|
||||
<button type="button" class="play"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
|
||||
<div class="grad-bottom"></div>
|
||||
<div class="meta"><span class="code">YUJ-001</span><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="actions"><button>♥</button><button>⋯</button></div>
|
||||
</div>
|
||||
<p class="why mt-2">Cyan border + soft cyan halo. Brand signal without solid fill.</p>
|
||||
</div>
|
||||
|
||||
<!-- E -->
|
||||
<div>
|
||||
<div class="label mb-2">E · Double ring</div>
|
||||
<div class="card vE group">
|
||||
<div class="id-bg">502 337</div>
|
||||
<span class="vip">◆ VIP</span><span class="checkbox">✓</span>
|
||||
<button type="button" class="play"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
|
||||
<div class="grad-bottom"></div>
|
||||
<div class="meta"><span class="code">YUJ-001</span><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="actions"><button>♥</button><button>⋯</button></div>
|
||||
</div>
|
||||
<p class="why mt-2">Outer + inner concentric stroke. Reads as a precise lens.</p>
|
||||
</div>
|
||||
|
||||
<!-- F -->
|
||||
<div>
|
||||
<div class="label mb-2">F · Heavy frost</div>
|
||||
<div class="card vF group">
|
||||
<div class="id-bg">502 337</div>
|
||||
<span class="vip">◆ VIP</span><span class="checkbox">✓</span>
|
||||
<button type="button" class="play"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
|
||||
<div class="grad-bottom"></div>
|
||||
<div class="meta"><span class="code">YUJ-001</span><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="actions"><button>♥</button><button>⋯</button></div>
|
||||
</div>
|
||||
<p class="why mt-2">Stronger backdrop-blur, brighter inner edge — pronounced glass.</p>
|
||||
</div>
|
||||
|
||||
<!-- G -->
|
||||
<div>
|
||||
<div class="label mb-2">G · Beveled metal</div>
|
||||
<div class="card vG group">
|
||||
<div class="id-bg">502 337</div>
|
||||
<span class="vip">◆ VIP</span><span class="checkbox">✓</span>
|
||||
<button type="button" class="play"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
|
||||
<div class="grad-bottom"></div>
|
||||
<div class="meta"><span class="code">YUJ-001</span><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="actions"><button>♥</button><button>⋯</button></div>
|
||||
</div>
|
||||
<p class="why mt-2">Top-to-bottom gradient + inset edges. Tactile, slightly skeuomorphic.</p>
|
||||
</div>
|
||||
|
||||
<!-- H -->
|
||||
<div>
|
||||
<div class="label mb-2">H · Floating disc</div>
|
||||
<div class="card vH group">
|
||||
<div class="id-bg">502 337</div>
|
||||
<span class="vip">◆ VIP</span><span class="checkbox">✓</span>
|
||||
<button type="button" class="play"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
|
||||
<div class="grad-bottom"></div>
|
||||
<div class="meta"><span class="code">YUJ-001</span><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="actions"><button>♥</button><button>⋯</button></div>
|
||||
</div>
|
||||
<p class="why mt-2">No border. Soft dark fill + prominent drop shadow — disc hovers above.</p>
|
||||
</div>
|
||||
|
||||
<!-- I -->
|
||||
<div>
|
||||
<div class="label mb-2">I · Etched</div>
|
||||
<div class="card vI group">
|
||||
<div class="id-bg">502 337</div>
|
||||
<span class="vip">◆ VIP</span><span class="checkbox">✓</span>
|
||||
<button type="button" class="play"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
|
||||
<div class="grad-bottom"></div>
|
||||
<div class="meta"><span class="code">YUJ-001</span><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="actions"><button>♥</button><button>⋯</button></div>
|
||||
</div>
|
||||
<p class="why mt-2">No fill, no border — only inset shadow + bottom highlight. Engraved into the card.</p>
|
||||
</div>
|
||||
|
||||
<!-- J -->
|
||||
<div>
|
||||
<div class="label mb-2">J · Dashed ring</div>
|
||||
<div class="card vJ group">
|
||||
<div class="id-bg">502 337</div>
|
||||
<span class="vip">◆ VIP</span><span class="checkbox">✓</span>
|
||||
<button type="button" class="play"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
|
||||
<div class="grad-bottom"></div>
|
||||
<div class="meta"><span class="code">YUJ-001</span><span class="name">Ichika Matsumoto</span></div>
|
||||
<div class="actions"><button>♥</button><button>⋯</button></div>
|
||||
</div>
|
||||
<p class="why mt-2">Dashed border. Technical/sketch feel — reads as overlay UI, not a sealed control.</p>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,351 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex — Selection Bar Layout Options</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0e0a14;
|
||||
--bg-1: #18121f;
|
||||
--fg: #ece6f5;
|
||||
--fg-dim: #b9b1c6;
|
||||
--fg-muted: #7d7388;
|
||||
--cyan: #4dd9e6;
|
||||
--violet: #b87cf6;
|
||||
--coral: #ff7a8a;
|
||||
--mint: #79e6b2;
|
||||
--amber: #fbbf24;
|
||||
--glass-border: #2a2434;
|
||||
--glass-border-strong: #3d3548;
|
||||
--glass: rgba(40, 32, 56, 0.5);
|
||||
--glass-strong: rgba(56, 46, 76, 0.7);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html, body {
|
||||
margin: 0;
|
||||
background: radial-gradient(1200px 600px at 70% -10%, rgba(184,124,246,0.08), transparent),
|
||||
radial-gradient(800px 500px at 10% 110%, rgba(77,217,230,0.06), transparent),
|
||||
var(--bg-0);
|
||||
color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
.page { max-width: 1200px; margin: 0 auto; padding: 32px 24px 96px; }
|
||||
h1 { font-size: 26px; margin: 0 0 8px; font-weight: 600; }
|
||||
.lede { color: var(--fg-dim); margin: 0 0 32px; max-width: 760px; line-height: 1.5; }
|
||||
h2 {
|
||||
margin: 32px 0 4px;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--cyan);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
.desc { color: var(--fg-dim); font-size: 13px; margin: 0 0 12px; max-width: 820px; line-height: 1.5; }
|
||||
|
||||
.stage {
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 16px;
|
||||
padding: 28px 20px;
|
||||
margin-bottom: 24px;
|
||||
position: relative;
|
||||
min-height: 120px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* selection bar — mimics SelectionBar.tsx */
|
||||
.sb {
|
||||
border: 1px solid var(--glass-border-strong);
|
||||
background: color-mix(in oklch, var(--bg-0) 85%, transparent);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 16px;
|
||||
padding: 10px 16px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
box-shadow: 0 12px 30px rgba(0,0,0,0.4);
|
||||
}
|
||||
.sel-count {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
.sel-count .n { color: var(--cyan); font-weight: 600; }
|
||||
.sel-count .l { color: var(--fg-dim); }
|
||||
.sep { width: 1px; height: 20px; background: var(--glass-border); }
|
||||
|
||||
.btn {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--glass-border);
|
||||
background: var(--glass);
|
||||
color: var(--fg-dim);
|
||||
font-size: 12px;
|
||||
cursor: default;
|
||||
}
|
||||
.btn svg { width: 13px; height: 13px; }
|
||||
.btn-mint { background: rgba(121,230,178,0.15); border-color: rgba(121,230,178,0.4); color: var(--mint); }
|
||||
.btn-coral { background: rgba(255,122,138,0.15); border-color: rgba(255,122,138,0.4); color: var(--coral); }
|
||||
.btn-cyan { background: rgba(77,217,230,0.15); border-color: rgba(77,217,230,0.4); color: var(--cyan); }
|
||||
.btn-amber { background: rgba(251,191,36,0.15); border-color: rgba(251,191,36,0.4); color: var(--amber); }
|
||||
.btn-icon-only { padding: 6px 8px; }
|
||||
.btn .caret { font-size: 9px; opacity: 0.7; }
|
||||
.btn-clear { color: var(--fg-muted); border-color: transparent; background: transparent; }
|
||||
|
||||
.pros-cons {
|
||||
display: flex; gap: 12px; margin-top: 12px; font-size: 12px;
|
||||
}
|
||||
.pros, .cons {
|
||||
flex: 1; padding: 8px 10px; border-radius: 8px;
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
.pros { border-color: rgba(121,230,178,0.2); }
|
||||
.cons { border-color: rgba(255,122,138,0.2); }
|
||||
.pros strong { color: var(--mint); font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; }
|
||||
.cons strong { color: var(--coral); font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; }
|
||||
.pros ul, .cons ul { margin: 4px 0 0; padding-left: 16px; color: var(--fg-dim); }
|
||||
.pros li, .cons li { margin: 2px 0; }
|
||||
|
||||
/* Mock dropdown */
|
||||
.menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 6px);
|
||||
right: 0;
|
||||
background: var(--bg-0);
|
||||
border: 1px solid var(--glass-border-strong);
|
||||
border-radius: 10px;
|
||||
padding: 4px;
|
||||
width: 180px;
|
||||
box-shadow: 0 16px 40px rgba(0,0,0,0.7);
|
||||
z-index: 5;
|
||||
}
|
||||
.menu .row {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 7px 10px;
|
||||
border-radius: 6px;
|
||||
color: var(--fg-dim);
|
||||
font-size: 13px;
|
||||
}
|
||||
.menu .row svg { width: 14px; height: 14px; }
|
||||
.menu .row:hover { background: var(--glass); color: var(--fg); }
|
||||
.menu .row.mint { color: var(--mint); }
|
||||
.menu .row.cyan { color: var(--cyan); }
|
||||
.menu .row.amber { color: var(--amber); }
|
||||
.menu .row.muted { color: var(--fg-muted); }
|
||||
.menu .divider { height: 1px; background: var(--glass-border); margin: 4px 0; }
|
||||
|
||||
.has-menu { position: relative; }
|
||||
|
||||
.stage-label {
|
||||
position: absolute; top: 8px; left: 12px;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--fg-muted);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
code {
|
||||
background: var(--glass); padding: 1px 5px; border-radius: 4px;
|
||||
font-size: 12px; color: var(--cyan);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<h1>Selection Bar — Bulk VIP / Favorite Options</h1>
|
||||
<p class="lede">
|
||||
Five layouts for adding bulk VIP / Favorite to the selection bar without it becoming a 7-button mess.
|
||||
All assume 2 covers selected. Dropdowns are shown "open" inline so you can see their content.
|
||||
</p>
|
||||
|
||||
<!-- Current -->
|
||||
<h2>Current — for reference</h2>
|
||||
<p class="desc">5 buttons. No way to bulk Mark / Favorite.</p>
|
||||
<div class="stage">
|
||||
<div class="stage-label">Current</div>
|
||||
<div class="sb">
|
||||
<span class="sel-count"><span class="n">2</span><span class="l"> selected</span></span>
|
||||
<div class="sep"></div>
|
||||
<span class="btn">📋 All (26)</span>
|
||||
<span class="btn btn-mint">👁 Watched</span>
|
||||
<span class="btn">🚫 Unwatched</span>
|
||||
<span class="btn btn-coral">🗑 Delete</span>
|
||||
<span class="btn btn-clear">✕ Clear</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Option A -->
|
||||
<h2>Option A — Just add two more buttons</h2>
|
||||
<p class="desc">Straightforward: VIP and Favorite chips alongside Watched/Unwatched. Quick and obvious; bar gets crowded.</p>
|
||||
<div class="stage">
|
||||
<div class="stage-label">A · 7 buttons</div>
|
||||
<div class="sb">
|
||||
<span class="sel-count"><span class="n">2</span><span class="l"> selected</span></span>
|
||||
<div class="sep"></div>
|
||||
<span class="btn">📋 All (26)</span>
|
||||
<span class="btn btn-mint">👁 Watched</span>
|
||||
<span class="btn">🚫 Unwatched</span>
|
||||
<span class="btn btn-cyan">💎 VIP</span>
|
||||
<span class="btn btn-amber">★ Favorite</span>
|
||||
<span class="btn btn-coral">🗑 Delete</span>
|
||||
<span class="btn btn-clear">✕ Clear</span>
|
||||
</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><strong>Pros</strong><ul>
|
||||
<li>One click for everything</li>
|
||||
<li>All actions visible</li>
|
||||
</ul></div>
|
||||
<div class="cons"><strong>Cons</strong><ul>
|
||||
<li>7 buttons — visually busy</li>
|
||||
<li>Doesn't scale to more bulk actions</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Option B -->
|
||||
<h2>Option B — Collapse all toggles into one <code>Set ▾</code> menu <span style="color:var(--fg-muted)">(my recommendation)</span></h2>
|
||||
<p class="desc">Single "Set ▾" dropdown houses Watched/Unwatched and VIP/Favorite/Unmark. Bar drops to 4 buttons.</p>
|
||||
<div class="stage" style="padding-top: 60px; padding-bottom: 60px;">
|
||||
<div class="stage-label">B · 4 buttons + 1 menu</div>
|
||||
<div class="sb">
|
||||
<span class="sel-count"><span class="n">2</span><span class="l"> selected</span></span>
|
||||
<div class="sep"></div>
|
||||
<span class="btn">📋 All (26)</span>
|
||||
<span class="has-menu">
|
||||
<span class="btn">⚙ Set <span class="caret">▾</span></span>
|
||||
<div class="menu">
|
||||
<div class="row mint">👁 Watched</div>
|
||||
<div class="row">🚫 Unwatched</div>
|
||||
<div class="divider"></div>
|
||||
<div class="row cyan">💎 VIP</div>
|
||||
<div class="row amber">★ Favorite</div>
|
||||
<div class="row muted">⊘ Unmark</div>
|
||||
</div>
|
||||
</span>
|
||||
<span class="btn btn-coral">🗑 Delete</span>
|
||||
<span class="btn btn-clear">✕ Clear</span>
|
||||
</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><strong>Pros</strong><ul>
|
||||
<li>Cleanest bar</li>
|
||||
<li>Scales to more actions (e.g. "Add to Collection…", "Tag…")</li>
|
||||
<li>Consistent with FilterBar's dropdown pattern</li>
|
||||
</ul></div>
|
||||
<div class="cons"><strong>Cons</strong><ul>
|
||||
<li>2 clicks for Watched (was 1)</li>
|
||||
<li>Bulk Watched is your most-frequent action — extra click hurts</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Option C -->
|
||||
<h2>Option C — Keep Watched/Unwatched, add only <code>Mark ▾</code></h2>
|
||||
<p class="desc">Preserves one-click Watched. Adds a single "Mark ▾" dropdown for VIP / Favorite / Unmark. 6 buttons.</p>
|
||||
<div class="stage" style="padding-top: 60px; padding-bottom: 60px;">
|
||||
<div class="stage-label">C · 5 buttons + 1 menu</div>
|
||||
<div class="sb">
|
||||
<span class="sel-count"><span class="n">2</span><span class="l"> selected</span></span>
|
||||
<div class="sep"></div>
|
||||
<span class="btn">📋 All (26)</span>
|
||||
<span class="btn btn-mint">👁 Watched</span>
|
||||
<span class="btn">🚫 Unwatched</span>
|
||||
<span class="has-menu">
|
||||
<span class="btn">🔖 Mark <span class="caret">▾</span></span>
|
||||
<div class="menu">
|
||||
<div class="row cyan">💎 VIP</div>
|
||||
<div class="row amber">★ Favorite</div>
|
||||
<div class="row muted">⊘ Unmark</div>
|
||||
</div>
|
||||
</span>
|
||||
<span class="btn btn-coral">🗑 Delete</span>
|
||||
<span class="btn btn-clear">✕ Clear</span>
|
||||
</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><strong>Pros</strong><ul>
|
||||
<li>One-click Watched preserved (your most-used)</li>
|
||||
<li>Mark grouped logically (VIP/Favorite are one axis)</li>
|
||||
</ul></div>
|
||||
<div class="cons"><strong>Cons</strong><ul>
|
||||
<li>Still 6 visible buttons — middle ground</li>
|
||||
<li>Two click-patterns: direct vs dropdown</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Option D -->
|
||||
<h2>Option D — Single <code>Actions ▾</code> kebab</h2>
|
||||
<p class="desc">Hide everything except Delete behind one "Actions" menu. Most minimal but loses quick-access to Watched.</p>
|
||||
<div class="stage" style="padding-top: 60px; padding-bottom: 100px;">
|
||||
<div class="stage-label">D · 3 buttons + 1 menu</div>
|
||||
<div class="sb">
|
||||
<span class="sel-count"><span class="n">2</span><span class="l"> selected</span></span>
|
||||
<div class="sep"></div>
|
||||
<span class="btn">📋 All (26)</span>
|
||||
<span class="has-menu">
|
||||
<span class="btn">⋯ Actions <span class="caret">▾</span></span>
|
||||
<div class="menu" style="width: 200px;">
|
||||
<div class="row mint">👁 Mark Watched</div>
|
||||
<div class="row">🚫 Mark Unwatched</div>
|
||||
<div class="divider"></div>
|
||||
<div class="row cyan">💎 Set VIP</div>
|
||||
<div class="row amber">★ Set Favorite</div>
|
||||
<div class="row muted">⊘ Unmark</div>
|
||||
</div>
|
||||
</span>
|
||||
<span class="btn btn-coral">🗑 Delete</span>
|
||||
<span class="btn btn-clear">✕ Clear</span>
|
||||
</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><strong>Pros</strong><ul>
|
||||
<li>Smallest bar — 4 visible items</li>
|
||||
<li>Easy to add more actions (Add to Collection, Tag, Export, …)</li>
|
||||
</ul></div>
|
||||
<div class="cons"><strong>Cons</strong><ul>
|
||||
<li>2 clicks for any state-change action</li>
|
||||
<li>Discoverability hit — new users won't know the menu's there</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Option E -->
|
||||
<h2>Option E — Icon-only Watched buttons + <code>Mark ▾</code></h2>
|
||||
<p class="desc">Trim Watched/Unwatched to bare icons (with tooltip), add Mark ▾. Saves horizontal space without hiding actions.</p>
|
||||
<div class="stage" style="padding-top: 60px; padding-bottom: 60px;">
|
||||
<div class="stage-label">E · 6 buttons (compact) + 1 menu</div>
|
||||
<div class="sb">
|
||||
<span class="sel-count"><span class="n">2</span><span class="l"> selected</span></span>
|
||||
<div class="sep"></div>
|
||||
<span class="btn">📋 All (26)</span>
|
||||
<span class="btn btn-mint btn-icon-only" title="Mark watched">👁</span>
|
||||
<span class="btn btn-icon-only" title="Mark unwatched">🚫</span>
|
||||
<span class="has-menu">
|
||||
<span class="btn">🔖 Mark <span class="caret">▾</span></span>
|
||||
<div class="menu">
|
||||
<div class="row cyan">💎 VIP</div>
|
||||
<div class="row amber">★ Favorite</div>
|
||||
<div class="row muted">⊘ Unmark</div>
|
||||
</div>
|
||||
</span>
|
||||
<span class="btn btn-coral">🗑 Delete</span>
|
||||
<span class="btn btn-clear">✕ Clear</span>
|
||||
</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><strong>Pros</strong><ul>
|
||||
<li>One-click Watched preserved, takes less space</li>
|
||||
<li>Mark dropdown adds bulk VIP/Favorite cleanly</li>
|
||||
</ul></div>
|
||||
<div class="cons"><strong>Cons</strong><ul>
|
||||
<li>Icons less discoverable than labels</li>
|
||||
<li>Mixed style (label + icon-only on the same row)</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p style="margin-top: 24px; font-size: 11px; color: var(--fg-muted); font-family: ui-monospace;">
|
||||
Pick based on what you do most: B if you mark covers more than you watch, C/E if you watch more than mark.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,497 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Settings Consolidation — 9 → 5 Sections</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-bg-0: oklch(0.13 0.025 280);
|
||||
--color-bg-1: oklch(0.17 0.04 285);
|
||||
--color-fg: oklch(0.97 0.01 280);
|
||||
--color-fg-dim: oklch(0.72 0.025 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: color-mix(in oklch, white 6%, transparent);
|
||||
--color-glass-strong: color-mix(in oklch, white 10%, transparent);
|
||||
--color-glass-border: color-mix(in oklch, white 14%, transparent);
|
||||
--color-glass-border-strong: color-mix(in oklch, white 22%, transparent);
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-violet: oklch(0.72 0.22 305);
|
||||
--color-mint: oklch(0.80 0.16 155);
|
||||
--color-coral: oklch(0.72 0.20 25);
|
||||
|
||||
--spacing-card: 15px;
|
||||
--spacing-card-gap: 9px;
|
||||
--spacing-section: 15px;
|
||||
--spacing-label: 7px;
|
||||
--spacing-chip: 7px;
|
||||
}
|
||||
body {
|
||||
background: var(--color-bg-0);
|
||||
color: var(--color-fg);
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
.glass { background: var(--color-glass); border: 1px solid var(--color-glass-border); }
|
||||
.why { font-size: 12px; color: var(--color-fg-muted); }
|
||||
.label-mono { font-family: ui-monospace, monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-fg-muted); }
|
||||
.pg {
|
||||
background: oklch(0.10 0.025 280);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pg-h {
|
||||
padding: 12px 16px;
|
||||
background: oklch(0.20 0.04 285);
|
||||
border-bottom: 1px solid var(--color-glass-border);
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 12px;
|
||||
color: var(--color-fg-dim);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.pg-h .tag { color: var(--color-cyan); font-weight: 600; }
|
||||
.pg-body { padding: 18px; }
|
||||
|
||||
/* Mini modal frame */
|
||||
.modal-frame {
|
||||
background: color-mix(in oklch, var(--color-bg-0) 96%, transparent);
|
||||
border: 1px solid var(--color-glass-border-strong);
|
||||
border-radius: 14px;
|
||||
overflow: hidden;
|
||||
display: flex; flex-direction: column;
|
||||
height: 540px;
|
||||
}
|
||||
.modal-header {
|
||||
padding: 14px 20px;
|
||||
border-bottom: 1px solid var(--color-glass-border);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.nav-btn {
|
||||
width: 100%;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--color-fg-dim);
|
||||
background: transparent;
|
||||
margin-bottom: 2px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
.nav-btn.active {
|
||||
background: oklch(0.82 0.16 200 / 0.15);
|
||||
color: var(--color-cyan);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--color-glass);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: 16px;
|
||||
padding: var(--spacing-card);
|
||||
}
|
||||
.card h3 {
|
||||
font-family: ui-monospace, monospace; font-size: 13px;
|
||||
text-transform: uppercase; letter-spacing: 0.08em;
|
||||
color: var(--color-fg-muted);
|
||||
margin-bottom: var(--spacing-label);
|
||||
}
|
||||
|
||||
.row { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 6px 0; font-size: 13px; }
|
||||
.row dt { color: var(--color-fg-dim); }
|
||||
.row dd { font-family: ui-monospace, monospace; color: var(--color-fg); font-size: 12px; }
|
||||
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
||||
padding: 0 10px; height: 28px;
|
||||
border-radius: 6px; font-size: 12px;
|
||||
border: 1px solid var(--color-glass-border); color: var(--color-fg-dim);
|
||||
background: var(--color-glass);
|
||||
}
|
||||
.pill.active { background: var(--color-cyan); color: black; border-color: transparent; font-weight: 600; }
|
||||
|
||||
.btn {
|
||||
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
||||
height: 30px; padding: 0 12px; min-width: 100px;
|
||||
border-radius: 8px; font-size: 12px;
|
||||
border: 1px solid var(--color-glass-border); background: var(--color-glass);
|
||||
color: var(--color-fg-dim);
|
||||
}
|
||||
.btn-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 8px 0; }
|
||||
.btn-row + .btn-row { border-top: 1px solid var(--color-glass-border); }
|
||||
.btn-row .meta { min-width: 0; }
|
||||
.btn-row .meta .ttl { font-size: 13px; font-weight: 500; }
|
||||
.btn-row .meta .desc { font-size: 11px; color: var(--color-fg-muted); margin-top: 2px; line-height: 1.4; }
|
||||
|
||||
.divider-soft {
|
||||
border: 0; border-top: 1px solid var(--color-glass-border);
|
||||
margin: 14px 0 12px;
|
||||
}
|
||||
.sub-h { font-family: ui-monospace, monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-cyan); margin-bottom: 6px; }
|
||||
|
||||
.arrow { color: var(--color-mint); font-family: monospace; }
|
||||
|
||||
.nav-side {
|
||||
width: 200px; padding: 12px;
|
||||
border-right: 1px solid var(--color-glass-border);
|
||||
background: oklch(0.13 0.025 280 / 0.5);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.body-pane { padding: var(--spacing-card); overflow-y: auto; flex: 1; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="space-y-6 max-w-[1500px] mx-auto p-6">
|
||||
|
||||
<header class="space-y-1">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Settings Consolidation · 9 → 5 sections</h1>
|
||||
<p class="why max-w-[820px]">No feature removed (except optional 3-column layout toggle). Sections merged by purpose: Appearance, Library, Video, Tools, Info.</p>
|
||||
</header>
|
||||
|
||||
<!-- ====================================================================
|
||||
Side-by-side nav comparison
|
||||
==================================================================== -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag">BEFORE</span> · 9 sections</span></div>
|
||||
<div class="pg-body">
|
||||
<ol class="space-y-1 text-sm">
|
||||
<li class="nav-btn active">⊟ General <span class="why ml-auto">2 controls</span></li>
|
||||
<li class="nav-btn">🎨 Theme <span class="why ml-auto">1 control</span></li>
|
||||
<li class="nav-btn">▦ Display <span class="why ml-auto">3 controls</span></li>
|
||||
<li class="nav-btn">🗑 Trash <span class="why ml-auto">4 controls</span></li>
|
||||
<li class="nav-btn">▶ Video Library <span class="why ml-auto">3 sub-blocks</span></li>
|
||||
<li class="nav-btn">⚒ Maintenance <span class="why ml-auto">7 buttons</span></li>
|
||||
<li class="nav-btn">💾 Backup <span class="why ml-auto">2 buttons</span></li>
|
||||
<li class="nav-btn">📊 Library stats <span class="why ml-auto">read-only</span></li>
|
||||
<li class="nav-btn">🗂 Storage paths <span class="why ml-auto">2 paths · read-only</span></li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">AFTER</span> · 5 sections</span></div>
|
||||
<div class="pg-body">
|
||||
<ol class="space-y-1 text-sm">
|
||||
<li class="nav-btn active">🎨 Appearance <span class="why ml-auto">colors · grid · fade · sort</span></li>
|
||||
<li class="nav-btn">🗑 Library <span class="why ml-auto">recycle bin · retention</span></li>
|
||||
<li class="nav-btn">▶ Video <span class="why ml-auto">paths · transcode · parts</span></li>
|
||||
<li class="nav-btn">⚒ Tools <span class="why ml-auto">maintenance + backup</span></li>
|
||||
<li class="nav-btn">📊 Info <span class="why ml-auto">stats + paths</span></li>
|
||||
</ol>
|
||||
<p class="why" style="margin-top: 14px;">
|
||||
Drop the sidebar/3-column layout toggle. Sidebar only — saves ~80 LOC + 1 setting.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
Appearance
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">1 · APPEARANCE</span> · Theme + Display + General's "Default sort"</span></div>
|
||||
<div class="pg-body">
|
||||
<div class="modal-frame">
|
||||
<div class="modal-header">
|
||||
<div style="display:flex; align-items:center; gap: 8px;">
|
||||
<span style="color: var(--color-cyan); font-size: 16px;">⚙</span>
|
||||
<h2 class="text-lg font-semibold">Settings</h2>
|
||||
</div>
|
||||
<span style="color: var(--color-fg-muted);">✕</span>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 200px 1fr; flex: 1; min-height: 0;">
|
||||
<nav class="nav-side">
|
||||
<button class="nav-btn active">🎨 Appearance</button>
|
||||
<button class="nav-btn">🗑 Library</button>
|
||||
<button class="nav-btn">▶ Video</button>
|
||||
<button class="nav-btn">⚒ Tools</button>
|
||||
<button class="nav-btn">📊 Info</button>
|
||||
</nav>
|
||||
<div class="body-pane">
|
||||
<section class="card">
|
||||
<h3>Appearance</h3>
|
||||
|
||||
<div class="sub-h">Colors</div>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 14px;">
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: 4px;">Accent · primary</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<span style="width: 28px; height: 28px; border-radius: 6px; background: var(--color-cyan);"></span>
|
||||
<span class="pill">#8edcff</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: 4px;">Accent · secondary</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<span style="width: 28px; height: 28px; border-radius: 6px; background: var(--color-violet);"></span>
|
||||
<span class="pill">#bd8eff</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="divider-soft">
|
||||
<div class="sub-h">Grid</div>
|
||||
<div class="row"><dt>Grid columns (Landscape)</dt><dd>3</dd></div>
|
||||
<div class="row"><dt>Grid columns (Portrait)</dt><dd>6</dd></div>
|
||||
|
||||
<hr class="divider-soft">
|
||||
<div class="sub-h">Behavior</div>
|
||||
<div class="row"><dt>Fade transitions</dt><dd><span class="pill active">On</span></dd></div>
|
||||
<div class="row"><dt>Fade duration</dt><dd>400 ms</dd></div>
|
||||
<div class="row"><dt>Default library sort</dt><dd><span class="pill active">Newest</span></dd></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
Library
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">2 · LIBRARY</span> · Trash & deletion (renamed)</span></div>
|
||||
<div class="pg-body">
|
||||
<div class="modal-frame">
|
||||
<div class="modal-header">
|
||||
<div style="display:flex; align-items:center; gap: 8px;">
|
||||
<span style="color: var(--color-cyan); font-size: 16px;">⚙</span>
|
||||
<h2 class="text-lg font-semibold">Settings</h2>
|
||||
</div>
|
||||
<span style="color: var(--color-fg-muted);">✕</span>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 200px 1fr; flex: 1; min-height: 0;">
|
||||
<nav class="nav-side">
|
||||
<button class="nav-btn">🎨 Appearance</button>
|
||||
<button class="nav-btn active">🗑 Library</button>
|
||||
<button class="nav-btn">▶ Video</button>
|
||||
<button class="nav-btn">⚒ Tools</button>
|
||||
<button class="nav-btn">📊 Info</button>
|
||||
</nav>
|
||||
<div class="body-pane">
|
||||
<section class="card">
|
||||
<h3>Library · file handling</h3>
|
||||
<div class="sub-h">Trash</div>
|
||||
<div class="row"><dt>Use recycle bin</dt><dd><span class="pill active">On</span></dd></div>
|
||||
<div class="row"><dt>Trash retention</dt><dd>30 days</dd></div>
|
||||
|
||||
<hr class="divider-soft">
|
||||
<div class="sub-h">Disk</div>
|
||||
<div class="row"><dt>Delete files from disk on empty</dt><dd><span class="pill active">On</span></dd></div>
|
||||
<div class="row"><dt>Superseded retention</dt><dd>30 days</dd></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
Video — unchanged in shape, just renamed
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">3 · VIDEO</span> · unchanged · paths · transcode · part patterns</span></div>
|
||||
<div class="pg-body">
|
||||
<div class="modal-frame">
|
||||
<div class="modal-header">
|
||||
<div style="display:flex; align-items:center; gap: 8px;">
|
||||
<span style="color: var(--color-cyan); font-size: 16px;">⚙</span>
|
||||
<h2 class="text-lg font-semibold">Settings</h2>
|
||||
</div>
|
||||
<span style="color: var(--color-fg-muted);">✕</span>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 200px 1fr; flex: 1; min-height: 0;">
|
||||
<nav class="nav-side">
|
||||
<button class="nav-btn">🎨 Appearance</button>
|
||||
<button class="nav-btn">🗑 Library</button>
|
||||
<button class="nav-btn active">▶ Video</button>
|
||||
<button class="nav-btn">⚒ Tools</button>
|
||||
<button class="nav-btn">📊 Info</button>
|
||||
</nav>
|
||||
<div class="body-pane">
|
||||
<section class="card">
|
||||
<h3>Video</h3>
|
||||
<div class="sub-h">Library paths</div>
|
||||
<div class="glass rounded-lg" style="padding: 8px 10px; font-family: monospace; font-size: 12px; color: var(--color-fg-dim); margin-bottom: 6px;">D:\JAV</div>
|
||||
<div class="glass rounded-lg" style="padding: 8px 10px; font-family: monospace; font-size: 12px; color: var(--color-fg-dim); margin-bottom: 6px;">E:\JAV-extras</div>
|
||||
|
||||
<hr class="divider-soft">
|
||||
<div class="sub-h">Transcode mode</div>
|
||||
<div style="display: flex; gap: 6px; flex-wrap: wrap;">
|
||||
<span class="pill">Off</span><span class="pill active">Always</span><span class="pill">Auto · predicate</span><span class="pill">Auto · runtime</span>
|
||||
</div>
|
||||
|
||||
<hr class="divider-soft">
|
||||
<div class="sub-h">Part suffix patterns</div>
|
||||
<ol style="display: flex; flex-direction: column; gap: 6px;">
|
||||
<li class="glass rounded-lg" style="padding: 6px 10px; font-family: monospace; font-size: 12px; color: var(--color-fg-dim);">-cd{N}</li>
|
||||
<li class="glass rounded-lg" style="padding: 6px 10px; font-family: monospace; font-size: 12px; color: var(--color-fg-dim);">.part{N}</li>
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
Tools — Maintenance + Backup merged
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">4 · TOOLS</span> · Maintenance + Backup merged · 2 sub-groups</span></div>
|
||||
<div class="pg-body">
|
||||
<div class="modal-frame">
|
||||
<div class="modal-header">
|
||||
<div style="display:flex; align-items:center; gap: 8px;">
|
||||
<span style="color: var(--color-cyan); font-size: 16px;">⚙</span>
|
||||
<h2 class="text-lg font-semibold">Settings</h2>
|
||||
</div>
|
||||
<span style="color: var(--color-fg-muted);">✕</span>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 200px 1fr; flex: 1; min-height: 0;">
|
||||
<nav class="nav-side">
|
||||
<button class="nav-btn">🎨 Appearance</button>
|
||||
<button class="nav-btn">🗑 Library</button>
|
||||
<button class="nav-btn">▶ Video</button>
|
||||
<button class="nav-btn active">⚒ Tools</button>
|
||||
<button class="nav-btn">📊 Info</button>
|
||||
</nav>
|
||||
<div class="body-pane">
|
||||
<section class="card">
|
||||
<h3>Tools</h3>
|
||||
|
||||
<div class="sub-h">Maintenance</div>
|
||||
<div>
|
||||
<div class="btn-row">
|
||||
<div class="meta"><div class="ttl">Purge Orphan Files</div><div class="desc">Delete files in library/thumbs that no record references.</div></div>
|
||||
<button class="btn">Scan</button>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<div class="meta"><div class="ttl">Re-parse Codes</div><div class="desc">Re-run JAV-code parser on every cover's stored filename.</div></div>
|
||||
<button class="btn"># Scan</button>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<div class="meta"><div class="ttl">Re-organize Files</div><div class="desc">Move covers into letter buckets on disk.</div></div>
|
||||
<button class="btn">Scan</button>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<div class="meta"><div class="ttl">Regenerate Thumbnails</div><div class="desc">Rebuild grid-preview WebP files from originals.</div></div>
|
||||
<button class="btn">Scan</button>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<div class="meta"><div class="ttl">Find Undersized Covers</div><div class="desc">Scan covers smaller than standard JAV size.</div></div>
|
||||
<button class="btn">⚲ Scan</button>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<div class="meta"><div class="ttl">Find Near-Duplicates</div><div class="desc">Compare perceptual hashes for visual matches.</div></div>
|
||||
<button class="btn">⧉ Scan</button>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<div class="meta"><div class="ttl">Clear Cache</div><div class="desc">Drop the in-memory settings cache.</div></div>
|
||||
<button class="btn">↻ Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="divider-soft">
|
||||
<div class="sub-h">Backup</div>
|
||||
<div>
|
||||
<div class="btn-row" style="border-top: 0;">
|
||||
<div class="meta"><div class="ttl">Export Database</div><div class="desc">Download a snapshot of library.db.</div></div>
|
||||
<button class="btn">⬇ Export</button>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<div class="meta"><div class="ttl">Import Database</div><div class="desc">Restore from a previous export.</div></div>
|
||||
<button class="btn">⬆ Import</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
Info — stats + paths
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">5 · INFO</span> · Library stats + Storage paths · read-only · 2-col stat layout</span></div>
|
||||
<div class="pg-body">
|
||||
<div class="modal-frame">
|
||||
<div class="modal-header">
|
||||
<div style="display:flex; align-items:center; gap: 8px;">
|
||||
<span style="color: var(--color-cyan); font-size: 16px;">⚙</span>
|
||||
<h2 class="text-lg font-semibold">Settings</h2>
|
||||
</div>
|
||||
<span style="color: var(--color-fg-muted);">✕</span>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 200px 1fr; flex: 1; min-height: 0;">
|
||||
<nav class="nav-side">
|
||||
<button class="nav-btn">🎨 Appearance</button>
|
||||
<button class="nav-btn">🗑 Library</button>
|
||||
<button class="nav-btn">▶ Video</button>
|
||||
<button class="nav-btn">⚒ Tools</button>
|
||||
<button class="nav-btn active">📊 Info</button>
|
||||
</nav>
|
||||
<div class="body-pane">
|
||||
<section class="card">
|
||||
<h3>Info</h3>
|
||||
<!-- stats in 2-col grid (was vertical stack of 5) -->
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<div class="sub-h">Covers</div>
|
||||
<div style="border-top: 1px solid var(--color-glass-border); padding-top: 6px;">
|
||||
<div class="row"><dt>Top-level</dt><dd>1,247</dd></div>
|
||||
<div class="row"><dt>Attached</dt><dd>328</dd></div>
|
||||
<div class="row"><dt>In trash</dt><dd>12</dd></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sub-h">Entities</div>
|
||||
<div style="border-top: 1px solid var(--color-glass-border); padding-top: 6px;">
|
||||
<div class="row"><dt>Actresses</dt><dd>324</dd></div>
|
||||
<div class="row"><dt>Studios</dt><dd>52</dd></div>
|
||||
<div class="row"><dt>Series</dt><dd>18</dd></div>
|
||||
<div class="row"><dt>Genres</dt><dd>14</dd></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sub-h">Tagging</div>
|
||||
<div style="border-top: 1px solid var(--color-glass-border); padding-top: 6px;">
|
||||
<div class="row"><dt>Tags</dt><dd>186</dd></div>
|
||||
<div class="row"><dt>Tag categories</dt><dd>7</dd></div>
|
||||
<div class="row"><dt>Collections</dt><dd>14</dd></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sub-h">State</div>
|
||||
<div style="border-top: 1px solid var(--color-glass-border); padding-top: 6px;">
|
||||
<div class="row"><dt>Watched</dt><dd>432 (35%)</dd></div>
|
||||
<div class="row"><dt>VIP</dt><dd>89</dd></div>
|
||||
<div class="row"><dt>Favorite</dt><dd>156</dd></div>
|
||||
<div class="row"><dt>Owned</dt><dd>42</dd></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="divider-soft">
|
||||
<div class="sub-h">Disk</div>
|
||||
<div class="row"><dt>Total cover bytes</dt><dd>14.2 GB</dd></div>
|
||||
<div class="row"><dt>Imports</dt><dd>2024-09-12 → 2026-04-30</dd></div>
|
||||
|
||||
<hr class="divider-soft">
|
||||
<div class="sub-h">Paths</div>
|
||||
<div class="row"><dt>Library folder</dt><dd>D:\Pinkudex\library</dd></div>
|
||||
<div class="row"><dt>Database</dt><dd>D:\Pinkudex\data\library.db</dd></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="text-[11px] text-[var(--color-fg-muted)] mt-6">
|
||||
Tell me <strong>go</strong> to consolidate, or call out which sections to merge differently.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,823 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex · Settings Panel — Layout Comparison</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0a0710;
|
||||
--bg-1: #14101e;
|
||||
--bg-2: #1a1428;
|
||||
--fg: #e8e6f0;
|
||||
--fg-dim: #a8a4b8;
|
||||
--fg-muted: #6e6a80;
|
||||
--cyan: #22d3ee;
|
||||
--violet: #a78bfa;
|
||||
--mint: #34d399;
|
||||
--coral: #fb7185;
|
||||
--amber: #fbbf24;
|
||||
--glass: rgba(255,255,255,0.04);
|
||||
--glass-strong: rgba(255,255,255,0.08);
|
||||
--border: rgba(255,255,255,0.08);
|
||||
--border-strong: rgba(255,255,255,0.18);
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg-0);
|
||||
color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
/* --- Page chrome --- */
|
||||
.page {
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
}
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
padding: 16px 20px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(34,211,238,0.08), rgba(167,139,250,0.05));
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.page-header h1 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.page-header p {
|
||||
margin: 0;
|
||||
color: var(--fg-dim);
|
||||
font-size: 14px;
|
||||
}
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-bottom: 24px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.nav button {
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--fg-dim);
|
||||
padding: 8px 14px;
|
||||
font-size: 13px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.nav button:hover {
|
||||
color: var(--fg);
|
||||
background: var(--glass-strong);
|
||||
}
|
||||
.nav button.active {
|
||||
background: var(--cyan);
|
||||
color: #000;
|
||||
border-color: transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* --- Mockup frame --- */
|
||||
.mockup {
|
||||
display: none;
|
||||
}
|
||||
.mockup.active {
|
||||
display: block;
|
||||
}
|
||||
.mockup-meta {
|
||||
margin-bottom: 16px;
|
||||
padding: 14px 18px;
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
}
|
||||
.mockup-meta h2 {
|
||||
margin: 0 0 6px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--cyan);
|
||||
}
|
||||
.mockup-meta .pros, .mockup-meta .cons {
|
||||
font-size: 12px;
|
||||
color: var(--fg-dim);
|
||||
margin-top: 4px;
|
||||
}
|
||||
.mockup-meta .pros::before { content: "✓ "; color: var(--mint); font-weight: bold; }
|
||||
.mockup-meta .cons::before { content: "✗ "; color: var(--coral); font-weight: bold; }
|
||||
|
||||
/* --- The actual settings drawer mockup --- */
|
||||
.drawer {
|
||||
position: relative;
|
||||
background: rgba(10,7,16,0.96);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
.drawer-narrow { max-width: 560px; margin-left: auto; } /* old panel */
|
||||
.drawer-wide { max-width: 1024px; margin-left: auto; } /* current */
|
||||
.drawer-xwide { max-width: 1280px; margin-left: auto; } /* 3-col */
|
||||
|
||||
.drawer-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: rgba(10,7,16,0.88);
|
||||
}
|
||||
.drawer-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.drawer-title .icon {
|
||||
width: 18px; height: 18px;
|
||||
color: var(--cyan);
|
||||
}
|
||||
.drawer-close {
|
||||
width: 32px; height: 32px;
|
||||
display: grid; place-items: center;
|
||||
border-radius: 8px;
|
||||
color: var(--fg-dim);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.drawer-body {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
/* --- Section card --- */
|
||||
.section {
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
padding: 18px;
|
||||
}
|
||||
.section + .section { margin-top: 14px; }
|
||||
.section h3 {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--fg-muted);
|
||||
font-family: ui-monospace, SFMono-Regular, monospace;
|
||||
font-weight: 500;
|
||||
}
|
||||
.section.collapsible h3 {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.section.collapsible.open h3 { margin-bottom: 12px; }
|
||||
.section.collapsible .body { display: none; }
|
||||
.section.collapsible.open .body { display: block; }
|
||||
.section.collapsible .chevron::after {
|
||||
content: "▾";
|
||||
transition: transform 0.15s;
|
||||
display: inline-block;
|
||||
}
|
||||
.section.collapsible:not(.open) .chevron::after {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
/* --- Setting rows (toggles, sliders, buttons) --- */
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
.row + .row {
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.row .label { font-size: 13px; font-weight: 500; }
|
||||
.row .desc { font-size: 11px; color: var(--fg-muted); margin-top: 2px; line-height: 1.4; }
|
||||
.row .control { flex-shrink: 0; }
|
||||
|
||||
.toggle {
|
||||
width: 36px; height: 20px;
|
||||
border-radius: 999px;
|
||||
background: var(--glass-strong);
|
||||
border: 1px solid var(--border-strong);
|
||||
position: relative;
|
||||
}
|
||||
.toggle::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 2px; left: 2px;
|
||||
width: 14px; height: 14px;
|
||||
border-radius: 999px;
|
||||
background: var(--fg-dim);
|
||||
}
|
||||
.toggle.on {
|
||||
background: rgba(34,211,238,0.3);
|
||||
border-color: var(--cyan);
|
||||
}
|
||||
.toggle.on::after {
|
||||
left: 18px;
|
||||
background: var(--cyan);
|
||||
}
|
||||
|
||||
.slider-row .slider {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: var(--glass-strong);
|
||||
border-radius: 2px;
|
||||
margin-top: 8px;
|
||||
position: relative;
|
||||
}
|
||||
.slider-row .slider::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 14px; height: 14px;
|
||||
border-radius: 999px;
|
||||
background: var(--cyan);
|
||||
top: -5px;
|
||||
left: 30%;
|
||||
}
|
||||
.slider-row .value {
|
||||
font-size: 11px;
|
||||
font-family: ui-monospace, monospace;
|
||||
color: var(--cyan);
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--fg-dim);
|
||||
padding: 6px 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
.btn-primary {
|
||||
background: rgba(34,211,238,0.15);
|
||||
color: var(--cyan);
|
||||
border-color: rgba(34,211,238,0.4);
|
||||
}
|
||||
.btn-warn {
|
||||
background: rgba(251,113,133,0.1);
|
||||
color: var(--coral);
|
||||
border-color: rgba(251,113,133,0.3);
|
||||
}
|
||||
.btn-amber {
|
||||
color: var(--amber);
|
||||
border-color: rgba(251,191,36,0.4);
|
||||
}
|
||||
|
||||
.row.btn-row { padding: 10px 0; }
|
||||
.row.btn-row .label { font-size: 13px; }
|
||||
|
||||
/* --- Stat rows (Library, Storage) --- */
|
||||
dl.stat-list {
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
dl.stat-list .item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
dl.stat-list dt { color: var(--fg-dim); }
|
||||
dl.stat-list dd { margin: 0; font-family: ui-monospace, monospace; font-size: 12px; text-align: right; word-break: break-all; }
|
||||
|
||||
/* --- Tabs (mockup 2) --- */
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 0 24px;
|
||||
background: rgba(10,7,16,0.6);
|
||||
}
|
||||
.tabs button {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--fg-dim);
|
||||
padding: 12px 16px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
.tabs button.active {
|
||||
color: var(--cyan);
|
||||
border-bottom-color: var(--cyan);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* --- Sidebar nav (mockup 5) --- */
|
||||
.sidenav-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 200px 1fr;
|
||||
gap: 0;
|
||||
}
|
||||
.sidenav {
|
||||
border-right: 1px solid var(--border);
|
||||
padding: 18px 12px;
|
||||
background: rgba(10,7,16,0.5);
|
||||
}
|
||||
.sidenav .item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
color: var(--fg-dim);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.sidenav .item.active {
|
||||
background: rgba(34,211,238,0.12);
|
||||
color: var(--cyan);
|
||||
}
|
||||
.sidenav .item:hover:not(.active) {
|
||||
background: var(--glass);
|
||||
}
|
||||
.sidenav .badge {
|
||||
margin-left: auto;
|
||||
font-size: 10px;
|
||||
padding: 1px 6px;
|
||||
border-radius: 6px;
|
||||
background: var(--glass-strong);
|
||||
color: var(--fg-muted);
|
||||
font-family: ui-monospace, monospace;
|
||||
}
|
||||
.sidenav-content {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
/* --- 2-col grid (mockup 1, 4) --- */
|
||||
.grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 14px;
|
||||
}
|
||||
.grid-3 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
/* --- Section sub-icons --- */
|
||||
.icon-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: var(--fg-muted);
|
||||
}
|
||||
|
||||
/* Mini chip used to suggest a count or status */
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 1px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
font-family: ui-monospace, monospace;
|
||||
background: var(--glass-strong);
|
||||
color: var(--fg-muted);
|
||||
}
|
||||
.pill.cyan {
|
||||
background: rgba(34,211,238,0.15);
|
||||
color: var(--cyan);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<header class="page-header">
|
||||
<h1>Settings panel — six layout options</h1>
|
||||
<p>Click each option above to see how the same content reorganises. Each mockup uses identical copy and controls so you can compare information density and scan-ability.</p>
|
||||
</header>
|
||||
|
||||
<nav class="nav">
|
||||
<button data-show="m1" class="active">1. Two-column groups (refined)</button>
|
||||
<button data-show="m2">2. Top tabs</button>
|
||||
<button data-show="m3">3. Accordion</button>
|
||||
<button data-show="m4">4. Three-column wide</button>
|
||||
<button data-show="m5">5. Sidebar nav</button>
|
||||
<button data-show="m6">6. Pinned + advanced</button>
|
||||
</nav>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- MOCKUP 1: Two-column groups (refined version of current) -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mockup active" id="m1">
|
||||
<div class="mockup-meta">
|
||||
<h2>Option 1 — Two-column groups, retighter labels</h2>
|
||||
<div class="pros">Closest to today; minimal change. Better at-a-glance scan once labels are sharper.</div>
|
||||
<div class="cons">Right column still longer than left. Maintenance still scrolls within its section.</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer drawer-wide">
|
||||
<header class="drawer-header">
|
||||
<div class="drawer-title"><span class="icon">⚙</span> Settings</div>
|
||||
<div class="drawer-close">×</div>
|
||||
</header>
|
||||
<div class="drawer-body">
|
||||
<div class="grid-2">
|
||||
<!-- LEFT col -->
|
||||
<div>
|
||||
<section class="section">
|
||||
<h3>Appearance</h3>
|
||||
<div class="row">
|
||||
<div><div class="label">Default sort</div><div class="desc">New library views start in this order.</div></div>
|
||||
<div class="control"><button class="btn">Newest First ▾</button></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div><div class="label">Accent — Primary</div><div class="desc">Cyan highlights & active state.</div></div>
|
||||
<div class="control"><button class="btn">#22D3EE</button></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div><div class="label">Accent — Secondary</div><div class="desc">Tag pills & secondary chrome.</div></div>
|
||||
<div class="control"><button class="btn">#A78BFA</button></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h3>Display</h3>
|
||||
<div class="row slider-row">
|
||||
<div><div class="label">Grid columns (Landscape)</div><div class="desc">Cover columns in L-mode.</div></div>
|
||||
<div class="control"><span class="value">3 per row</span></div>
|
||||
</div>
|
||||
<div class="row slider-row">
|
||||
<div><div class="label">Grid columns (Portrait)</div><div class="desc">Cover columns in P-mode.</div></div>
|
||||
<div class="control"><span class="value">6 per row</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div><div class="label">Fade transitions</div><div class="desc">Soft cross-fade on navigation.</div></div>
|
||||
<div class="control"><div class="toggle on"></div></div>
|
||||
</div>
|
||||
<div class="row slider-row">
|
||||
<div><div class="label">Fade duration</div><div class="desc"></div></div>
|
||||
<div class="control"><span class="value">400ms</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h3>Library</h3>
|
||||
<dl class="stat-list">
|
||||
<div class="item"><dt>Images</dt><dd>422</dd></div>
|
||||
<div class="item"><dt>Tags</dt><dd>38</dd></div>
|
||||
<div class="item"><dt>Collections</dt><dd>12</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h3>Storage</h3>
|
||||
<dl class="stat-list">
|
||||
<div class="item"><dt>Library folder</dt><dd>D:/.../library</dd></div>
|
||||
<div class="item"><dt>Database</dt><dd>D:/.../data/library.db</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT col -->
|
||||
<div>
|
||||
<section class="section">
|
||||
<h3>Trash & deletion</h3>
|
||||
<div class="row">
|
||||
<div><div class="label">Use recycle bin</div><div class="desc">Soft-delete to bin instead of permanent.</div></div>
|
||||
<div class="control"><div class="toggle on"></div></div>
|
||||
</div>
|
||||
<div class="row slider-row">
|
||||
<div><div class="label">Trash retention</div><div class="desc">Auto-purge after N days. 0 = never.</div></div>
|
||||
<div class="control"><span class="value">30 days</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div><div class="label">Delete files from disk</div><div class="desc">When emptying trash, unlink originals + thumbs.</div></div>
|
||||
<div class="control"><div class="toggle on"></div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h3>Maintenance</h3>
|
||||
<div class="row btn-row"><div><div class="label">Re-organize library</div><div class="desc">Move covers into letter buckets.</div></div><div class="control"><button class="btn">Scan</button></div></div>
|
||||
<div class="row btn-row"><div><div class="label">Regenerate thumbnails</div><div class="desc">Rebuild WebP previews. Migrates legacy filenames.</div></div><div class="control"><button class="btn">Scan</button></div></div>
|
||||
<div class="row btn-row"><div><div class="label">Find undersized covers</div><div class="desc">Flag covers under 750×500.</div></div><div class="control"><button class="btn">Scan</button></div></div>
|
||||
<div class="row btn-row"><div><div class="label">Purge orphan files</div><div class="desc">Remove on-disk files not referenced by DB.</div></div><div class="control"><button class="btn">Scan</button></div></div>
|
||||
<div class="row btn-row"><div><div class="label">Clear cache</div><div class="desc">Drop all server-side cached pages.</div></div><div class="control"><button class="btn">Clear</button></div></div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h3>Backup</h3>
|
||||
<div class="row btn-row">
|
||||
<div><div class="label">Export & import</div><div class="desc">JSON dump of all metadata. Image files copy separately.</div></div>
|
||||
<div class="control" style="display:flex; gap:6px;">
|
||||
<button class="btn btn-primary">Export</button>
|
||||
<button class="btn btn-amber">Import…</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- MOCKUP 2: Top tabs -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mockup" id="m2">
|
||||
<div class="mockup-meta">
|
||||
<h2>Option 2 — Top tabs (General · Display · Trash · Maintenance · Backup · About)</h2>
|
||||
<div class="pros">Tightest visual: only one section visible at a time. No scroll for any single tab.</div>
|
||||
<div class="cons">More clicks; can hide settings you forgot exist. Best when settings rarely-used overall.</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer drawer-narrow">
|
||||
<header class="drawer-header">
|
||||
<div class="drawer-title"><span class="icon">⚙</span> Settings</div>
|
||||
<div class="drawer-close">×</div>
|
||||
</header>
|
||||
<div class="tabs">
|
||||
<button>General</button>
|
||||
<button class="active">Display</button>
|
||||
<button>Trash</button>
|
||||
<button>Maintenance</button>
|
||||
<button>Backup</button>
|
||||
<button>About</button>
|
||||
</div>
|
||||
<div class="drawer-body">
|
||||
<section class="section">
|
||||
<h3>Display</h3>
|
||||
<div class="row slider-row"><div><div class="label">Grid columns (Landscape)</div><div class="desc">Cover columns in L-mode.</div></div><div class="control"><span class="value">3 per row</span></div></div>
|
||||
<div class="row slider-row"><div><div class="label">Grid columns (Portrait)</div><div class="desc">Cover columns in P-mode.</div></div><div class="control"><span class="value">6 per row</span></div></div>
|
||||
<div class="row"><div><div class="label">Fade transitions</div><div class="desc">Soft cross-fade on navigation.</div></div><div class="control"><div class="toggle on"></div></div></div>
|
||||
<div class="row slider-row"><div><div class="label">Fade duration</div><div class="desc">How long the fade lasts.</div></div><div class="control"><span class="value">400ms</span></div></div>
|
||||
</section>
|
||||
<p style="font-size:12px; color: var(--fg-muted); margin-top: 16px; padding: 0 4px;">
|
||||
(Showing one tab at a time — switch tabs above for the others.)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- MOCKUP 3: Accordion -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mockup" id="m3">
|
||||
<div class="mockup-meta">
|
||||
<h2>Option 3 — Accordion / collapsible sections</h2>
|
||||
<div class="pros">Skim all section names at once; expand only what you need. Section count stays visible.</div>
|
||||
<div class="cons">Extra click per section. Scroll re-flows when sections open/close.</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer drawer-narrow">
|
||||
<header class="drawer-header">
|
||||
<div class="drawer-title"><span class="icon">⚙</span> Settings</div>
|
||||
<div class="drawer-close">×</div>
|
||||
</header>
|
||||
<div class="drawer-body">
|
||||
<section class="section collapsible open">
|
||||
<h3>Defaults <span class="chevron"></span></h3>
|
||||
<div class="body">
|
||||
<div class="row"><div><div class="label">Default sort</div></div><div class="control"><button class="btn">Newest First ▾</button></div></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section collapsible">
|
||||
<h3>Theme <span class="chevron"></span></h3>
|
||||
</section>
|
||||
<section class="section collapsible open">
|
||||
<h3>Display <span class="chevron"></span></h3>
|
||||
<div class="body">
|
||||
<div class="row slider-row"><div><div class="label">Grid columns (Landscape)</div></div><div class="control"><span class="value">3 per row</span></div></div>
|
||||
<div class="row slider-row"><div><div class="label">Grid columns (Portrait)</div></div><div class="control"><span class="value">6 per row</span></div></div>
|
||||
<div class="row"><div><div class="label">Fade transitions</div></div><div class="control"><div class="toggle on"></div></div></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section collapsible">
|
||||
<h3>Trash & deletion <span class="chevron"></span></h3>
|
||||
</section>
|
||||
<section class="section collapsible">
|
||||
<h3>Maintenance <span class="pill cyan">5</span> <span class="chevron"></span></h3>
|
||||
</section>
|
||||
<section class="section collapsible">
|
||||
<h3>Backup <span class="chevron"></span></h3>
|
||||
</section>
|
||||
<section class="section collapsible">
|
||||
<h3>Library & storage <span class="chevron"></span></h3>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- MOCKUP 4: Three-column wide -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mockup" id="m4">
|
||||
<div class="mockup-meta">
|
||||
<h2>Option 4 — Three columns at full width</h2>
|
||||
<div class="pros">Almost everything visible without scrolling. Great on a wide monitor.</div>
|
||||
<div class="cons">Drawer takes up more screen real estate. Awkward on smaller laptops.</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer drawer-xwide">
|
||||
<header class="drawer-header">
|
||||
<div class="drawer-title"><span class="icon">⚙</span> Settings</div>
|
||||
<div class="drawer-close">×</div>
|
||||
</header>
|
||||
<div class="drawer-body">
|
||||
<div class="grid-3">
|
||||
<!-- COL 1 -->
|
||||
<div>
|
||||
<section class="section">
|
||||
<h3>Defaults & Theme</h3>
|
||||
<div class="row"><div><div class="label">Default sort</div></div><div class="control"><button class="btn">Newest First ▾</button></div></div>
|
||||
<div class="row"><div><div class="label">Accent primary</div></div><div class="control"><button class="btn">#22D3EE</button></div></div>
|
||||
<div class="row"><div><div class="label">Accent secondary</div></div><div class="control"><button class="btn">#A78BFA</button></div></div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<h3>Display</h3>
|
||||
<div class="row slider-row"><div><div class="label">Grid cols — Landscape</div></div><div class="control"><span class="value">3</span></div></div>
|
||||
<div class="row slider-row"><div><div class="label">Grid cols — Portrait</div></div><div class="control"><span class="value">6</span></div></div>
|
||||
<div class="row"><div><div class="label">Fade transitions</div></div><div class="control"><div class="toggle on"></div></div></div>
|
||||
<div class="row slider-row"><div><div class="label">Fade duration</div></div><div class="control"><span class="value">400ms</span></div></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- COL 2 -->
|
||||
<div>
|
||||
<section class="section">
|
||||
<h3>Trash & deletion</h3>
|
||||
<div class="row"><div><div class="label">Use recycle bin</div></div><div class="control"><div class="toggle on"></div></div></div>
|
||||
<div class="row slider-row"><div><div class="label">Retention</div></div><div class="control"><span class="value">30d</span></div></div>
|
||||
<div class="row"><div><div class="label">Delete from disk</div></div><div class="control"><div class="toggle on"></div></div></div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<h3>Library</h3>
|
||||
<dl class="stat-list">
|
||||
<div class="item"><dt>Images</dt><dd>422</dd></div>
|
||||
<div class="item"><dt>Tags</dt><dd>38</dd></div>
|
||||
<div class="item"><dt>Collections</dt><dd>12</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
<section class="section">
|
||||
<h3>Storage</h3>
|
||||
<dl class="stat-list">
|
||||
<div class="item"><dt>Library</dt><dd>D:/.../library</dd></div>
|
||||
<div class="item"><dt>Database</dt><dd>D:/.../library.db</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- COL 3 -->
|
||||
<div>
|
||||
<section class="section">
|
||||
<h3>Maintenance</h3>
|
||||
<div class="row btn-row"><div><div class="label">Re-organize</div><div class="desc">Letter buckets.</div></div><div class="control"><button class="btn">Scan</button></div></div>
|
||||
<div class="row btn-row"><div><div class="label">Regen thumbnails</div><div class="desc">Rebuild WebP.</div></div><div class="control"><button class="btn">Scan</button></div></div>
|
||||
<div class="row btn-row"><div><div class="label">Find undersized</div><div class="desc">Flag <750×500.</div></div><div class="control"><button class="btn">Scan</button></div></div>
|
||||
<div class="row btn-row"><div><div class="label">Purge orphans</div></div><div class="control"><button class="btn">Scan</button></div></div>
|
||||
<div class="row btn-row"><div><div class="label">Clear cache</div></div><div class="control"><button class="btn">Clear</button></div></div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<h3>Backup</h3>
|
||||
<div class="row btn-row">
|
||||
<div><div class="label">JSON metadata dump</div><div class="desc">Image files copy separately.</div></div>
|
||||
</div>
|
||||
<div style="display:flex; gap:6px; margin-top: 6px;">
|
||||
<button class="btn btn-primary">Export</button>
|
||||
<button class="btn btn-amber">Import…</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- MOCKUP 5: Sidebar nav -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mockup" id="m5">
|
||||
<div class="mockup-meta">
|
||||
<h2>Option 5 — Vertical sidebar nav inside the panel (macOS / VSCode style)</h2>
|
||||
<div class="pros">Beautiful focused single-section view. Sidebar count badges hint at activity.</div>
|
||||
<div class="cons">More clicks. Padding-heavy at narrow drawer width — needs the wide drawer.</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer drawer-wide">
|
||||
<header class="drawer-header">
|
||||
<div class="drawer-title"><span class="icon">⚙</span> Settings</div>
|
||||
<div class="drawer-close">×</div>
|
||||
</header>
|
||||
<div class="sidenav-layout">
|
||||
<nav class="sidenav">
|
||||
<div class="item">⊕ General</div>
|
||||
<div class="item active">▭ Display</div>
|
||||
<div class="item">⌫ Trash <span class="badge">3</span></div>
|
||||
<div class="item">🔧 Maintenance <span class="badge">5</span></div>
|
||||
<div class="item">⤓ Backup</div>
|
||||
<div class="item">📚 Library stats</div>
|
||||
<div class="item">📁 Storage</div>
|
||||
</nav>
|
||||
<div class="sidenav-content">
|
||||
<h2 style="margin: 0 0 4px 0; font-size: 18px;">Display</h2>
|
||||
<p style="margin: 0 0 18px 0; font-size: 13px; color: var(--fg-muted);">Grid density, transitions, and the front-only portrait toggle.</p>
|
||||
<section class="section">
|
||||
<h3>Grid columns</h3>
|
||||
<div class="row slider-row"><div><div class="label">Landscape (full cover)</div><div class="desc">L-mode card count.</div></div><div class="control"><span class="value">3 per row</span></div></div>
|
||||
<div class="row slider-row"><div><div class="label">Portrait (front-only)</div><div class="desc">P-mode card count.</div></div><div class="control"><span class="value">6 per row</span></div></div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<h3>Animations</h3>
|
||||
<div class="row"><div><div class="label">Fade transitions</div><div class="desc">Soft cross-fade on navigation.</div></div><div class="control"><div class="toggle on"></div></div></div>
|
||||
<div class="row slider-row"><div><div class="label">Fade duration</div></div><div class="control"><span class="value">400ms</span></div></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- MOCKUP 6: Pinned + Advanced -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mockup" id="m6">
|
||||
<div class="mockup-meta">
|
||||
<h2>Option 6 — Pinned common settings, advanced collapsed by default</h2>
|
||||
<div class="pros">Surfaces 90% of what you use; hides the destructive/rarely-used 10%. Lowest cognitive load.</div>
|
||||
<div class="cons">"Advanced" implies expert-only — folks who'd benefit from re-organize won't go looking.</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer drawer-narrow">
|
||||
<header class="drawer-header">
|
||||
<div class="drawer-title"><span class="icon">⚙</span> Settings</div>
|
||||
<div class="drawer-close">×</div>
|
||||
</header>
|
||||
<div class="drawer-body">
|
||||
<section class="section">
|
||||
<h3>Common</h3>
|
||||
<div class="row"><div><div class="label">Default sort</div></div><div class="control"><button class="btn">Newest First ▾</button></div></div>
|
||||
<div class="row slider-row"><div><div class="label">Grid cols — Landscape</div></div><div class="control"><span class="value">3</span></div></div>
|
||||
<div class="row slider-row"><div><div class="label">Grid cols — Portrait</div></div><div class="control"><span class="value">6</span></div></div>
|
||||
<div class="row"><div><div class="label">Use recycle bin</div></div><div class="control"><div class="toggle on"></div></div></div>
|
||||
<div class="row"><div><div class="label">Fade transitions</div></div><div class="control"><div class="toggle on"></div></div></div>
|
||||
<div class="row"><div><div class="label">Accent — Primary</div></div><div class="control"><button class="btn">#22D3EE</button></div></div>
|
||||
</section>
|
||||
|
||||
<section class="section collapsible">
|
||||
<h3>Advanced — Theme, accents, fade duration <span class="chevron"></span></h3>
|
||||
</section>
|
||||
|
||||
<section class="section collapsible">
|
||||
<h3>Maintenance — Reorganize, regen, scans <span class="pill cyan">5</span> <span class="chevron"></span></h3>
|
||||
</section>
|
||||
|
||||
<section class="section collapsible">
|
||||
<h3>Backup — Export & import <span class="chevron"></span></h3>
|
||||
</section>
|
||||
|
||||
<section class="section collapsible">
|
||||
<h3>Library & storage info <span class="chevron"></span></h3>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.querySelectorAll(".nav button").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const target = btn.dataset.show;
|
||||
document.querySelectorAll(".nav button").forEach((b) => b.classList.toggle("active", b === btn));
|
||||
document.querySelectorAll(".mockup").forEach((m) => m.classList.toggle("active", m.id === target));
|
||||
});
|
||||
});
|
||||
|
||||
// Tabs in mockup 2
|
||||
document.querySelectorAll(".tabs button").forEach((b) => {
|
||||
b.addEventListener("click", () => {
|
||||
b.parentElement.querySelectorAll("button").forEach((x) => x.classList.toggle("active", x === b));
|
||||
});
|
||||
});
|
||||
|
||||
// Accordion toggle
|
||||
document.querySelectorAll(".section.collapsible h3").forEach((h) => {
|
||||
h.addEventListener("click", () => {
|
||||
h.parentElement.classList.toggle("open");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,400 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Settings Modal — Spacing Pass (Layout Untouched)</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
--color-bg-0: oklch(0.13 0.025 280);
|
||||
--color-bg-1: oklch(0.17 0.04 285);
|
||||
--color-fg: oklch(0.97 0.01 280);
|
||||
--color-fg-dim: oklch(0.72 0.025 280);
|
||||
--color-fg-muted: oklch(0.55 0.02 280);
|
||||
--color-glass: color-mix(in oklch, white 6%, transparent);
|
||||
--color-glass-strong: color-mix(in oklch, white 10%, transparent);
|
||||
--color-glass-border: color-mix(in oklch, white 14%, transparent);
|
||||
--color-glass-border-strong: color-mix(in oklch, white 22%, transparent);
|
||||
--color-cyan: oklch(0.82 0.16 200);
|
||||
--color-violet: oklch(0.72 0.22 305);
|
||||
--color-mint: oklch(0.80 0.16 155);
|
||||
--color-coral: oklch(0.72 0.20 25);
|
||||
|
||||
--spacing-card: 15px;
|
||||
--spacing-section: 15px;
|
||||
--spacing-card-gap: 9px;
|
||||
--spacing-chip: 7px;
|
||||
--spacing-label: 7px;
|
||||
}
|
||||
body {
|
||||
background: var(--color-bg-0);
|
||||
color: var(--color-fg);
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
.glass { background: var(--color-glass); border: 1px solid var(--color-glass-border); }
|
||||
.why { font-size: 12px; color: var(--color-fg-muted); }
|
||||
.label-mono { font-family: ui-monospace, monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-fg-muted); }
|
||||
|
||||
.pg {
|
||||
background: oklch(0.10 0.025 280);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pg-h {
|
||||
padding: 12px 16px;
|
||||
background: oklch(0.20 0.04 285);
|
||||
border-bottom: 1px solid var(--color-glass-border);
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 12px;
|
||||
color: var(--color-fg-dim);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.pg-h .tag { color: var(--color-cyan); font-weight: 600; }
|
||||
.pg-body { padding: 18px; }
|
||||
|
||||
/* Modal frame — same as Trash + Settings + Queue panels */
|
||||
.modal-frame {
|
||||
background: color-mix(in oklch, var(--color-bg-0) 96%, transparent);
|
||||
border: 1px solid var(--color-glass-border-strong);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
|
||||
display: flex; flex-direction: column;
|
||||
height: 600px; /* mockup-only height for legibility */
|
||||
}
|
||||
.modal-header {
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid var(--color-glass-border);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
width: 100%;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--color-fg-dim);
|
||||
background: transparent;
|
||||
margin-bottom: 2px;
|
||||
text-align: left;
|
||||
}
|
||||
.nav-btn.active {
|
||||
background: oklch(0.82 0.16 200 / 0.15);
|
||||
color: var(--color-cyan);
|
||||
}
|
||||
|
||||
.stat-row { display: flex; justify-content: space-between; gap: 16px; align-items: baseline; }
|
||||
.stat-row dt { color: var(--color-fg-dim); font-size: 13px; }
|
||||
.stat-row dd { color: var(--color-fg); font-family: ui-monospace, monospace; font-size: 12px; }
|
||||
|
||||
.pill-sm {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 0 10px; height: 28px;
|
||||
border-radius: 6px; font-size: 12px;
|
||||
border: 1px solid var(--color-glass-border); color: var(--color-fg-dim);
|
||||
background: var(--color-glass);
|
||||
}
|
||||
.pill-sm.active { background: var(--color-cyan); color: black; border-color: transparent; font-weight: 600; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="space-y-6 max-w-[1600px] mx-auto p-6">
|
||||
|
||||
<header class="space-y-1">
|
||||
<h1 class="text-2xl font-semibold tracking-tight">Settings Modal · Spacing Pass</h1>
|
||||
<p class="why max-w-[820px]">
|
||||
Modal frame is already on the new shared shell (1400×900, same as Trash + Watch Queue).
|
||||
Only the internal Card primitive + section content blocks adjust their padding/margins.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- ====================================================================
|
||||
Audit
|
||||
==================================================================== -->
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<div class="label-mono" style="margin-bottom: 8px;">Spacing changes — Card primitive + nested layouts</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-2 text-[12px] font-mono text-[var(--color-fg-dim)]">
|
||||
<div>1 · <strong class="text-[var(--color-fg)]">Card</strong> (line 298) padding</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">p-5</code> 20 px → <code class="text-[var(--color-cyan)]">p-card</code> 15 px</div>
|
||||
<div>2 · Card title margin</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">mb-3</code> 12 px → <code class="text-[var(--color-cyan)]">mb-label</code> 7 px</div>
|
||||
<div>3 · sidebar content scroll-area</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">p-6</code> 24 px → <code class="text-[var(--color-cyan)]">p-card</code> 15 px</div>
|
||||
<div>4 · 3-column grid gap</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">gap-4</code> 16 px → <code class="text-[var(--color-cyan)]">gap-card-gap</code> 9 px</div>
|
||||
<div>5 · 3-col column inner stacking</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">space-y-4</code> 16 px → <code class="text-[var(--color-cyan)]">gap-card-gap</code> 9 px</div>
|
||||
<div>6 · Library section inner stat groups</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">space-y-4</code> 16 px → <code class="text-[var(--color-cyan)]">gap-section</code> 15 px</div>
|
||||
<div>7 · StatGroup label margin</div>
|
||||
<div><code class="text-[var(--color-fg-muted)]">mb-1.5</code> 6 px → <code class="text-[var(--color-cyan)]">mb-label</code> 7 px</div>
|
||||
</div>
|
||||
<p class="why mt-3">
|
||||
<strong>Untouched:</strong> modal frame (already 1400×900 shared shell), header padding, sidebar nav width 220 px, nav button <code class="text-[var(--color-cyan)]">px-3 py-2</code> + <code class="text-[var(--color-cyan)]">mb-0.5</code>, StatGroup dl <code class="text-[var(--color-cyan)]">space-y-1.5</code> + <code class="text-[var(--color-cyan)]">pt-1.5</code>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================
|
||||
BEFORE — Sidebar layout
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag">BEFORE</span> · Sidebar layout</span><span>Card p-5 · mb-3 · content p-6</span></div>
|
||||
<div class="pg-body">
|
||||
<div class="modal-frame" style="max-width: 1100px; margin: 0 auto;">
|
||||
<div class="modal-header">
|
||||
<div style="display:flex; align-items:center; gap: 8px;">
|
||||
<span style="color: var(--color-cyan); font-size: 18px;">⚙</span>
|
||||
<h2 class="text-xl font-semibold tracking-tight">Settings</h2>
|
||||
</div>
|
||||
<span style="width: 30px; height: 30px; border-radius: 6px; display:grid; place-items:center; color: var(--color-fg-dim);">✕</span>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 220px 1fr; flex: 1; min-height: 0;">
|
||||
<nav style="padding: 12px; border-right: 1px solid var(--color-glass-border); background: oklch(0.13 0.025 280 / 0.5); overflow-y: auto;">
|
||||
<button class="nav-btn"><span>⊟</span><span>General</span></button>
|
||||
<button class="nav-btn active"><span>🎨</span><span>Theme</span></button>
|
||||
<button class="nav-btn"><span>▦</span><span>Display</span></button>
|
||||
<button class="nav-btn"><span>🗑</span><span>Trash</span></button>
|
||||
<button class="nav-btn"><span>▶</span><span>Video Library</span></button>
|
||||
<button class="nav-btn"><span>⚒</span><span>Maintenance</span></button>
|
||||
</nav>
|
||||
<!-- Content area: p-6 (24px) -->
|
||||
<div style="overflow-y: auto; padding: 24px;">
|
||||
<!-- Card: p-5, header mb-3 -->
|
||||
<section class="glass rounded-2xl" style="padding: 20px;">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 12px;">Theme</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Accent · primary</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<span style="width: 28px; height: 28px; border-radius: 6px; background: var(--color-cyan);"></span>
|
||||
<span class="pill-sm" style="font-family: ui-monospace, monospace;">#8edcff</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Accent · secondary</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<span style="width: 28px; height: 28px; border-radius: 6px; background: var(--color-violet);"></span>
|
||||
<span class="pill-sm" style="font-family: ui-monospace, monospace;">#bd8eff</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
AFTER — Sidebar layout
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">AFTER</span> · Sidebar layout</span><span>Card p-card · mb-label · content p-card</span></div>
|
||||
<div class="pg-body">
|
||||
<div class="modal-frame" style="max-width: 1100px; margin: 0 auto;">
|
||||
<div class="modal-header">
|
||||
<div style="display:flex; align-items:center; gap: 8px;">
|
||||
<span style="color: var(--color-cyan); font-size: 18px;">⚙</span>
|
||||
<h2 class="text-xl font-semibold tracking-tight">Settings</h2>
|
||||
</div>
|
||||
<span style="width: 30px; height: 30px; border-radius: 6px; display:grid; place-items:center; color: var(--color-fg-dim);">✕</span>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 220px 1fr; flex: 1; min-height: 0;">
|
||||
<nav style="padding: 12px; border-right: 1px solid var(--color-glass-border); background: oklch(0.13 0.025 280 / 0.5); overflow-y: auto;">
|
||||
<button class="nav-btn"><span>⊟</span><span>General</span></button>
|
||||
<button class="nav-btn active"><span>🎨</span><span>Theme</span></button>
|
||||
<button class="nav-btn"><span>▦</span><span>Display</span></button>
|
||||
<button class="nav-btn"><span>🗑</span><span>Trash</span></button>
|
||||
<button class="nav-btn"><span>▶</span><span>Video Library</span></button>
|
||||
<button class="nav-btn"><span>⚒</span><span>Maintenance</span></button>
|
||||
</nav>
|
||||
<!-- Content area: p-card (15px) -->
|
||||
<div style="overflow-y: auto; padding: var(--spacing-card);">
|
||||
<!-- Card: p-card, header mb-label -->
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: var(--spacing-label);">Theme</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Accent · primary</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<span style="width: 28px; height: 28px; border-radius: 6px; background: var(--color-cyan);"></span>
|
||||
<span class="pill-sm" style="font-family: ui-monospace, monospace;">#8edcff</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Accent · secondary</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<span style="width: 28px; height: 28px; border-radius: 6px; background: var(--color-violet);"></span>
|
||||
<span class="pill-sm" style="font-family: ui-monospace, monospace;">#bd8eff</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
BEFORE — Three-column layout
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag">BEFORE</span> · Three-column layout</span><span>p-6 · gap-4 · column space-y-4</span></div>
|
||||
<div class="pg-body">
|
||||
<div class="modal-frame" style="height: 580px;">
|
||||
<div class="modal-header">
|
||||
<div style="display:flex; align-items:center; gap: 8px;">
|
||||
<span style="color: var(--color-cyan); font-size: 18px;">⚙</span>
|
||||
<h2 class="text-xl font-semibold tracking-tight">Settings</h2>
|
||||
</div>
|
||||
<span style="width: 30px; height: 30px; border-radius: 6px; display:grid; place-items:center; color: var(--color-fg-dim);">✕</span>
|
||||
</div>
|
||||
<div style="overflow-y: auto; padding: 24px;">
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: start;">
|
||||
<!-- Column 1 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 16px;">
|
||||
<section class="glass rounded-2xl" style="padding: 20px;">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 12px;">Defaults</h3>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Default sort</div>
|
||||
<div style="display:flex; gap: 6px;"><span class="pill-sm active">Newest</span><span class="pill-sm">Title</span></div>
|
||||
</section>
|
||||
<section class="glass rounded-2xl" style="padding: 20px;">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 12px;">Theme</h3>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Accent</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<span style="width: 28px; height: 28px; border-radius: 6px; background: var(--color-cyan);"></span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<!-- Column 2 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 16px;">
|
||||
<section class="glass rounded-2xl" style="padding: 20px;">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 12px;">Trash</h3>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Retention days</div>
|
||||
<div style="display:flex; gap: 6px;"><span class="pill-sm">7</span><span class="pill-sm active">30</span><span class="pill-sm">∞</span></div>
|
||||
</section>
|
||||
<section class="glass rounded-2xl" style="padding: 20px;">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 12px;">Library</h3>
|
||||
<!-- LibrarySection inner: space-y-4 (16px) between StatGroups -->
|
||||
<div style="display: flex; flex-direction: column; gap: 16px;">
|
||||
<div>
|
||||
<!-- StatGroup label: mb-1.5 (6px) -->
|
||||
<div style="font-family: ui-monospace, monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-cyan); margin-bottom: 6px;">Covers</div>
|
||||
<dl style="border-top: 1px solid var(--color-glass-border); padding-top: 6px; display: flex; flex-direction: column; gap: 6px;">
|
||||
<div class="stat-row"><dt>Top-level</dt><dd>1,247</dd></div>
|
||||
<div class="stat-row"><dt>Attached</dt><dd>328</dd></div>
|
||||
</dl>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-family: ui-monospace, monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-cyan); margin-bottom: 6px;">Entities</div>
|
||||
<dl style="border-top: 1px solid var(--color-glass-border); padding-top: 6px; display: flex; flex-direction: column; gap: 6px;">
|
||||
<div class="stat-row"><dt>Actresses</dt><dd>324</dd></div>
|
||||
<div class="stat-row"><dt>Studios</dt><dd>52</dd></div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<!-- Column 3 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 16px;">
|
||||
<section class="glass rounded-2xl" style="padding: 20px;">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 12px;">Video Library</h3>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Library path</div>
|
||||
<div class="glass rounded-lg" style="padding: 8px 10px; font-size: 12px; font-family: monospace; color: var(--color-fg-dim);">D:\JAV</div>
|
||||
</section>
|
||||
<section class="glass rounded-2xl" style="padding: 20px;">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: 12px;">Maintenance</h3>
|
||||
<div style="display: flex; gap: 6px;"><span class="pill-sm">↻ Re-scan</span><span class="pill-sm">⌗ Backfill phash</span></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================
|
||||
AFTER — Three-column layout
|
||||
==================================================================== -->
|
||||
<div class="pg">
|
||||
<div class="pg-h"><span><span class="tag" style="color: var(--color-mint);">AFTER</span> · Three-column layout</span><span>p-card · gap-card-gap · column gap-card-gap</span></div>
|
||||
<div class="pg-body">
|
||||
<div class="modal-frame" style="height: 580px;">
|
||||
<div class="modal-header">
|
||||
<div style="display:flex; align-items:center; gap: 8px;">
|
||||
<span style="color: var(--color-cyan); font-size: 18px;">⚙</span>
|
||||
<h2 class="text-xl font-semibold tracking-tight">Settings</h2>
|
||||
</div>
|
||||
<span style="width: 30px; height: 30px; border-radius: 6px; display:grid; place-items:center; color: var(--color-fg-dim);">✕</span>
|
||||
</div>
|
||||
<div style="overflow-y: auto; padding: var(--spacing-card);">
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--spacing-card-gap); align-items: start;">
|
||||
<!-- Column 1 -->
|
||||
<div style="display: flex; flex-direction: column; gap: var(--spacing-card-gap);">
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: var(--spacing-label);">Defaults</h3>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Default sort</div>
|
||||
<div style="display:flex; gap: 6px;"><span class="pill-sm active">Newest</span><span class="pill-sm">Title</span></div>
|
||||
</section>
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: var(--spacing-label);">Theme</h3>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Accent</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<span style="width: 28px; height: 28px; border-radius: 6px; background: var(--color-cyan);"></span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<!-- Column 2 -->
|
||||
<div style="display: flex; flex-direction: column; gap: var(--spacing-card-gap);">
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: var(--spacing-label);">Trash</h3>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Retention days</div>
|
||||
<div style="display:flex; gap: 6px;"><span class="pill-sm">7</span><span class="pill-sm active">30</span><span class="pill-sm">∞</span></div>
|
||||
</section>
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: var(--spacing-label);">Library</h3>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--spacing-section);">
|
||||
<div>
|
||||
<div style="font-family: ui-monospace, monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-cyan); margin-bottom: var(--spacing-label);">Covers</div>
|
||||
<dl style="border-top: 1px solid var(--color-glass-border); padding-top: 6px; display: flex; flex-direction: column; gap: 6px;">
|
||||
<div class="stat-row"><dt>Top-level</dt><dd>1,247</dd></div>
|
||||
<div class="stat-row"><dt>Attached</dt><dd>328</dd></div>
|
||||
</dl>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-family: ui-monospace, monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-cyan); margin-bottom: var(--spacing-label);">Entities</div>
|
||||
<dl style="border-top: 1px solid var(--color-glass-border); padding-top: 6px; display: flex; flex-direction: column; gap: 6px;">
|
||||
<div class="stat-row"><dt>Actresses</dt><dd>324</dd></div>
|
||||
<div class="stat-row"><dt>Studios</dt><dd>52</dd></div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<!-- Column 3 -->
|
||||
<div style="display: flex; flex-direction: column; gap: var(--spacing-card-gap);">
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: var(--spacing-label);">Video Library</h3>
|
||||
<div class="label-mono" style="margin-bottom: 6px;">Library path</div>
|
||||
<div class="glass rounded-lg" style="padding: 8px 10px; font-size: 12px; font-family: monospace; color: var(--color-fg-dim);">D:\JAV</div>
|
||||
</section>
|
||||
<section class="glass rounded-2xl" style="padding: var(--spacing-card);">
|
||||
<h3 style="font-family: ui-monospace, monospace; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-fg-muted); margin-bottom: var(--spacing-label);">Maintenance</h3>
|
||||
<div style="display: flex; gap: 6px;"><span class="pill-sm">↻ Re-scan</span><span class="pill-sm">⌗ Backfill phash</span></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="text-[11px] text-[var(--color-fg-muted)]">
|
||||
Tell me <strong>go</strong> to apply, or point at anything you want different.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,473 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex — Status Filter Layouts</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0e0a14;
|
||||
--bg-1: #18121f;
|
||||
--fg: #ece6f5;
|
||||
--fg-dim: #b9b1c6;
|
||||
--fg-muted: #7d7388;
|
||||
--cyan: #4dd9e6;
|
||||
--violet: #b87cf6;
|
||||
--coral: #ff7a8a;
|
||||
--mint: #79e6b2;
|
||||
--amber: #fbbf24;
|
||||
--glass-border: #2a2434;
|
||||
--glass-border-strong: #3d3548;
|
||||
--glass: rgba(40, 32, 56, 0.5);
|
||||
--glass-strong: rgba(56, 46, 76, 0.7);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html, body {
|
||||
margin: 0;
|
||||
background: radial-gradient(1200px 600px at 70% -10%, rgba(184,124,246,0.08), transparent),
|
||||
radial-gradient(800px 500px at 10% 110%, rgba(77,217,230,0.06), transparent),
|
||||
var(--bg-0);
|
||||
color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
.page { max-width: 1600px; margin: 0 auto; padding: 32px 24px 96px; }
|
||||
h1 { font-size: 28px; margin: 0 0 8px; font-weight: 600; letter-spacing: -0.01em; }
|
||||
.lede { color: var(--fg-dim); margin: 0 0 32px; max-width: 820px; line-height: 1.5; }
|
||||
h2 {
|
||||
margin: 32px 0 4px;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--cyan);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
.desc { color: var(--fg-dim); font-size: 13px; margin: 0 0 12px; max-width: 820px; line-height: 1.5; }
|
||||
.stage {
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
margin-bottom: 24px;
|
||||
position: relative;
|
||||
}
|
||||
.stage-label {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--fg-muted);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding-bottom: 220px; }
|
||||
.chip {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 6px 12px; border-radius: 9999px;
|
||||
border: 1px solid var(--glass-border); background: var(--glass);
|
||||
color: var(--fg-dim); font-size: 14px; cursor: default;
|
||||
font-family: inherit;
|
||||
}
|
||||
.chip.active-coral { background: rgba(255,122,138,0.12); border-color: rgba(255,122,138,0.4); color: var(--coral); }
|
||||
.chip.active-cyan { background: rgba(77,217,230,0.15); border-color: rgba(77,217,230,0.4); color: var(--cyan); }
|
||||
.badge {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 22px; height: 16px; border-radius: 9999px;
|
||||
color: #000; font-size: 10px; font-weight: 700;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
.badge.coral { background: var(--coral); }
|
||||
.badge.cyan { background: var(--cyan); }
|
||||
|
||||
.popup {
|
||||
margin-top: 8px;
|
||||
background: var(--bg-0);
|
||||
border: 1px solid var(--glass-border-strong);
|
||||
border-radius: 14px;
|
||||
padding: 14px;
|
||||
box-shadow: 0 24px 60px rgba(0,0,0,0.7);
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.tabs {
|
||||
display: flex; gap: 2px; border-bottom: 1px solid var(--glass-border);
|
||||
padding-bottom: 8px; margin-bottom: 10px; flex-wrap: wrap;
|
||||
}
|
||||
.tab {
|
||||
padding: 5px 10px; border-radius: 6px;
|
||||
font-size: 12px; color: var(--fg-muted);
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
}
|
||||
.tab.active { background: var(--glass-strong); color: var(--coral); }
|
||||
.tab .tab-badge {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 14px; height: 14px; border-radius: 9999px;
|
||||
background: var(--coral); color: #000;
|
||||
font-size: 9px; font-weight: 700;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
|
||||
/* segmented tri-state */
|
||||
.seg {
|
||||
display: flex; gap: 0;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 8px; overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.seg .opt {
|
||||
flex: 1; text-align: center;
|
||||
padding: 6px 10px;
|
||||
font-size: 12px; color: var(--fg-muted);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
border-right: 1px solid var(--glass-border);
|
||||
cursor: default;
|
||||
}
|
||||
.seg .opt:last-child { border-right: none; }
|
||||
.seg .opt.on {
|
||||
background: var(--coral); color: #000; font-weight: 700;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 8px 4px;
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
}
|
||||
.row:last-child { border-bottom: none; }
|
||||
.row .label {
|
||||
color: var(--fg-dim); font-size: 13px;
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.row .label .ico { color: var(--fg-muted); }
|
||||
.axis-title {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em;
|
||||
color: var(--coral); margin-top: 8px; margin-bottom: 6px;
|
||||
}
|
||||
.axis-title:first-child { margin-top: 0; }
|
||||
|
||||
/* checkbox */
|
||||
.check {
|
||||
width: 14px; height: 14px; border-radius: 3px;
|
||||
border: 1.5px solid var(--glass-border-strong);
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-size: 9px; color: var(--coral);
|
||||
}
|
||||
.check.on { background: rgba(255,122,138,0.18); border-color: var(--coral); }
|
||||
|
||||
/* Master/detail */
|
||||
.md {
|
||||
display: flex; gap: 12px;
|
||||
}
|
||||
.md .left {
|
||||
width: 160px;
|
||||
border-right: 1px solid var(--glass-border);
|
||||
padding-right: 8px;
|
||||
}
|
||||
.md .left .ax {
|
||||
padding: 8px 10px; border-radius: 6px;
|
||||
color: var(--fg-muted); font-size: 13px;
|
||||
cursor: default; display: flex; align-items: center; justify-content: space-between;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.md .left .ax.on { background: var(--glass-strong); color: var(--coral); }
|
||||
.md .left .ax .pill {
|
||||
font-size: 9px; padding: 1px 6px; border-radius: 9999px;
|
||||
background: var(--coral); color: #000; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-weight: 700;
|
||||
}
|
||||
.md .right { flex: 1; padding-left: 4px; }
|
||||
.md .right .opt {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 8px 10px; border-radius: 6px;
|
||||
color: var(--fg-dim); font-size: 13px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.md .right .opt.on { background: rgba(255,122,138,0.12); color: var(--coral); }
|
||||
.md .right .radio {
|
||||
width: 14px; height: 14px; border-radius: 9999px;
|
||||
border: 1.5px solid var(--glass-border-strong);
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.md .right .radio .dot {
|
||||
width: 6px; height: 6px; border-radius: 9999px; background: var(--coral);
|
||||
display: none;
|
||||
}
|
||||
.md .right .opt.on .radio { border-color: var(--coral); }
|
||||
.md .right .opt.on .radio .dot { display: block; }
|
||||
|
||||
.pros-cons {
|
||||
display: flex; gap: 12px; margin-top: 14px; font-size: 12px;
|
||||
}
|
||||
.pros, .cons {
|
||||
flex: 1; padding: 8px 10px; border-radius: 8px;
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
.pros { border-color: rgba(121,230,178,0.2); }
|
||||
.cons { border-color: rgba(255,122,138,0.2); }
|
||||
.pros strong { color: var(--mint); font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; }
|
||||
.cons strong { color: var(--coral); font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; }
|
||||
.pros ul, .cons ul { margin: 4px 0 0; padding-left: 16px; color: var(--fg-dim); }
|
||||
.pros li, .cons li { margin: 2px 0; }
|
||||
code { background: var(--glass); padding: 1px 5px; border-radius: 4px; font-size: 12px; color: var(--coral); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<h1>Status Filter — Layout Options</h1>
|
||||
<p class="lede">
|
||||
Five takes on solving the redundancy of paired Watched/Unwatched + Rated/No Rating filters,
|
||||
plus housing for No Collection / No Tags. Goal: zero contradictory states, easy to extend with new axes
|
||||
(Year, Resolution, etc.), keeps the filter bar slim.
|
||||
</p>
|
||||
|
||||
<!-- =================================================== -->
|
||||
<h2>Option A — Tabbed status popover (mirrors "Filter by")</h2>
|
||||
<p class="desc">
|
||||
Same shape as the entity filter. Each axis is a tab; inside the tab is a tri-state radio (All / X / not X).
|
||||
Future axes (Year, Resolution, Has Notes) drop in as new tabs at zero design cost.
|
||||
</p>
|
||||
<div class="stage">
|
||||
<div class="stage-label">Bar: All · Filter by ▾ · Status ▾ · search · sort</div>
|
||||
<div class="bar">
|
||||
<span class="chip active-cyan">All</span>
|
||||
<span class="chip">⛓ Filter by <span class="badge cyan" style="visibility:hidden">0</span> ▾</span>
|
||||
<span class="chip active-coral" style="position: relative">
|
||||
⊘ Status <span class="badge coral">2</span> ▾
|
||||
<div class="popup" style="left: 0; top: calc(100% + 6px); width: 380px;">
|
||||
<div class="tabs">
|
||||
<span class="tab active">👁 Watched <span class="tab-badge">1</span></span>
|
||||
<span class="tab">⭐ Rated <span class="tab-badge">1</span></span>
|
||||
<span class="tab">📁 Collection</span>
|
||||
<span class="tab">🏷 Tags</span>
|
||||
</div>
|
||||
<div style="font-family: ui-monospace; font-size: 10px; color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px;">Watched</div>
|
||||
<div class="seg">
|
||||
<span class="opt">All</span>
|
||||
<span class="opt on">Watched</span>
|
||||
<span class="opt">Unwatched</span>
|
||||
</div>
|
||||
<div style="font-size: 11px; color: var(--fg-muted); font-family: ui-monospace;">tap to set · only one option per tab</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><strong>Pros</strong><ul>
|
||||
<li>Endlessly extensible — new axis = new tab</li>
|
||||
<li>Visually consistent with Filter by</li>
|
||||
<li>No contradictions possible</li>
|
||||
</ul></div>
|
||||
<div class="cons"><strong>Cons</strong><ul>
|
||||
<li>Two clicks to change a different axis</li>
|
||||
<li>Tabs hide other axes' state until clicked</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- =================================================== -->
|
||||
<h2>Option B — Single popover, all axes visible (sectioned tri-states)</h2>
|
||||
<p class="desc">
|
||||
Keep one popover, but show every axis at once with a tri-state segmented control per axis.
|
||||
No tabs — everything's a click away. Becomes taller as axes are added.
|
||||
</p>
|
||||
<div class="stage">
|
||||
<div class="stage-label">All status axes visible at once</div>
|
||||
<div class="bar">
|
||||
<span class="chip active-cyan">All</span>
|
||||
<span class="chip">⛓ Filter by <span class="badge cyan" style="visibility:hidden">0</span> ▾</span>
|
||||
<span class="chip active-coral" style="position: relative">
|
||||
⊘ Status <span class="badge coral">3</span> ▾
|
||||
<div class="popup" style="left: 0; top: calc(100% + 6px); width: 360px;">
|
||||
<div class="axis-title">Watched</div>
|
||||
<div class="seg">
|
||||
<span class="opt on">All</span>
|
||||
<span class="opt">Watched</span>
|
||||
<span class="opt">Unwatched</span>
|
||||
</div>
|
||||
<div class="axis-title">Rated</div>
|
||||
<div class="seg">
|
||||
<span class="opt">All</span>
|
||||
<span class="opt on">Rated</span>
|
||||
<span class="opt">No Rating</span>
|
||||
</div>
|
||||
<div class="axis-title">Collection</div>
|
||||
<div class="seg">
|
||||
<span class="opt">All</span>
|
||||
<span class="opt">Has</span>
|
||||
<span class="opt on">No Collection</span>
|
||||
</div>
|
||||
<div class="axis-title">Tags</div>
|
||||
<div class="seg">
|
||||
<span class="opt">All</span>
|
||||
<span class="opt">Has</span>
|
||||
<span class="opt on">No Tags</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><strong>Pros</strong><ul>
|
||||
<li>All state visible — no hidden tabs</li>
|
||||
<li>Tri-state radios prevent contradictions</li>
|
||||
<li>One click to toggle any axis</li>
|
||||
</ul></div>
|
||||
<div class="cons"><strong>Cons</strong><ul>
|
||||
<li>Grows tall when more axes are added (Year, Resolution, etc.)</li>
|
||||
<li>Less directly extensible than tabs</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- =================================================== -->
|
||||
<h2>Option C — One chip per axis in the bar</h2>
|
||||
<p class="desc">
|
||||
Each status dimension is its own chip in the filter bar with its own dropdown. Clicking shows just that axis's options.
|
||||
Most decluttered axis-by-axis, but the bar grows wide as axes are added.
|
||||
</p>
|
||||
<div class="stage">
|
||||
<div class="stage-label">Bar: All · Filter by ▾ · Watched ▾ · Rated ▾ · Collection ▾ · Tags ▾ · search · sort</div>
|
||||
<div class="bar">
|
||||
<span class="chip active-cyan">All</span>
|
||||
<span class="chip">⛓ Filter by ▾</span>
|
||||
<span class="chip active-coral" style="position: relative">
|
||||
👁 Watched <span style="font-family: ui-monospace; font-size: 11px; opacity: 0.8;">: Watched</span> ▾
|
||||
<div class="popup" style="left: 0; top: calc(100% + 6px); width: 200px;">
|
||||
<div class="row"><span class="label">All</span><span class="check"></span></div>
|
||||
<div class="row"><span class="label">Watched</span><span class="check on">✓</span></div>
|
||||
<div class="row"><span class="label">Unwatched</span><span class="check"></span></div>
|
||||
</div>
|
||||
</span>
|
||||
<span class="chip">⭐ Rated ▾</span>
|
||||
<span class="chip">📁 Collection ▾</span>
|
||||
<span class="chip">🏷 Tags ▾</span>
|
||||
</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><strong>Pros</strong><ul>
|
||||
<li>Each axis label visible without opening</li>
|
||||
<li>Active selection shows on the chip itself ("Watched: Watched")</li>
|
||||
<li>One click to a specific axis</li>
|
||||
</ul></div>
|
||||
<div class="cons"><strong>Cons</strong><ul>
|
||||
<li>Bar grows wide — each new axis adds a chip</li>
|
||||
<li>Less consolidation; reverts to the "many chips" problem</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- =================================================== -->
|
||||
<h2>Option D — Master/detail popover (axis list left, options right)</h2>
|
||||
<p class="desc">
|
||||
Opens to a two-column layout: axis list on the left (with active value), options for the selected axis on the right.
|
||||
Only one click to switch axes; current selection visible at all times in the left column. Scales to many axes.
|
||||
</p>
|
||||
<div class="stage">
|
||||
<div class="stage-label">Two-pane popover with permanent axis sidebar</div>
|
||||
<div class="bar">
|
||||
<span class="chip active-cyan">All</span>
|
||||
<span class="chip">⛓ Filter by ▾</span>
|
||||
<span class="chip active-coral" style="position: relative">
|
||||
⊘ Status <span class="badge coral">2</span> ▾
|
||||
<div class="popup" style="left: 0; top: calc(100% + 6px); width: 460px;">
|
||||
<div class="md">
|
||||
<div class="left">
|
||||
<div class="ax on">👁 Watched <span class="pill">Watched</span></div>
|
||||
<div class="ax">⭐ Rated <span class="pill">Rated</span></div>
|
||||
<div class="ax">📁 Collection</div>
|
||||
<div class="ax">🏷 Tags</div>
|
||||
<!-- Future axes drop in here:
|
||||
<div class="ax">📅 Year</div>
|
||||
<div class="ax">📐 Resolution</div>
|
||||
-->
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="opt">
|
||||
<span class="radio"><span class="dot"></span></span>
|
||||
All
|
||||
</div>
|
||||
<div class="opt on">
|
||||
<span class="radio"><span class="dot"></span></span>
|
||||
Watched
|
||||
</div>
|
||||
<div class="opt">
|
||||
<span class="radio"><span class="dot"></span></span>
|
||||
Unwatched
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><strong>Pros</strong><ul>
|
||||
<li>Active value visible per axis without opening anything</li>
|
||||
<li>Switch axes in one click — no tab clicks lost</li>
|
||||
<li>Cleanest scaling for many axes (just longer left column)</li>
|
||||
<li>Scans like a settings panel — predictable</li>
|
||||
</ul></div>
|
||||
<div class="cons"><strong>Cons</strong><ul>
|
||||
<li>Wider popover (~460px+)</li>
|
||||
<li>More implementation work than a flat tabbed layout</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- =================================================== -->
|
||||
<h2>Option E — Inline accordion (collapsed sections, click to expand)</h2>
|
||||
<p class="desc">
|
||||
All axes listed; current selection shown inline; click an axis to expand its options. Closed axes show only their value.
|
||||
Compact when most axes are on default, expands as needed.
|
||||
</p>
|
||||
<div class="stage">
|
||||
<div class="stage-label">Sections collapse when not active; current value always visible</div>
|
||||
<div class="bar">
|
||||
<span class="chip active-cyan">All</span>
|
||||
<span class="chip">⛓ Filter by ▾</span>
|
||||
<span class="chip active-coral" style="position: relative">
|
||||
⊘ Status <span class="badge coral">2</span> ▾
|
||||
<div class="popup" style="left: 0; top: calc(100% + 6px); width: 320px;">
|
||||
<div class="row">
|
||||
<span class="label">👁 Watched <span style="color: var(--coral); font-family: ui-monospace; font-size: 11px; margin-left: 4px;">: Watched</span></span>
|
||||
<span style="color: var(--fg-muted); font-size: 11px;">▾</span>
|
||||
</div>
|
||||
<div style="padding: 4px 8px 8px;">
|
||||
<div class="seg">
|
||||
<span class="opt">All</span>
|
||||
<span class="opt on">Watched</span>
|
||||
<span class="opt">Unwatched</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">⭐ Rated <span style="color: var(--coral); font-family: ui-monospace; font-size: 11px; margin-left: 4px;">: Rated</span></span>
|
||||
<span style="color: var(--fg-muted); font-size: 11px;">▸</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">📁 Collection <span style="color: var(--fg-muted); font-family: ui-monospace; font-size: 11px; margin-left: 4px;">: All</span></span>
|
||||
<span style="color: var(--fg-muted); font-size: 11px;">▸</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">🏷 Tags <span style="color: var(--fg-muted); font-family: ui-monospace; font-size: 11px; margin-left: 4px;">: All</span></span>
|
||||
<span style="color: var(--fg-muted); font-size: 11px;">▸</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><strong>Pros</strong><ul>
|
||||
<li>Compact when nothing's set; details on demand</li>
|
||||
<li>Active values visible inline without opening anything</li>
|
||||
<li>Stays narrow — easy to fit on tablets</li>
|
||||
</ul></div>
|
||||
<div class="cons"><strong>Cons</strong><ul>
|
||||
<li>Two clicks to change an axis (open + select)</li>
|
||||
<li>Animations / disclosure indicators add UI complexity</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p style="margin-top: 24px; font-size: 11px; color: var(--fg-muted); font-family: ui-monospace;">
|
||||
All five solve contradictions via tri-state radios and house Collection/Tags as proper axes.
|
||||
Pick based on your taste: A scales most cleanly; D shows the most info at once; B is simplest to implement;
|
||||
C wears state on the bar; E is the most space-efficient.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,724 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex · Tag Bulk Import — 10 Layout Suggestions</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0a0710;
|
||||
--bg-1: #14101e;
|
||||
--bg-2: #1c1729;
|
||||
--fg: #e8e6f0;
|
||||
--fg-dim: #a8a4b8;
|
||||
--fg-muted: #6e6a80;
|
||||
--cyan: #22d3ee;
|
||||
--violet: #a78bfa;
|
||||
--mint: #34d399;
|
||||
--coral: #fb7185;
|
||||
--amber: #fbbf24;
|
||||
--pink: #f472b6;
|
||||
--glass: rgba(255,255,255,0.04);
|
||||
--glass-strong: rgba(255,255,255,0.08);
|
||||
--border: rgba(255,255,255,0.08);
|
||||
--border-strong: rgba(255,255,255,0.18);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg-0);
|
||||
color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.page { max-width: 1600px; margin: 0 auto; padding: 24px; }
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
padding: 16px 20px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(34,211,238,0.08), rgba(167,139,250,0.05));
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.page-header h1 { margin: 0 0 8px 0; font-size: 24px; font-weight: 600; }
|
||||
.page-header p { margin: 0; color: var(--fg-dim); font-size: 14px; }
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.variant {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
background: var(--bg-1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.variant-head {
|
||||
padding: 14px 18px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--glass);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.variant-num {
|
||||
width: 28px; height: 28px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--cyan), var(--violet));
|
||||
display: grid; place-items: center;
|
||||
font-weight: 700; font-size: 13px; color: #0a0710;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.variant-title { font-size: 16px; font-weight: 600; }
|
||||
.variant-desc { color: var(--fg-dim); font-size: 12px; }
|
||||
.variant-body {
|
||||
padding: 24px;
|
||||
background: var(--bg-0);
|
||||
min-height: 360px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Common element styles */
|
||||
.glass {
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 7px 12px;
|
||||
font-size: 13px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-strong);
|
||||
background: var(--glass);
|
||||
color: var(--fg);
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn:hover { background: var(--glass-strong); }
|
||||
.btn.primary {
|
||||
background: linear-gradient(135deg, var(--cyan), #1ea3ba);
|
||||
color: #0a0710;
|
||||
border-color: transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
.btn.danger {
|
||||
border-color: rgba(251,113,133,0.4);
|
||||
color: var(--coral);
|
||||
}
|
||||
textarea, input[type="text"] {
|
||||
background: var(--bg-2);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
color: var(--fg);
|
||||
padding: 10px 12px;
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
}
|
||||
textarea:focus, input:focus {
|
||||
border-color: var(--cyan);
|
||||
}
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
font-family: ui-monospace, monospace;
|
||||
border: 1px solid var(--border-strong);
|
||||
}
|
||||
.pill.cyan { color: var(--cyan); border-color: rgba(34,211,238,0.4); background: rgba(34,211,238,0.08); }
|
||||
.pill.mint { color: var(--mint); border-color: rgba(52,211,153,0.4); background: rgba(52,211,153,0.08); }
|
||||
.pill.amber { color: var(--amber); border-color: rgba(251,191,36,0.4); background: rgba(251,191,36,0.08); }
|
||||
.pill.coral { color: var(--coral); border-color: rgba(251,113,133,0.4); background: rgba(251,113,133,0.08); }
|
||||
.pill.violet{ color: var(--violet);border-color: rgba(167,139,250,0.4); background: rgba(167,139,250,0.08); }
|
||||
.pill.muted { color: var(--fg-muted); border-color: var(--border); }
|
||||
|
||||
table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
||||
th, td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--border); }
|
||||
th { color: var(--fg-muted); font-weight: 500; text-transform: uppercase; font-size: 10px; letter-spacing: 0.05em; }
|
||||
|
||||
.toolbar {
|
||||
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
|
||||
}
|
||||
.toolbar .label { font-size: 11px; color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.04em; }
|
||||
|
||||
.swatch { width: 14px; height: 14px; border-radius: 4px; display: inline-block; vertical-align: middle; }
|
||||
|
||||
/* ── Variant 1: Modal with side-by-side paste + preview ───────── */
|
||||
.modal-bg {
|
||||
width: 100%;
|
||||
background: rgba(0,0,0,0.6);
|
||||
backdrop-filter: blur(8px);
|
||||
padding: 24px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.modal {
|
||||
width: 100%; max-width: 980px;
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 24px 80px rgba(0,0,0,0.6);
|
||||
}
|
||||
.modal-head { padding: 16px 20px; border-bottom: 1px solid var(--border); display:flex; justify-content:space-between; align-items:center; }
|
||||
.modal-title { font-size: 16px; font-weight: 600; }
|
||||
.modal-body { padding: 20px; }
|
||||
.modal-foot { padding: 14px 20px; border-top: 1px solid var(--border); display:flex; justify-content:space-between; align-items:center; gap: 12px; background: var(--glass); }
|
||||
|
||||
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
.stack { display: flex; flex-direction: column; gap: 12px; }
|
||||
.col-h { font-size: 11px; color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 6px; }
|
||||
|
||||
/* ── Variant 2: Stepper ─────────── */
|
||||
.stepper { display:flex; gap: 8px; padding: 14px 20px; border-bottom: 1px solid var(--border); }
|
||||
.step { display:flex; align-items:center; gap:8px; padding: 6px 12px; border-radius: 999px; font-size: 12px; }
|
||||
.step.active { background: var(--glass-strong); color: var(--cyan); border: 1px solid rgba(34,211,238,0.4); }
|
||||
.step .num { width: 20px; height: 20px; border-radius: 50%; background: var(--glass); display:grid; place-items:center; font-size: 10px; }
|
||||
.step.active .num { background: var(--cyan); color: #0a0710; }
|
||||
|
||||
/* ── Variant 3: Dropzone ────────── */
|
||||
.dropzone {
|
||||
border: 2px dashed var(--border-strong);
|
||||
border-radius: 16px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
background: var(--glass);
|
||||
width: 100%; max-width: 600px;
|
||||
}
|
||||
.dropzone .icon { font-size: 40px; margin-bottom: 12px; opacity: 0.5; }
|
||||
.dropzone .or { color: var(--fg-muted); font-size: 12px; margin: 14px 0; }
|
||||
|
||||
/* ── Variant 4: Inline panel ────── */
|
||||
.page-mini { width: 100%; max-width: 900px; }
|
||||
.page-mini-head { display:flex; justify-content:space-between; align-items:center; margin-bottom: 16px; }
|
||||
.inline-importer { background: var(--bg-1); border: 1px solid var(--border); border-radius: 12px; }
|
||||
.inline-importer-head { padding: 12px 16px; border-bottom: 1px solid var(--border); background: var(--glass); display:flex; justify-content:space-between; align-items:center; }
|
||||
.tag-row { display:inline-flex; align-items:center; gap:6px; padding: 4px 10px; border-radius:999px; font-size:12px; background: var(--glass); border: 1px solid var(--border); }
|
||||
|
||||
/* ── Variant 5: Compact pasteboard ── */
|
||||
.compact { width: 100%; max-width: 700px; padding: 20px; background: var(--bg-1); border: 1px solid var(--border); border-radius: 12px; }
|
||||
.badges { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 10px; padding: 10px; background: var(--bg-2); border-radius: 8px; }
|
||||
|
||||
/* ── Variant 6: Spreadsheet ──────── */
|
||||
.sheet { width: 100%; max-width: 900px; }
|
||||
.sheet table { background: var(--bg-1); border-radius: 8px; overflow: hidden; }
|
||||
.sheet td input { background: transparent; border: none; padding: 4px 6px; font-size: 12px; width: 100%; }
|
||||
.sheet td input:focus { background: var(--bg-2); border-radius: 4px; }
|
||||
.sheet tr:hover td { background: rgba(255,255,255,0.02); }
|
||||
.sheet .row-num { color: var(--fg-muted); font-family: ui-monospace, monospace; font-size: 10px; }
|
||||
|
||||
/* ── Variant 7: Chips ────────────── */
|
||||
.chips-zone { width: 100%; max-width: 760px; padding: 16px; background: var(--bg-1); border: 1px solid var(--border); border-radius: 12px; }
|
||||
.chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px; background: var(--bg-2); border-radius: 8px; min-height: 120px; align-content: flex-start; }
|
||||
.chip {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 4px 10px; border-radius: 999px;
|
||||
background: var(--glass-strong); border: 1px solid var(--border-strong);
|
||||
font-size: 12px;
|
||||
}
|
||||
.chip .x { opacity: 0.5; cursor: pointer; }
|
||||
.chip.dup { border-color: rgba(251,191,36,0.5); background: rgba(251,191,36,0.08); }
|
||||
.chip.new { border-color: rgba(52,211,153,0.5); background: rgba(52,211,153,0.08); }
|
||||
|
||||
/* ── Variant 8: Side drawer ─────── */
|
||||
.drawer-frame {
|
||||
width: 100%; max-width: 1100px; height: 480px;
|
||||
background: var(--bg-2); border-radius: 12px; overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
display: flex;
|
||||
}
|
||||
.drawer-bg { flex: 1; padding: 20px; opacity: 0.4; pointer-events: none; }
|
||||
.drawer { width: 460px; background: var(--bg-1); border-left: 1px solid var(--border-strong); padding: 18px; box-shadow: -10px 0 40px rgba(0,0,0,0.6); display: flex; flex-direction: column; gap: 12px; }
|
||||
|
||||
/* ── Variant 9: Full-page importer ── */
|
||||
.fullpage { width: 100%; max-width: 1100px; }
|
||||
.fullpage-head { padding: 16px 0; border-bottom: 1px solid var(--border); margin-bottom: 16px; display:flex; justify-content:space-between; align-items:center; }
|
||||
.three-col { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; }
|
||||
|
||||
/* ── Variant 10: Tabs Quick / Advanced ── */
|
||||
.tabs-modal { width: 100%; max-width: 720px; background: var(--bg-1); border: 1px solid var(--border-strong); border-radius: 16px; overflow: hidden; }
|
||||
.tabs { display:flex; gap: 0; padding: 0 20px; border-bottom: 1px solid var(--border); }
|
||||
.tab { padding: 12px 16px; font-size: 13px; color: var(--fg-dim); border-bottom: 2px solid transparent; cursor: pointer; }
|
||||
.tab.active { color: var(--cyan); border-bottom-color: var(--cyan); }
|
||||
|
||||
/* preview row */
|
||||
.prev-status-new { color: var(--mint); }
|
||||
.prev-status-dup { color: var(--amber); }
|
||||
.prev-status-bad { color: var(--coral); }
|
||||
.prev-status-update{ color: var(--violet); }
|
||||
|
||||
.summary-line {
|
||||
display: flex; gap: 14px; align-items: center;
|
||||
font-size: 13px; color: var(--fg-dim);
|
||||
}
|
||||
.summary-line strong { color: var(--fg); }
|
||||
|
||||
.opt-row { display:flex; align-items:center; gap: 14px; flex-wrap: wrap; }
|
||||
.checkbox { display:inline-flex; align-items:center; gap: 6px; font-size: 12px; color: var(--fg-dim); }
|
||||
.checkbox input { accent-color: var(--cyan); }
|
||||
|
||||
code.kbd {
|
||||
display: inline-block; padding: 1px 6px; background: var(--bg-2);
|
||||
border: 1px solid var(--border); border-radius: 4px;
|
||||
font-family: ui-monospace, monospace; font-size: 11px;
|
||||
color: var(--fg-dim);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="page-header">
|
||||
<h1>Tag Bulk Import — 10 layout suggestions</h1>
|
||||
<p>Goal: paste/upload a list of tags (one-per-line, CSV, or JSON), preview parsed results with new/duplicate/error badges, then commit. Each variant explores a different UX shape.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
|
||||
<!-- ───────────── 1 ───────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">1</div>
|
||||
<div>
|
||||
<div class="variant-title">Modal · Split paste + live preview</div>
|
||||
<div class="variant-desc">Classic split: paste left, parsed table right. Auto-detects JSON / CSV / line-list. Best general purpose.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="modal-bg">
|
||||
<div class="modal" style="margin: 0 auto;">
|
||||
<div class="modal-head">
|
||||
<div class="modal-title">Import tags</div>
|
||||
<span class="pill cyan">auto-detected: CSV</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="split">
|
||||
<div>
|
||||
<div class="col-h">Paste — line, CSV, or JSON</div>
|
||||
<textarea rows="13">name,category,color
|
||||
Bondage,Fetish,#a78bfa
|
||||
Cosplay,Genre,#22d3ee
|
||||
Bukkake,Genre,
|
||||
Office Lady,,
|
||||
Ahegao,Expression,#f472b6
|
||||
School Uniform,Cosplay,#fbbf24</textarea>
|
||||
<div class="opt-row" style="margin-top: 10px;">
|
||||
<label class="checkbox"><input type="checkbox" checked /> Create missing categories</label>
|
||||
<label class="checkbox"><input type="checkbox" /> Update existing (color & category)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="col-h">Preview · 6 parsed</div>
|
||||
<div class="glass" style="overflow: hidden;">
|
||||
<table>
|
||||
<thead><tr><th>Name</th><th>Category</th><th>Color</th><th>Status</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Bondage</td><td>Fetish</td><td><span class="swatch" style="background:#a78bfa"></span></td><td class="prev-status-new">+ new</td></tr>
|
||||
<tr><td>Cosplay</td><td>Genre</td><td><span class="swatch" style="background:#22d3ee"></span></td><td class="prev-status-dup">↺ exists</td></tr>
|
||||
<tr><td>Bukkake</td><td>Genre</td><td>—</td><td class="prev-status-new">+ new</td></tr>
|
||||
<tr><td>Office Lady</td><td><span style="color:var(--fg-muted)">—</span></td><td>—</td><td class="prev-status-new">+ new</td></tr>
|
||||
<tr><td>Ahegao</td><td>Expression <span class="pill mint" style="margin-left:4px">+ create</span></td><td><span class="swatch" style="background:#f472b6"></span></td><td class="prev-status-new">+ new</td></tr>
|
||||
<tr><td>School Uniform</td><td>Cosplay</td><td><span class="swatch" style="background:#fbbf24"></span></td><td class="prev-status-dup">↺ exists</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="summary-line" style="margin-top: 10px;">
|
||||
<span><strong>4</strong> new</span>
|
||||
<span><strong>2</strong> duplicates</span>
|
||||
<span><strong>1</strong> category to create</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-foot">
|
||||
<span style="color: var(--fg-muted); font-size: 12px;">Tip: drag a <code class="kbd">.csv</code> / <code class="kbd">.json</code> onto the textarea.</span>
|
||||
<div style="display:flex; gap: 8px;">
|
||||
<button class="btn">Cancel</button>
|
||||
<button class="btn primary">Import 4 new tags →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ───────────── 2 ───────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">2</div>
|
||||
<div>
|
||||
<div class="variant-title">Modal · Stepper wizard (Paste → Map → Review)</div>
|
||||
<div class="variant-desc">3-step flow with progress chips. Verbose for new users; lets you map columns explicitly on CSV.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="modal" style="max-width: 820px;">
|
||||
<div class="stepper">
|
||||
<div class="step"><span class="num">1</span> Paste data</div>
|
||||
<div class="step active"><span class="num">2</span> Map columns</div>
|
||||
<div class="step"><span class="num">3</span> Review & import</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div style="font-size:13px; color: var(--fg-dim); margin-bottom: 12px;">Detected <strong style="color:var(--fg)">CSV</strong> with header row. Map columns to tag fields.</div>
|
||||
<table class="glass" style="overflow:hidden;">
|
||||
<thead><tr><th>Column</th><th>Sample</th><th>Maps to</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><code class="kbd">name</code></td><td>Bondage, Cosplay…</td><td><span class="pill cyan">Tag name (required)</span></td></tr>
|
||||
<tr><td><code class="kbd">category</code></td><td>Fetish, Genre…</td><td><span class="pill violet">Category (lookup or create)</span></td></tr>
|
||||
<tr><td><code class="kbd">color</code></td><td>#a78bfa…</td><td><span class="pill mint">Color hex</span></td></tr>
|
||||
<tr><td><code class="kbd">notes</code></td><td>internal use…</td><td><span class="pill muted">Skip (no field)</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-foot">
|
||||
<button class="btn">← Back</button>
|
||||
<button class="btn primary">Next: Review →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ───────────── 3 ───────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">3</div>
|
||||
<div>
|
||||
<div class="variant-title">Modal · Drag-drop hero</div>
|
||||
<div class="variant-desc">File-first. Big dropzone in the center; clicking opens picker. Paste-textarea hidden behind a toggle.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="modal" style="max-width: 700px;">
|
||||
<div class="modal-head">
|
||||
<div class="modal-title">Import tags</div>
|
||||
<button class="btn" style="font-size: 11px; padding: 4px 10px;">⌨ Paste instead</button>
|
||||
</div>
|
||||
<div class="modal-body" style="display:flex; justify-content:center;">
|
||||
<div class="dropzone">
|
||||
<div class="icon">📥</div>
|
||||
<div style="font-size: 16px; font-weight: 500; margin-bottom: 4px;">Drop .csv, .json, or .txt</div>
|
||||
<div style="color: var(--fg-dim); font-size: 13px;">One tag per line, or CSV with name/category/color columns</div>
|
||||
<div class="or">— or —</div>
|
||||
<button class="btn">Choose file…</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-foot">
|
||||
<span style="color: var(--fg-muted); font-size: 12px;">Files are parsed locally before any DB write.</span>
|
||||
<button class="btn">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ───────────── 4 ───────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">4</div>
|
||||
<div>
|
||||
<div class="variant-title">Inline panel on /tag (no modal)</div>
|
||||
<div class="variant-desc">Expandable section drops between the toolbar and the tag list. Less context-switch; preview tags appear inline next to the existing ones.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body" style="align-items: stretch;">
|
||||
<div class="page-mini">
|
||||
<div class="page-mini-head">
|
||||
<div style="font-size: 18px; font-weight: 600;">Tags</div>
|
||||
<div style="display:flex; gap:8px;">
|
||||
<button class="btn">A–Z ↓</button>
|
||||
<button class="btn primary">+ Import</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline-importer">
|
||||
<div class="inline-importer-head">
|
||||
<div style="font-size: 13px;"><strong>Bulk import</strong> — paste below, preview appears live</div>
|
||||
<button class="btn" style="font-size:11px; padding: 4px 8px;">✕ Close</button>
|
||||
</div>
|
||||
<div style="padding: 14px 16px; display: grid; grid-template-columns: 1fr 1fr; gap: 14px;">
|
||||
<textarea rows="6">Bondage
|
||||
Cosplay
|
||||
Bukkake
|
||||
Ahegao</textarea>
|
||||
<div>
|
||||
<div class="col-h">Will create 3 new</div>
|
||||
<div style="display:flex; flex-wrap:wrap; gap:6px;">
|
||||
<span class="tag-row"><span class="swatch" style="background:#a78bfa"></span>Bondage <span class="pill mint">new</span></span>
|
||||
<span class="tag-row">Cosplay <span class="pill amber">exists</span></span>
|
||||
<span class="tag-row">Bukkake <span class="pill mint">new</span></span>
|
||||
<span class="tag-row">Ahegao <span class="pill mint">new</span></span>
|
||||
</div>
|
||||
<div style="margin-top: 14px;">
|
||||
<button class="btn primary">Import 3 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 14px; display:flex; flex-wrap:wrap; gap:6px; opacity: 0.6;">
|
||||
<span class="tag-row">Existing 1</span><span class="tag-row">Existing 2</span><span class="tag-row">Existing 3</span><span class="tag-row">Existing …</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ───────────── 5 ───────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">5</div>
|
||||
<div>
|
||||
<div class="variant-title">Compact pasteboard with detection badges</div>
|
||||
<div class="variant-desc">Single column, minimal. Status pills under the textarea give a one-glance summary without a preview table.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="compact">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom: 10px;">
|
||||
<div style="font-size: 14px; font-weight: 600;">Paste tags</div>
|
||||
<span class="pill violet">JSON detected</span>
|
||||
</div>
|
||||
<textarea rows="9">[
|
||||
{"name": "Bondage", "category": "Fetish", "color": "#a78bfa"},
|
||||
{"name": "Cosplay", "category": "Genre", "color": "#22d3ee"},
|
||||
{"name": "Bukkake", "category": "Genre"},
|
||||
{"name": "Ahegao"}
|
||||
]</textarea>
|
||||
<div class="badges">
|
||||
<span class="pill cyan">4 parsed</span>
|
||||
<span class="pill mint">3 new</span>
|
||||
<span class="pill amber">1 duplicate</span>
|
||||
<span class="pill violet">1 category to create</span>
|
||||
<span class="pill muted">0 errors</span>
|
||||
<div style="margin-left: auto;">
|
||||
<button class="btn primary" style="padding: 4px 12px;">Import 3</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ───────────── 6 ───────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">6</div>
|
||||
<div>
|
||||
<div class="variant-title">Spreadsheet · Editable grid</div>
|
||||
<div class="variant-desc">Paste fills rows; each cell is editable. Add/remove rows. Best for fixing small mistakes before committing.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body" style="align-items: stretch;">
|
||||
<div class="sheet">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom: 12px;">
|
||||
<div style="font-size: 14px; font-weight: 600;">Edit before import (5 rows)</div>
|
||||
<div style="display:flex; gap: 6px;">
|
||||
<button class="btn" style="font-size:11px;">+ Row</button>
|
||||
<button class="btn" style="font-size:11px;">📋 Paste from clipboard</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass" style="overflow:hidden;">
|
||||
<table>
|
||||
<thead><tr><th style="width: 28px;">#</th><th>Name *</th><th>Category</th><th>Color</th><th>Status</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="row-num">1</td><td><input value="Bondage" /></td><td><input value="Fetish" /></td><td><input value="#a78bfa" /></td><td class="prev-status-new">+ new</td><td>✕</td></tr>
|
||||
<tr><td class="row-num">2</td><td><input value="Cosplay" /></td><td><input value="Genre" /></td><td><input value="#22d3ee" /></td><td class="prev-status-dup">↺ exists</td><td>✕</td></tr>
|
||||
<tr><td class="row-num">3</td><td><input value="Bukkake" /></td><td><input value="Genre" /></td><td><input value="" placeholder="—" /></td><td class="prev-status-new">+ new</td><td>✕</td></tr>
|
||||
<tr><td class="row-num">4</td><td><input value="Ahegao" /></td><td><input value="Expression" /></td><td><input value="#f472b6" /></td><td class="prev-status-new">+ new</td><td>✕</td></tr>
|
||||
<tr><td class="row-num">5</td><td><input value="" placeholder="(name required)" /></td><td><input /></td><td><input /></td><td class="prev-status-bad">⚠ blank</td><td>✕</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="display:flex; justify-content:space-between; margin-top: 12px;">
|
||||
<div class="summary-line"><span><strong>3</strong> new</span><span><strong>1</strong> duplicate</span><span class="prev-status-bad"><strong>1</strong> error</span></div>
|
||||
<button class="btn primary">Import 3 valid →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ───────────── 7 ───────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">7</div>
|
||||
<div>
|
||||
<div class="variant-title">Chip input · Type / paste, comma to split</div>
|
||||
<div class="variant-desc">Each tag becomes a chip you can edit or delete. Press Enter or comma to add. Color-coded: green=new, amber=dup.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="chips-zone">
|
||||
<div style="font-size: 14px; font-weight: 600; margin-bottom: 4px;">Tags to import</div>
|
||||
<div style="font-size: 12px; color: var(--fg-muted); margin-bottom: 10px;">Type a name, press <code class="kbd">Enter</code> or <code class="kbd">,</code> to add. Or paste many at once.</div>
|
||||
<div class="chips">
|
||||
<span class="chip new">Bondage <span class="x">✕</span></span>
|
||||
<span class="chip dup">Cosplay <span class="x">✕</span></span>
|
||||
<span class="chip new">Bukkake <span class="x">✕</span></span>
|
||||
<span class="chip new">Ahegao <span class="x">✕</span></span>
|
||||
<span class="chip new">Office Lady <span class="x">✕</span></span>
|
||||
<span class="chip new">Schoolgirl <span class="x">✕</span></span>
|
||||
<span class="chip dup">School Uniform <span class="x">✕</span></span>
|
||||
<input type="text" placeholder="Add more…" style="border: none; background: transparent; flex: 1; min-width: 120px; padding: 4px 8px;" />
|
||||
</div>
|
||||
<div class="opt-row" style="margin-top: 14px;">
|
||||
<select style="background: var(--bg-2); border: 1px solid var(--border); color: var(--fg); padding: 6px 10px; border-radius: 6px; font-size: 12px;">
|
||||
<option>Apply category to all: (none)</option>
|
||||
<option>Fetish</option>
|
||||
<option>Genre</option>
|
||||
</select>
|
||||
<span class="summary-line" style="margin-left:auto;"><span><strong>5</strong> new</span><span><strong>2</strong> dup</span></span>
|
||||
<button class="btn primary">Import 5</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ───────────── 8 ───────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">8</div>
|
||||
<div>
|
||||
<div class="variant-title">Side drawer · slides in from right</div>
|
||||
<div class="variant-desc">Lighter than a modal. Page stays visible behind. Good if you want to consult existing tags while pasting.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body" style="align-items:stretch;">
|
||||
<div class="drawer-frame">
|
||||
<div class="drawer-bg">
|
||||
<div style="font-size: 16px; font-weight: 600; margin-bottom: 14px;">Tags</div>
|
||||
<div style="display:flex; flex-wrap:wrap; gap: 6px;">
|
||||
<span class="tag-row">Cosplay</span><span class="tag-row">School Uniform</span><span class="tag-row">Tentacle</span><span class="tag-row">Beach</span><span class="tag-row">Office</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawer">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<div style="font-size: 14px; font-weight: 600;">Bulk import tags</div>
|
||||
<span style="color: var(--fg-muted); cursor:pointer;">✕</span>
|
||||
</div>
|
||||
<textarea rows="7">Bondage
|
||||
Cosplay
|
||||
Bukkake
|
||||
Ahegao
|
||||
Office Lady</textarea>
|
||||
<div class="badges" style="margin-top: 0;">
|
||||
<span class="pill mint">3 new</span><span class="pill amber">2 dup</span>
|
||||
</div>
|
||||
<label class="checkbox"><input type="checkbox" checked /> Create missing categories</label>
|
||||
<label class="checkbox"><input type="checkbox" /> Update existing</label>
|
||||
<button class="btn primary" style="margin-top: auto;">Import 3 tags</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ───────────── 9 ───────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">9</div>
|
||||
<div>
|
||||
<div class="variant-title">Full-page importer at /tag/import</div>
|
||||
<div class="variant-desc">Maximum room. Three columns: input, preview, options. Best when importing many hundreds.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body" style="align-items:stretch;">
|
||||
<div class="fullpage">
|
||||
<div class="fullpage-head">
|
||||
<div>
|
||||
<div style="font-size: 12px; color: var(--fg-muted);">Tags / Import</div>
|
||||
<div style="font-size: 20px; font-weight: 600;">Bulk import</div>
|
||||
</div>
|
||||
<div style="display:flex; gap:8px;"><button class="btn">Cancel</button><button class="btn primary">Import 4 →</button></div>
|
||||
</div>
|
||||
<div class="three-col">
|
||||
<div>
|
||||
<div class="col-h">Input</div>
|
||||
<textarea rows="14">name,category,color
|
||||
Bondage,Fetish,#a78bfa
|
||||
Cosplay,Genre,#22d3ee
|
||||
Bukkake,Genre,
|
||||
Ahegao,Expression,#f472b6</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div class="col-h">Preview</div>
|
||||
<div class="glass" style="overflow:hidden;">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr><td>Bondage</td><td><span class="pill violet">Fetish</span></td><td class="prev-status-new">new</td></tr>
|
||||
<tr><td>Cosplay</td><td><span class="pill violet">Genre</span></td><td class="prev-status-dup">dup</td></tr>
|
||||
<tr><td>Bukkake</td><td><span class="pill violet">Genre</span></td><td class="prev-status-new">new</td></tr>
|
||||
<tr><td>Ahegao</td><td><span class="pill mint">+ Expression</span></td><td class="prev-status-new">new</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="col-h">Options</div>
|
||||
<div class="glass" style="padding: 14px; display:flex; flex-direction: column; gap: 12px;">
|
||||
<label class="checkbox"><input type="checkbox" checked /> Create missing categories</label>
|
||||
<label class="checkbox"><input type="checkbox" /> Update color/category of existing tags</label>
|
||||
<label class="checkbox"><input type="checkbox" checked /> Trim whitespace</label>
|
||||
<label class="checkbox"><input type="checkbox" /> Lowercase names</label>
|
||||
<hr style="border: 0; border-top: 1px solid var(--border); width: 100%;" />
|
||||
<div style="font-size: 11px; color: var(--fg-muted);">On conflict</div>
|
||||
<select style="background: var(--bg-2); border: 1px solid var(--border); color: var(--fg); padding: 6px 10px; border-radius: 6px; font-size: 12px;">
|
||||
<option>Skip duplicates</option>
|
||||
<option>Replace duplicates</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ───────────── 10 ───────────── -->
|
||||
<div class="variant">
|
||||
<div class="variant-head">
|
||||
<div class="variant-num">10</div>
|
||||
<div>
|
||||
<div class="variant-title">Tabbed modal · Quick / Advanced</div>
|
||||
<div class="variant-desc">Two modes in one shell. Quick = just a textarea for one-per-line. Advanced = CSV/JSON with options. Lowest cognitive load for the common case.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="variant-body">
|
||||
<div class="tabs-modal">
|
||||
<div class="modal-head">
|
||||
<div class="modal-title">Import tags</div>
|
||||
<span style="color: var(--fg-muted); cursor:pointer;">✕</span>
|
||||
</div>
|
||||
<div class="tabs">
|
||||
<div class="tab active">Quick · one per line</div>
|
||||
<div class="tab">Advanced · CSV / JSON</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div style="font-size: 12px; color: var(--fg-muted); margin-bottom: 8px;">Just paste names, one per line. Categories & colors can be assigned afterwards on the tag page.</div>
|
||||
<textarea rows="9">Bondage
|
||||
Cosplay
|
||||
Bukkake
|
||||
Ahegao
|
||||
Office Lady
|
||||
School Uniform</textarea>
|
||||
<div class="opt-row" style="margin-top: 12px;">
|
||||
<span class="pill mint">5 new</span>
|
||||
<span class="pill amber">1 dup</span>
|
||||
<label class="checkbox" style="margin-left:auto;"><input type="checkbox" checked /> Skip duplicates</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-foot">
|
||||
<span style="color: var(--fg-muted); font-size: 12px;">Need categories or colors? Switch to <strong style="color:var(--fg)">Advanced</strong>.</span>
|
||||
<button class="btn primary">Import 5 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="margin: 40px 0 12px; padding: 16px 20px; border-radius: 12px; background: var(--glass); border: 1px solid var(--border); font-size: 13px; color: var(--fg-dim);">
|
||||
<strong style="color: var(--fg);">My pick</strong>: <em>#1 (split modal) for the default flow</em> — covers all three input formats with live feedback. Pair it with <em>#7 (chips)</em> as a "Quick add" alt for ad-hoc single-tag entry from the toolbar. <em>#6 (spreadsheet)</em> if editing-before-commit becomes a frequent need.
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,241 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pinkudex · Toolbar naming — 10 suggestions</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-0: #0a0710;
|
||||
--bg-1: #14101e;
|
||||
--bg-2: #1c1729;
|
||||
--fg: #e8e6f0;
|
||||
--fg-dim: #a8a4b8;
|
||||
--fg-muted: #6e6a80;
|
||||
--cyan: #22d3ee;
|
||||
--violet: #a78bfa;
|
||||
--mint: #34d399;
|
||||
--coral: #fb7185;
|
||||
--amber: #fbbf24;
|
||||
--glass: rgba(255,255,255,0.04);
|
||||
--glass-strong: rgba(255,255,255,0.08);
|
||||
--border: rgba(255,255,255,0.08);
|
||||
--border-strong: rgba(255,255,255,0.18);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0; background: var(--bg-0); color: var(--fg);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.page { max-width: 1300px; margin: 0 auto; padding: 24px; }
|
||||
.page-header {
|
||||
margin-bottom: 24px; padding: 16px 20px; border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(34,211,238,0.08), rgba(167,139,250,0.05));
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.page-header h1 { margin: 0 0 8px 0; font-size: 22px; font-weight: 600; }
|
||||
.page-header p { margin: 0; color: var(--fg-dim); font-size: 13px; }
|
||||
|
||||
.legend {
|
||||
margin: 18px 0 24px; padding: 14px 18px;
|
||||
background: var(--bg-1); border: 1px solid var(--border);
|
||||
border-radius: 12px; display: grid; grid-template-columns: 50px 1fr; gap: 8px 14px;
|
||||
font-size: 13px; color: var(--fg-dim);
|
||||
}
|
||||
.legend dt { font-family: ui-monospace, monospace; color: var(--cyan); font-weight: 600; }
|
||||
.legend dd { margin: 0; }
|
||||
.legend strong { color: var(--fg); }
|
||||
|
||||
.row {
|
||||
border: 1px solid var(--border); border-radius: 14px; background: var(--bg-1);
|
||||
padding: 14px 18px; margin-bottom: 14px;
|
||||
display: grid; grid-template-columns: 36px 1fr 1fr; gap: 14px; align-items: center;
|
||||
}
|
||||
.row .num {
|
||||
width: 28px; height: 28px; border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--cyan), var(--violet));
|
||||
display: grid; place-items: center;
|
||||
font-weight: 700; font-size: 12px; color: #0a0710;
|
||||
}
|
||||
.row .toolbar {
|
||||
display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
|
||||
}
|
||||
.row .desc { color: var(--fg-dim); font-size: 12px; line-height: 1.5; }
|
||||
.row .desc em { color: var(--fg); font-style: normal; font-weight: 500; }
|
||||
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 6px 12px; border-radius: 999px;
|
||||
background: var(--glass); border: 1px solid var(--border-strong);
|
||||
font-size: 13px; color: var(--fg-dim); white-space: nowrap;
|
||||
}
|
||||
.pill.all { background: rgba(34,211,238,0.15); border-color: rgba(34,211,238,0.4); color: var(--cyan); }
|
||||
.pill.disabled { opacity: 0.5; }
|
||||
.pill svg { width: 14px; height: 14px; opacity: 0.85; }
|
||||
.pill .cv { font-size: 10px; opacity: 0.5; margin-left: 2px; }
|
||||
|
||||
.pick {
|
||||
margin: 24px 0 12px; padding: 14px 18px;
|
||||
border-radius: 12px; background: rgba(52,211,153,0.07);
|
||||
border: 1px solid rgba(52,211,153,0.25);
|
||||
color: var(--mint); font-size: 13px;
|
||||
}
|
||||
.pick strong { color: var(--mint); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="page-header">
|
||||
<h1>Toolbar naming — 10 suggestions</h1>
|
||||
<p>Five buttons need distinct labels. Each row below tries a different naming strategy. Currently both #3 and #4 read as "Status", which is the bug.</p>
|
||||
</div>
|
||||
|
||||
<dl class="legend">
|
||||
<dt>Slot 1</dt>
|
||||
<dd><strong>All / clear-all</strong> — resets every filter back to default.</dd>
|
||||
<dt>Slot 2</dt>
|
||||
<dd><strong>Multi-entity filter</strong> — actresses, studios, series, genres, collections, tags, categories.</dd>
|
||||
<dt>Slot 3</dt>
|
||||
<dd><strong>Engagement axes</strong> — watched, rated, has-collection, has-tags (binary tri-state per axis).</dd>
|
||||
<dt>Slot 4</dt>
|
||||
<dd><strong>Mark filter</strong> — VIP / Favorite / Owned / Unmarked (multi-select).</dd>
|
||||
<dt>Slot 5</dt>
|
||||
<dd><strong>Mark action</strong> — applies a mark to the current selection (disabled when nothing selected).</dd>
|
||||
</dl>
|
||||
|
||||
<!-- 1 -->
|
||||
<div class="row">
|
||||
<div class="num">1</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">All</span>
|
||||
<span class="pill">📁 Browse <span class="cv">▾</span></span>
|
||||
<span class="pill">⏱ Activity <span class="cv">▾</span></span>
|
||||
<span class="pill">★ Stars <span class="cv">▾</span></span>
|
||||
<span class="pill disabled">✏ Apply <span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="desc"><em>Browse / Activity / Stars / Apply</em><br/>"Browse" = entity drill-in. "Activity" = how you've engaged. "Stars" = curated marks. "Apply" = the verb. Compact, distinctive.</div>
|
||||
</div>
|
||||
|
||||
<!-- 2 -->
|
||||
<div class="row">
|
||||
<div class="num">2</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">All</span>
|
||||
<span class="pill">🏷 Tags & People <span class="cv">▾</span></span>
|
||||
<span class="pill">⏱ State <span class="cv">▾</span></span>
|
||||
<span class="pill">◆ Marks <span class="cv">▾</span></span>
|
||||
<span class="pill disabled">✏ Mark As <span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="desc"><em>Tags & People / State / Marks / Mark As</em><br/>Most literal. Slot 2 says exactly what's inside; "Marks" (filter) and "Mark As" (verb) read as a noun/verb pair.</div>
|
||||
</div>
|
||||
|
||||
<!-- 3 -->
|
||||
<div class="row">
|
||||
<div class="num">3</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">Reset</span>
|
||||
<span class="pill">🔍 Filter <span class="cv">▾</span></span>
|
||||
<span class="pill">⏱ Watched & Rated <span class="cv">▾</span></span>
|
||||
<span class="pill">◆ Highlights <span class="cv">▾</span></span>
|
||||
<span class="pill disabled">✏ Mark As <span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="desc"><em>Filter / Watched & Rated / Highlights / Mark As</em><br/>Slot 3 spells out the two engagement axes that matter most. "Highlights" is curated/standout flavor for VIP/Fav/Owned.</div>
|
||||
</div>
|
||||
|
||||
<!-- 4 -->
|
||||
<div class="row">
|
||||
<div class="num">4</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">All</span>
|
||||
<span class="pill">🔗 Match <span class="cv">▾</span></span>
|
||||
<span class="pill">⏱ Progress <span class="cv">▾</span></span>
|
||||
<span class="pill">◆ Tier <span class="cv">▾</span></span>
|
||||
<span class="pill disabled">✏ Set <span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="desc"><em>Match / Progress / Tier / Set</em><br/>Single-word everything. "Tier" reframes VIP/Fav/Owned as quality bands. Tight but more abstract.</div>
|
||||
</div>
|
||||
|
||||
<!-- 5 -->
|
||||
<div class="row">
|
||||
<div class="num">5</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">All</span>
|
||||
<span class="pill">🏷 Filter By <span class="cv">▾</span></span>
|
||||
<span class="pill">⏱ Engagement <span class="cv">▾</span></span>
|
||||
<span class="pill">◆ Marks <span class="cv">▾</span></span>
|
||||
<span class="pill disabled">✏ Mark As <span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="desc"><em>Filter By / Engagement / Marks / Mark As</em><br/>Keep "Filter By" (current). Rename slot 3 → "Engagement" (covers watched/rated/tagged/in collection). "Marks"/"Mark As" pair handles the rest.</div>
|
||||
</div>
|
||||
|
||||
<!-- 6 -->
|
||||
<div class="row">
|
||||
<div class="num">6</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">All</span>
|
||||
<span class="pill">🔗 Connections <span class="cv">▾</span></span>
|
||||
<span class="pill">⏱ Behavior <span class="cv">▾</span></span>
|
||||
<span class="pill">◆ Highlights <span class="cv">▾</span></span>
|
||||
<span class="pill disabled">✏ Apply Marks <span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="desc"><em>Connections / Behavior / Highlights / Apply Marks</em><br/>Conceptual: who-what-when. "Connections" reads as relationships (people, studios). "Apply Marks" makes the verb explicit.</div>
|
||||
</div>
|
||||
|
||||
<!-- 7 -->
|
||||
<div class="row">
|
||||
<div class="num">7</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">Show All</span>
|
||||
<span class="pill">📂 Categories <span class="cv">▾</span></span>
|
||||
<span class="pill">⏱ Activity <span class="cv">▾</span></span>
|
||||
<span class="pill">◆ Marks <span class="cv">▾</span></span>
|
||||
<span class="pill disabled">✏ Mark Selected <span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="desc"><em>Categories / Activity / Marks / Mark Selected</em><br/>"Categories" is broad; "Mark Selected" tells you exactly what gets the action. Risk: "Categories" overlaps with the actress-categories nav item.</div>
|
||||
</div>
|
||||
|
||||
<!-- 8 -->
|
||||
<div class="row">
|
||||
<div class="num">8</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">All</span>
|
||||
<span class="pill">🔍 Filter <span class="cv">▾</span></span>
|
||||
<span class="pill">⏱ Watch State <span class="cv">▾</span></span>
|
||||
<span class="pill">◆ Marks <span class="cv">▾</span></span>
|
||||
<span class="pill disabled">✏ Mark As <span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="desc"><em>Filter / Watch State / Marks / Mark As</em><br/>Drops "By", picks up the watched-flavor of slot 3 since that's its most-used axis.</div>
|
||||
</div>
|
||||
|
||||
<!-- 9 -->
|
||||
<div class="row">
|
||||
<div class="num">9</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">All</span>
|
||||
<span class="pill">🔗 Match <span class="cv">▾</span></span>
|
||||
<span class="pill">⏱ State <span class="cv">▾</span></span>
|
||||
<span class="pill">◆ Picks <span class="cv">▾</span></span>
|
||||
<span class="pill disabled">✏ Mark As <span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="desc"><em>Match / State / Picks / Mark As</em><br/>"Picks" reframes VIP/Fav/Owned as user picks (curated). One-word everything; clean toolbar look.</div>
|
||||
</div>
|
||||
|
||||
<!-- 10 -->
|
||||
<div class="row">
|
||||
<div class="num">10</div>
|
||||
<div class="toolbar">
|
||||
<span class="pill all">All</span>
|
||||
<span class="pill">🏷 Filter By <span class="cv">▾</span></span>
|
||||
<span class="pill">⏱ How <span class="cv">▾</span></span>
|
||||
<span class="pill">◆ What <span class="cv">▾</span></span>
|
||||
<span class="pill disabled">✏ Mark As <span class="cv">▾</span></span>
|
||||
</div>
|
||||
<div class="desc"><em>Filter By / How / What / Mark As</em><br/>Pithy: "How" you've engaged (watched/rated) vs "What" it is (VIP/Owned). Cute but might be too clever to be discoverable on first look.</div>
|
||||
</div>
|
||||
|
||||
<div class="pick">
|
||||
<strong>My pick: #2 — All / Tags & People / State / Marks / Mark As.</strong> Most literal labels, no ambiguity, "Marks" + "Mark As" is a clean noun/verb pair. <strong>Runner-up: #5</strong> if you want to keep "Filter By" — swap slot 3 to "Engagement" for clarity.
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,507 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Pinkudex Video Page Redesign Options</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0d0b18;
|
||||
--panel: #171827;
|
||||
--panel-2: #1d2030;
|
||||
--line: #38384c;
|
||||
--muted: #8a869b;
|
||||
--text: #eceaf5;
|
||||
--cyan: #00d9f6;
|
||||
--green: #43d488;
|
||||
--violet: #a774f5;
|
||||
--red: #ff5d64;
|
||||
--amber: #ffbd5a;
|
||||
--radius: 14px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: radial-gradient(circle at 20% 0%, #21172f 0, transparent 34%), var(--bg);
|
||||
color: var(--text);
|
||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
}
|
||||
.topbar {
|
||||
height: 58px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28px;
|
||||
padding: 0 24px;
|
||||
background: rgba(9, 9, 18, 0.82);
|
||||
border-bottom: 1px solid #29263a;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 4;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
.brand { color: var(--cyan); font-weight: 700; font-size: 18px; }
|
||||
.nav { display: flex; gap: 26px; color: #aaa6b8; font-size: 14px; }
|
||||
.search { margin-left: auto; width: 300px; height: 36px; border: 1px solid #343348; border-radius: 10px; color: #777386; display: flex; align-items: center; padding: 0 14px; }
|
||||
main { padding: 28px 24px 60px; }
|
||||
h1 { margin: 0 0 8px; color: var(--cyan); font-size: 14px; letter-spacing: .12em; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||||
h2 { margin: 0 0 12px; font-size: 18px; }
|
||||
h3 { margin: 0 0 10px; font-size: 13px; color: #aaa6b8; text-transform: uppercase; letter-spacing: .12em; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||||
.intro {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
align-items: end;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.intro p { margin: 0; max-width: 760px; color: #aaa6b8; line-height: 1.45; }
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 22px;
|
||||
}
|
||||
.option {
|
||||
border: 1px solid var(--line);
|
||||
background: rgba(23, 24, 39, .78);
|
||||
border-radius: 18px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 16px 40px rgba(0,0,0,.22);
|
||||
}
|
||||
.option-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 14px;
|
||||
align-items: start;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.option-head p { margin: 4px 0 0; color: #9894a8; font-size: 13px; line-height: 1.35; }
|
||||
.pick { color: var(--cyan); border: 1px solid rgba(0,217,246,.35); padding: 4px 9px; border-radius: 999px; font: 11px ui-monospace, monospace; white-space: nowrap; }
|
||||
.mock {
|
||||
border: 1px solid #333246;
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
background: #11111e;
|
||||
min-height: 420px;
|
||||
}
|
||||
.bar {
|
||||
height: 38px;
|
||||
border-bottom: 1px solid #2f2e40;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 14px;
|
||||
color: #9d98aa;
|
||||
font-size: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn, .pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
height: 26px;
|
||||
padding: 0 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #3b3a50;
|
||||
color: #c9c5d4;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.pill { border-radius: 999px; height: 24px; font-size: 11px; }
|
||||
.cyan { color: var(--cyan); border-color: rgba(0,217,246,.45); background: rgba(0,217,246,.09); }
|
||||
.green { color: var(--green); border-color: rgba(67,212,136,.38); background: rgba(67,212,136,.08); }
|
||||
.violet { color: var(--violet); border-color: rgba(167,116,245,.38); background: rgba(167,116,245,.10); }
|
||||
.red { color: var(--red); border-color: rgba(255,93,100,.40); background: rgba(255,93,100,.08); }
|
||||
.amber { color: var(--amber); border-color: rgba(255,189,90,.35); background: rgba(255,189,90,.08); }
|
||||
.cover {
|
||||
background: #d6d6d8;
|
||||
color: #aaa;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 52px;
|
||||
letter-spacing: .05em;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.cover:after {
|
||||
content: "▶";
|
||||
position: absolute;
|
||||
font-size: 28px;
|
||||
color: white;
|
||||
width: 64px;
|
||||
height: 46px;
|
||||
border-radius: 8px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: rgba(20,20,26,.78);
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,.35);
|
||||
}
|
||||
.panel { border: 1px solid var(--line); border-radius: 12px; background: linear-gradient(135deg, rgba(30,31,45,.95), rgba(23,36,44,.88)); padding: 14px; }
|
||||
.meta-line { display: flex; flex-wrap: wrap; gap: 8px; color: #dcd8e8; font: 12px ui-monospace, monospace; }
|
||||
.label { color: #777386; text-transform: uppercase; font: 10px ui-monospace, monospace; letter-spacing: .1em; }
|
||||
.stars { color: var(--cyan); letter-spacing: 2px; }
|
||||
.tabs { display: flex; gap: 4px; border-bottom: 1px solid #333246; padding: 0 12px; }
|
||||
.tab { padding: 10px 10px; color: #8e899b; font-size: 12px; border-bottom: 2px solid transparent; }
|
||||
.tab.active { color: var(--cyan); border-color: var(--cyan); }
|
||||
.mini-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
||||
.field { min-height: 46px; border: 1px solid #333246; border-radius: 9px; padding: 8px; background: rgba(255,255,255,.025); }
|
||||
.field strong { display: block; margin-top: 4px; font-size: 13px; }
|
||||
.rail { width: 86px; border-right: 1px solid #302f42; background: #121320; display: flex; flex-direction: column; gap: 8px; padding: 10px; }
|
||||
.thumb { height: 54px; border-radius: 8px; background: #d4d4d6; color: #aaa; display: grid; place-items: center; font-size: 12px; position: relative; }
|
||||
.thumb.active { outline: 2px solid var(--cyan); }
|
||||
.split-a { display: grid; grid-template-columns: 52% 48%; min-height: 382px; }
|
||||
.split-a .cover { min-height: 342px; }
|
||||
.split-a .right { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
|
||||
.summary { display: grid; grid-template-columns: 1fr; gap: 10px; }
|
||||
.action-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: auto; }
|
||||
.split-b { display: grid; grid-template-columns: minmax(0, 1fr) 260px; min-height: 382px; }
|
||||
.split-b .stage { padding: 12px; }
|
||||
.split-b .cover { height: 260px; border-radius: 10px; }
|
||||
.side { border-left: 1px solid #302f42; padding: 14px; background: rgba(255,255,255,.025); }
|
||||
.hero { min-height: 382px; display: grid; grid-template-rows: 250px auto; }
|
||||
.hero .cover { font-size: 44px; }
|
||||
.hero .dock { padding: 14px; display: grid; gap: 10px; }
|
||||
.stack { display: grid; grid-template-columns: 92px 1fr; min-height: 382px; }
|
||||
.stack .content { padding: 12px; display: grid; grid-template-rows: auto 1fr; gap: 12px; }
|
||||
.stack .cover { min-height: 236px; border-radius: 10px; }
|
||||
.dense { min-height: 382px; display: grid; grid-template-columns: 46% 54%; }
|
||||
.dense .cover { min-height: 382px; }
|
||||
.dense .sheet { padding: 14px; display: grid; gap: 10px; align-content: start; }
|
||||
.netflix {
|
||||
min-height: 520px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(8,8,15,.96) 0%, rgba(8,8,15,.82) 32%, rgba(8,8,15,.28) 62%, rgba(8,8,15,.84) 100%),
|
||||
linear-gradient(0deg, #11111e 0%, rgba(17,17,30,0) 42%),
|
||||
#d6d6d8;
|
||||
}
|
||||
.netflix .poster-text {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: rgba(20,20,25,.20);
|
||||
font-size: 82px;
|
||||
letter-spacing: .05em;
|
||||
transform: translateX(12%);
|
||||
}
|
||||
.netflix .hero-copy {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: min(560px, 52%);
|
||||
padding: 54px 0 0 42px;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
.netflix .hero-title {
|
||||
font-size: 46px;
|
||||
line-height: .95;
|
||||
letter-spacing: .04em;
|
||||
color: var(--cyan);
|
||||
font-weight: 800;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
.netflix .hero-subtitle {
|
||||
font-size: 22px;
|
||||
color: #d8d4e4;
|
||||
font-style: italic;
|
||||
}
|
||||
.netflix .hero-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 6px; }
|
||||
.netflix .play {
|
||||
height: 38px;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
background: var(--text);
|
||||
color: #0b0b12;
|
||||
font-weight: 800;
|
||||
padding: 0 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.netflix .ghost {
|
||||
height: 38px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255,255,255,.25);
|
||||
background: rgba(255,255,255,.13);
|
||||
color: var(--text);
|
||||
padding: 0 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 650;
|
||||
}
|
||||
.netflix .hero-facts {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
max-width: 720px;
|
||||
color: #d8d4e4;
|
||||
font: 13px ui-monospace, monospace;
|
||||
}
|
||||
.netflix .bottom-shelf {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
left: 24px;
|
||||
right: 24px;
|
||||
bottom: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: 1.2fr .8fr .8fr;
|
||||
gap: 12px;
|
||||
}
|
||||
.netflix-card {
|
||||
border: 1px solid rgba(255,255,255,.14);
|
||||
background: rgba(18,19,32,.72);
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
backdrop-filter: blur(10px);
|
||||
min-height: 72px;
|
||||
}
|
||||
.note-list {
|
||||
margin: 26px 0 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.note {
|
||||
border: 1px solid #343348;
|
||||
border-radius: 12px;
|
||||
background: rgba(255,255,255,.035);
|
||||
padding: 12px;
|
||||
color: #aaa6b8;
|
||||
font-size: 13px;
|
||||
line-height: 1.42;
|
||||
}
|
||||
.note strong { color: var(--text); display: block; margin-bottom: 4px; }
|
||||
@media (max-width: 1100px) {
|
||||
.note-list { grid-template-columns: 1fr; }
|
||||
.search { display: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<div class="brand">◎ Pinkudex</div>
|
||||
<div class="nav"><span>Library</span><span>Actress</span><span>Database</span><span>Categories</span><span>Tags</span><span>Collection</span></div>
|
||||
<div class="search">⌕ Search Code, Title, Notes...</div>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div class="intro">
|
||||
<div>
|
||||
<h2>Video Detail Page Redesign Mockups</h2>
|
||||
<p>Five visual directions using the same content: cover, play, metadata, tags, collections, actresses, attached images, and edit/reveal/delete actions.</p>
|
||||
</div>
|
||||
<span class="pick">compare options</span>
|
||||
</div>
|
||||
|
||||
<section class="grid">
|
||||
<article class="option">
|
||||
<div class="option-head">
|
||||
<div><h2>Option A: Media First</h2><p>Keep the current two-column shape, but make metadata denser and consolidate the status pills into one header block.</p></div>
|
||||
<span class="pick">closest to current</span>
|
||||
</div>
|
||||
<div class="mock">
|
||||
<div class="bar">← Back to library <span style="margin-left:auto" class="btn">⤴ Reveal</span><span class="btn red">Delete</span></div>
|
||||
<div class="split-a">
|
||||
<div style="padding:14px">
|
||||
<div class="cover">798 × 537</div>
|
||||
<div class="panel" style="margin-top:10px">
|
||||
<div class="label">Back covers / extras</div>
|
||||
<div style="margin-top:10px; border:1px dashed #49475c; border-radius:10px; height:54px; display:grid; place-items:center; color:#817b90; font-size:12px;">Drop extras here or add</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div>
|
||||
<h1>YUJ-001</h1>
|
||||
<div style="font-size:18px; color:#aaa6b8; font-style:italic">Untitled</div>
|
||||
</div>
|
||||
<div class="meta-line"><span class="stars">★★★★★</span><span class="pill cyan">VIP</span><span class="pill green">Watched</span><span class="pill violet">Owned</span></div>
|
||||
<div class="panel summary">
|
||||
<div class="meta-line"><span class="label">Cover</span> 800x538 · 170 KB · Imported 4/29/2026</div>
|
||||
<div class="meta-line"><span class="label">Video</span> 1920x1080 · H.264 · 5.7 Mbps · 4.90 GB · 1:56:55</div>
|
||||
</div>
|
||||
<div class="mini-grid">
|
||||
<div class="field"><span class="label">Actresses</span><strong><span class="pill violet">Ichika Matsumoto</span></strong></div>
|
||||
<div class="field"><span class="label">Collections</span><strong><span class="pill cyan">Sugar & Spice</span></strong></div>
|
||||
<div class="field"><span class="label">Tags</span><strong style="color:#777386">Add tag...</strong></div>
|
||||
<div class="field"><span class="label">Notes</span><strong style="color:#777386">Empty</strong></div>
|
||||
</div>
|
||||
<div class="action-row"><span class="btn">✎ Edit metadata</span><span class="btn">⇩ Import .nfo</span><span class="btn">+ Add extra</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="option">
|
||||
<div class="option-head">
|
||||
<div><h2>Option B: Inspector Sidebar</h2><p>The image/player gets most of the width; a right inspector groups status, file facts, and relationship editing.</p></div>
|
||||
<span class="pick">best for viewing</span>
|
||||
</div>
|
||||
<div class="mock">
|
||||
<div class="bar">← Library <span style="margin-left:auto" class="btn">Prev</span><span class="btn">Shuffle</span><span class="btn">Next</span></div>
|
||||
<div class="split-b">
|
||||
<div class="stage">
|
||||
<div class="cover">798 × 537</div>
|
||||
<div style="display:flex; gap:8px; margin-top:10px">
|
||||
<span class="btn cyan">▶ Play 2 parts</span><span class="btn">+ Add extra</span><span class="btn">⤴ Reveal file</span>
|
||||
</div>
|
||||
<div class="panel" style="margin-top:10px">
|
||||
<div class="label">Extras</div>
|
||||
<div style="display:flex; gap:8px; margin-top:8px"><div class="thumb">Back</div><div class="thumb">Still</div><div class="thumb">+</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="side">
|
||||
<h1>YUJ-001</h1>
|
||||
<div style="color:#aaa6b8; font-style:italic; margin-bottom:12px">Untitled</div>
|
||||
<div class="meta-line" style="margin-bottom:12px"><span class="pill cyan">VIP</span><span class="pill green">Watched</span><span class="pill violet">Owned</span></div>
|
||||
<div class="mini-grid" style="grid-template-columns:1fr">
|
||||
<div class="field"><span class="label">Video file</span><strong>1920x1080 · H.264</strong><div class="meta-line">5.7 Mbps · 4.90 GB · 1:56:55</div></div>
|
||||
<div class="field"><span class="label">Cover file</span><strong>800x538 · 170 KB</strong></div>
|
||||
<div class="field"><span class="label">People</span><strong><span class="pill violet">Ichika Matsumoto</span></strong></div>
|
||||
<div class="field"><span class="label">Tags & Collections</span><strong><span class="pill cyan">Sugar & Spice</span></strong></div>
|
||||
</div>
|
||||
<div class="action-row"><span class="btn">✎ Edit</span><span class="btn red">Delete</span></div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="option">
|
||||
<div class="option-head">
|
||||
<div><h2>Option C: Tabbed Details</h2><p>Reduce vertical clutter by putting metadata, people, tags, and extras under tabs below a single strong media header.</p></div>
|
||||
<span class="pick">least busy</span>
|
||||
</div>
|
||||
<div class="mock hero">
|
||||
<div class="cover">798 × 537</div>
|
||||
<div class="dock">
|
||||
<div style="display:flex; align-items:center; gap:12px">
|
||||
<div><h1>YUJ-001</h1><div style="color:#aaa6b8; font-style:italic">Untitled</div></div>
|
||||
<div style="margin-left:auto" class="meta-line"><span class="pill cyan">VIP</span><span class="pill green">Watched</span><span class="pill violet">Owned</span></div>
|
||||
</div>
|
||||
<div class="tabs"><span class="tab active">Summary</span><span class="tab">People</span><span class="tab">Tags</span><span class="tab">Extras</span><span class="tab">Files</span></div>
|
||||
<div class="mini-grid">
|
||||
<div class="field"><span class="label">Video</span><strong>1920x1080 · H.264 · 5.7 Mbps</strong></div>
|
||||
<div class="field"><span class="label">Length / Size</span><strong>1:56:55 · 4.90 GB</strong></div>
|
||||
<div class="field"><span class="label">Cover</span><strong>800x538 · 170 KB</strong></div>
|
||||
<div class="field"><span class="label">Collection</span><strong><span class="pill cyan">Sugar & Spice</span></strong></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="option">
|
||||
<div class="option-head">
|
||||
<div><h2>Option D: Parts Rail</h2><p>Designed around multi-part videos: parts, cover/back/stills, and file info live in a left rail while the main content stays focused.</p></div>
|
||||
<span class="pick">best for multipart</span>
|
||||
</div>
|
||||
<div class="mock stack">
|
||||
<div class="rail">
|
||||
<div class="label">Parts</div>
|
||||
<div class="thumb active">1<br>1080p</div>
|
||||
<div class="thumb">2<br>1080p</div>
|
||||
<div class="label" style="margin-top:8px">Images</div>
|
||||
<div class="thumb">Cover</div>
|
||||
<div class="thumb">+</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div style="display:flex; align-items:start; gap:12px">
|
||||
<div><h1>YUJ-001</h1><div style="font-size:16px; color:#aaa6b8; font-style:italic">Untitled</div></div>
|
||||
<div style="margin-left:auto; display:flex; gap:7px"><span class="btn">✎ Edit</span><span class="btn red">Delete</span></div>
|
||||
</div>
|
||||
<div class="cover">798 × 537</div>
|
||||
<div class="panel">
|
||||
<div class="meta-line"><span class="label">Selected part</span> 1920x1080 · H.264 · 5.7 Mbps · 4.90 GB · 1:56:55</div>
|
||||
<div class="meta-line" style="margin-top:8px"><span class="pill cyan">VIP</span><span class="pill green">Watched</span><span class="pill violet">Owned</span><span class="pill amber">2 parts</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="option">
|
||||
<div class="option-head">
|
||||
<div><h2>Option E: Compact Workbench</h2><p>A denser editing layout: everything important is visible without scrolling, useful when tagging or cleaning many covers.</p></div>
|
||||
<span class="pick">best for editing</span>
|
||||
</div>
|
||||
<div class="mock dense">
|
||||
<div class="cover">798 × 537</div>
|
||||
<div class="sheet">
|
||||
<div style="display:flex; justify-content:space-between; gap:10px">
|
||||
<div><h1>YUJ-001</h1><div style="color:#aaa6b8; font-style:italic">Untitled</div></div>
|
||||
<span class="btn red">Delete</span>
|
||||
</div>
|
||||
<div class="meta-line"><span class="stars">★★★★★</span><span class="pill cyan">VIP</span><span class="pill green">Watched</span><span class="pill violet">Owned</span></div>
|
||||
<div class="field">
|
||||
<span class="label">Cover file</span>
|
||||
<strong>800x538 · 170 KB</strong>
|
||||
</div>
|
||||
<div class="mini-grid">
|
||||
<div class="field"><span class="label">Video</span><strong>1920x1080 · H.264</strong></div>
|
||||
<div class="field"><span class="label">Bitrate</span><strong>5.7 Mbps</strong></div>
|
||||
<div class="field"><span class="label">File size</span><strong>4.90 GB</strong></div>
|
||||
<div class="field"><span class="label">Length</span><strong>1:56:55</strong></div>
|
||||
</div>
|
||||
<div class="field"><span class="label">Actresses</span><strong><span class="pill violet">Ichika Matsumoto</span> <span class="pill">+ Add</span></strong></div>
|
||||
<div class="field"><span class="label">Tags</span><strong><span class="pill">+ Add tag</span></strong></div>
|
||||
<div class="field"><span class="label">Collections</span><strong><span class="pill cyan">Sugar & Spice</span> <span class="pill">+ Add</span></strong></div>
|
||||
<div class="action-row"><span class="btn cyan">▶ Play</span><span class="btn">✎ Edit metadata</span><span class="btn">⇩ Import .nfo</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="option">
|
||||
<div class="option-head">
|
||||
<div><h2>Option F: Netflix Hero</h2><p>A more cinematic page: oversized media backdrop, direct play as the first action, and metadata folded into readable hero facts.</p></div>
|
||||
<span class="pick">most immersive</span>
|
||||
</div>
|
||||
<div class="mock netflix">
|
||||
<div class="poster-text">798 × 537</div>
|
||||
<div class="hero-copy">
|
||||
<div>
|
||||
<div class="label">Now in library</div>
|
||||
<div class="hero-title">YUJ-001</div>
|
||||
<div class="hero-subtitle">Untitled</div>
|
||||
</div>
|
||||
<div class="hero-facts">
|
||||
<span>1920x1080</span><span>·</span><span>H.264</span><span>·</span><span>5.7 Mbps</span><span>·</span><span>4.90 GB</span><span>·</span><span>1:56:55</span>
|
||||
</div>
|
||||
<div class="meta-line"><span class="stars">★★★★★</span><span class="pill cyan">VIP</span><span class="pill green">Watched</span><span class="pill violet">Owned</span><span class="pill amber">2 parts</span></div>
|
||||
<div class="hero-actions">
|
||||
<span class="play">▶ Play</span>
|
||||
<span class="ghost">+ Add tag</span>
|
||||
<span class="ghost">✎ Edit</span>
|
||||
<span class="ghost">⤴ Reveal</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom-shelf">
|
||||
<div class="netflix-card">
|
||||
<div class="label">Actresses</div>
|
||||
<div style="margin-top:8px"><span class="pill violet">Ichika Matsumoto</span></div>
|
||||
</div>
|
||||
<div class="netflix-card">
|
||||
<div class="label">Collection</div>
|
||||
<div style="margin-top:8px"><span class="pill cyan">Sugar & Spice</span></div>
|
||||
</div>
|
||||
<div class="netflix-card">
|
||||
<div class="label">Cover file</div>
|
||||
<div class="meta-line" style="margin-top:8px">800x538 · 170 KB</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="note-list">
|
||||
<div class="note"><strong>1. Put video facts in their own file block.</strong>Cover resolution and video resolution currently sit close together. Separate “Cover file” from “Video file” so 800x538 and 1920x1080 do not visually compete.</div>
|
||||
<div class="note"><strong>2. Consolidate VIP/Favorite/Watched/Owned.</strong>They read like separate widgets today. Make them a single status row with equal sizing and clearer active/inactive contrast.</div>
|
||||
<div class="note"><strong>3. Move destructive actions away from metadata.</strong>Delete is visually close to identity info. Put it in an action cluster or overflow menu to reduce accidental attention.</div>
|
||||
<div class="note"><strong>4. Make Play the primary page action.</strong>The play button on the cover is good, but add a clear “Play 2 parts” action near metadata for keyboard/mouse scanning.</div>
|
||||
<div class="note"><strong>5. Use tabs if the page starts feeling administrative.</strong>Summary, People, Tags, Extras, Files lets the page breathe while keeping all editing reachable.</div>
|
||||
<div class="note"><strong>6. Show per-part metadata where parts are selected.</strong>For multi-part videos, show 1080p/H.264 on each part chip instead of one ambiguous summary.</div>
|
||||
<div class="note"><strong>7. Combine tags and collections into one relationship editor.</strong>Both are library organization tools, so one “Organize” panel could reduce card stacking.</div>
|
||||
<div class="note"><strong>8. Make the extras area a media strip.</strong>Back covers/extras currently consume a full panel even when empty. A compact strip keeps the main page lighter.</div>
|
||||
<div class="note"><strong>9. Keep editing controls closer to fields.</strong>Edit metadata, import NFO, add tag, and add collection can sit near their target sections instead of the bottom-left corner.</div>
|
||||
<div class="note"><strong>10. Add a file health badge later.</strong>Once metadata exists, a small “Direct Play”, “Large file”, or “Unknown metadata” badge can help diagnose playback without clutter.</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user