Files
pinkudex/mockups/tag-import-mockups.html
2026-05-26 22:46:00 +02:00

725 lines
35 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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 &amp; 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 &amp; 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">AZ ↓</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 &amp; 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>