chore: add brainstorming docs and design reference
This commit is contained in:
@@ -0,0 +1,589 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Kotobane — Design Reference</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { background: #0D0D14; color: #F3F5F7; font-family: 'Inter', system-ui, sans-serif; font-size: 14px; }
|
||||
|
||||
/* ── TAB BAR ── */
|
||||
#tab-bar {
|
||||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||
background: #171A21; border-bottom: 1px solid #2A3140;
|
||||
display: flex; align-items: center; gap: 0; height: 48px; padding: 0 24px;
|
||||
}
|
||||
#tab-bar .brand { font-size: 15px; font-weight: 800; color: #00B4D8; letter-spacing: 3px; margin-right: 24px; flex-shrink: 0; }
|
||||
#tab-bar .divider { width: 1px; height: 18px; background: #2A3140; margin-right: 20px; }
|
||||
.tab-btn {
|
||||
background: none; border: none; color: #7D8795; font-size: 12px; font-weight: 500;
|
||||
padding: 0 14px; height: 48px; cursor: pointer; border-bottom: 2px solid transparent;
|
||||
transition: color 0.15s, border-color 0.15s; white-space: nowrap;
|
||||
}
|
||||
.tab-btn:hover { color: #B8C0CC; }
|
||||
.tab-btn.active { color: #00B4D8; border-bottom-color: #00B4D8; }
|
||||
.tab-hint { margin-left: auto; font-size: 11px; color: #5C6470; }
|
||||
|
||||
/* ── PANELS ── */
|
||||
.panel { display: none; padding-top: 48px; }
|
||||
.panel.active { display: block; }
|
||||
|
||||
/* ════════════════════════════════
|
||||
PANEL 1 — HOMEPAGE
|
||||
════════════════════════════════ */
|
||||
nav.site-nav {
|
||||
background: #171A21; border-bottom: 1px solid #2A3140; padding: 0 32px;
|
||||
height: 52px; display: flex; align-items: center; gap: 24px;
|
||||
}
|
||||
.logo { font-size: 17px; font-weight: 800; color: #00B4D8; letter-spacing: 3px; }
|
||||
.nav-sep { width: 1px; height: 16px; background: #2A3140; }
|
||||
.nav-a { font-size: 12px; font-weight: 500; color: #7D8795; cursor: pointer; white-space: nowrap; }
|
||||
.nav-a.on { color: #00B4D8; border-bottom: 2px solid #00B4D8; padding-bottom: 2px; }
|
||||
.nav-search { margin-left: auto; display: flex; align-items: center; gap: 6px; background: #1D212B; border: 1px solid #2A3140; border-radius: 6px; padding: 5px 10px; cursor: pointer; }
|
||||
.nav-search span { font-size: 11px; color: #5C6470; }
|
||||
.kbd { background: #2A3140; border-radius: 3px; padding: 1px 5px; font-size: 10px; color: #7D8795; font-family: monospace; }
|
||||
|
||||
.ticker { background: #171A21; border-bottom: 1px solid #2A3140; padding: 8px 32px; display: flex; align-items: center; gap: 14px; overflow: hidden; }
|
||||
.ticker-label { background: #00B4D8; color: #000; font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 3px; white-space: nowrap; letter-spacing: 0.5px; }
|
||||
.ticker-items { display: flex; gap: 20px; overflow: hidden; }
|
||||
.ticker-item { font-size: 11px; color: #B8C0CC; white-space: nowrap; display: flex; align-items: center; gap: 8px; }
|
||||
.ticker-dot { width: 3px; height: 3px; border-radius: 50%; background: #2A3140; flex-shrink: 0; }
|
||||
|
||||
.hero { display: grid; grid-template-columns: 1fr 360px; gap: 2px; max-width: 1200px; margin: 28px auto; padding: 0 32px; }
|
||||
.hero-main { background: #1D212B; border-radius: 16px 0 0 16px; overflow: hidden; position: relative; min-height: 400px; display: flex; flex-direction: column; justify-content: flex-end; }
|
||||
.hero-bg { position: absolute; inset: 0; background: linear-gradient(135deg,#0D0D14,#1a1730,#0d1a2e); }
|
||||
.hero-bg::after { content:''; position:absolute; inset:0; background: linear-gradient(to top,#0D0D14 0%,transparent 55%); }
|
||||
.hero-c { position:relative; z-index:1; padding:26px; }
|
||||
.hero-badges { display:flex; gap:8px; margin-bottom:12px; }
|
||||
.badge { font-size:10px; font-weight:700; padding:3px 10px; border-radius:4px; letter-spacing:0.5px; }
|
||||
.b-feat { background:#7C3AED; color:#fff; }
|
||||
.b-cat { background:transparent; border:1px solid #2A3140; color:#7D8795; }
|
||||
.b-trend { background:#D64545; color:#fff; }
|
||||
.b-new { background:#FFB300; color:#000; }
|
||||
.b-pub { background:#00D166; color:#000; }
|
||||
.hero-title { font-size:24px; font-weight:700; line-height:1.3; margin-bottom:10px; }
|
||||
.hero-excerpt { font-size:13px; color:#B8C0CC; line-height:1.6; margin-bottom:14px; }
|
||||
.hero-meta { font-size:11px; color:#5C6470; display:flex; gap:10px; align-items:center; }
|
||||
.hero-read { font-size:12px; color:#00B4D8; font-weight:600; margin-left:auto; }
|
||||
.hero-stack { display:flex; flex-direction:column; gap:2px; }
|
||||
.hero-side { background:#1D212B; padding:16px; flex:1; display:flex; flex-direction:column; justify-content:flex-end; position:relative; overflow:hidden; }
|
||||
.hero-side:first-child { border-radius:0 16px 0 0; }
|
||||
.hero-side:last-child { border-radius:0 0 16px 0; }
|
||||
.hero-side-bg { position:absolute; inset:0; background:linear-gradient(135deg,#0d1a2e,#1a1730); opacity:.6; }
|
||||
.hero-side-c { position:relative; z-index:1; }
|
||||
.hero-side-title { font-size:13px; font-weight:600; line-height:1.4; margin:8px 0 5px; }
|
||||
.hero-side-meta { font-size:10px; color:#5C6470; }
|
||||
|
||||
.section { max-width:1200px; margin:0 auto 36px; padding:0 32px; }
|
||||
.sec-hd { display:flex; align-items:center; gap:10px; margin-bottom:16px; }
|
||||
.sec-title { font-size:12px; font-weight:700; color:#F3F5F7; letter-spacing:1px; text-transform:uppercase; white-space:nowrap; }
|
||||
.sec-line { flex:1; height:1px; background:#2A3140; }
|
||||
.sec-more { font-size:11px; color:#00B4D8; font-weight:600; cursor:pointer; white-space:nowrap; }
|
||||
|
||||
.g4 { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; }
|
||||
.g3 { display:grid; grid-template-columns:repeat(3,1fr); gap:12px; }
|
||||
.card { background:#1D212B; border:1px solid #2A3140; border-radius:12px; overflow:hidden; cursor:pointer; }
|
||||
.card-img { height:130px; background:linear-gradient(135deg,#0D0D14,#1a1730); }
|
||||
.ci-vt { background:linear-gradient(135deg,#120d1a,#1e1530); }
|
||||
.ci-mg { background:linear-gradient(135deg,#1a0d0d,#2a1515); }
|
||||
.ci-gm { background:linear-gradient(135deg,#1a1200,#2a1f00); }
|
||||
.ci-mu { background:linear-gradient(135deg,#0d1a18,#152a26); }
|
||||
.card-body { padding:12px; }
|
||||
.card-cat { font-size:10px; font-weight:600; color:#7D8795; letter-spacing:1px; text-transform:uppercase; margin-bottom:7px; display:flex; align-items:center; gap:6px; }
|
||||
.card-title { font-size:12px; font-weight:600; color:#F3F5F7; line-height:1.4; margin-bottom:9px; }
|
||||
.card-ft { display:flex; justify-content:space-between; align-items:center; }
|
||||
.card-date { font-size:10px; color:#5C6470; }
|
||||
.card-read { font-size:11px; color:#00B4D8; font-weight:600; }
|
||||
.card-wide { background:#1D212B; border:1px solid #2A3140; border-radius:12px; display:flex; overflow:hidden; cursor:pointer; }
|
||||
.cwi { width:160px; flex-shrink:0; background:linear-gradient(135deg,#0D0D14,#1a1730); }
|
||||
.cwb { padding:14px; display:flex; flex-direction:column; justify-content:space-between; flex:1; }
|
||||
|
||||
/* ════════════════════════════════
|
||||
PANEL 2 — ARTICLE
|
||||
════════════════════════════════ */
|
||||
.art-wrap { max-width:760px; margin:0 auto; padding:40px 32px; }
|
||||
.breadcrumb { display:flex; align-items:center; gap:8px; margin-bottom:24px; font-size:12px; color:#5C6470; }
|
||||
.breadcrumb a { color:#7D8795; cursor:pointer; }
|
||||
.breadcrumb a:hover { color:#00B4D8; }
|
||||
.art-cat-label { font-size:10px; font-weight:700; letter-spacing:2px; color:#7D8795; text-transform:uppercase; margin-bottom:12px; }
|
||||
.art-title { font-size:34px; font-weight:700; line-height:1.25; margin-bottom:14px; }
|
||||
.art-excerpt { font-size:16px; color:#B8C0CC; line-height:1.65; margin-bottom:22px; }
|
||||
.art-meta { display:flex; align-items:center; gap:10px; padding:12px 0; border-top:1px solid #2A3140; border-bottom:1px solid #2A3140; margin-bottom:30px; }
|
||||
.dot3 { width:3px; height:3px; border-radius:50%; background:#2A3140; }
|
||||
.art-hero-img { width:100%; height:380px; background:linear-gradient(135deg,#0D0D14,#1a1730,#0d1a2e); border-radius:12px; margin-bottom:32px; display:flex; align-items:center; justify-content:center; }
|
||||
.art-hero-img span { font-size:12px; color:#2A3140; }
|
||||
.art-body { font-size:16px; line-height:1.8; color:#B8C0CC; }
|
||||
.art-body p { margin-bottom:18px; }
|
||||
.art-body h2 { font-size:22px; font-weight:700; color:#F3F5F7; margin:32px 0 14px; }
|
||||
.art-body h3 { font-size:17px; font-weight:600; color:#F3F5F7; margin:24px 0 10px; }
|
||||
.art-body strong { color:#F3F5F7; font-weight:600; }
|
||||
.art-body blockquote { border-left:3px solid #00B4D8; padding:10px 18px; background:#1D212B; border-radius:0 8px 8px 0; margin:20px 0; }
|
||||
.art-body blockquote p { margin:0; color:#B8C0CC; font-style:italic; }
|
||||
.art-tags { display:flex; flex-wrap:wrap; gap:8px; padding:24px 0; border-top:1px solid #2A3140; margin-top:28px; }
|
||||
.tag { background:#1D212B; border:1px solid #2A3140; color:#B8C0CC; font-size:11px; padding:4px 12px; border-radius:6px; cursor:pointer; }
|
||||
.tag:hover { border-color:#00B4D8; color:#00B4D8; }
|
||||
.related { max-width:760px; margin:0 auto 40px; padding:0 32px; }
|
||||
|
||||
/* ════════════════════════════════
|
||||
PANEL 3 — COLORS
|
||||
════════════════════════════════ */
|
||||
.colors-wrap { max-width:860px; margin:0 auto; padding:40px 32px; display:flex; flex-direction:column; gap:28px; }
|
||||
.pal-section-label { font-size:11px; letter-spacing:2px; color:#7D8795; margin-bottom:10px; }
|
||||
.swatch-row { display:grid; gap:10px; }
|
||||
.swatch { background:#1D212B; border:1px solid #2A3140; border-radius:12px; padding:14px; }
|
||||
.swatch-color { width:100%; height:48px; border-radius:8px; margin-bottom:10px; }
|
||||
.swatch-hex { font-family:monospace; font-size:11px; margin-bottom:3px; }
|
||||
.swatch-name { font-size:12px; font-weight:600; color:#F3F5F7; margin-bottom:2px; }
|
||||
.swatch-use { font-size:11px; color:#5C6470; line-height:1.4; }
|
||||
.badge-row { display:flex; flex-wrap:wrap; gap:10px; align-items:center; padding:18px; background:#1D212B; border:1px solid #2A3140; border-radius:12px; }
|
||||
.text-demo { background:#1D212B; border:1px solid #2A3140; border-radius:12px; padding:20px; display:flex; flex-direction:column; gap:10px; }
|
||||
.text-demo-row { display:flex; justify-content:space-between; align-items:baseline; }
|
||||
|
||||
/* ════════════════════════════════
|
||||
PANEL 4 — DIRECTUS
|
||||
════════════════════════════════ */
|
||||
.dir-wrap { max-width:820px; margin:0 auto; padding:40px 32px; display:flex; flex-direction:column; gap:12px; }
|
||||
.step { background:#1D212B; border:1px solid #2A3140; border-radius:12px; padding:18px; display:flex; gap:14px; align-items:flex-start; }
|
||||
.step-num { width:28px; height:28px; border-radius:50%; font-size:12px; font-weight:800; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
|
||||
.step-body { flex:1; }
|
||||
.step-title { font-size:14px; font-weight:700; margin-bottom:5px; }
|
||||
.step-desc { font-size:13px; color:#B8C0CC; line-height:1.6; }
|
||||
.step-note { margin-top:8px; background:#0D0D14; border-radius:6px; padding:8px 12px; font-size:12px; color:#B8C0CC; border-left:2px solid #00B4D8; }
|
||||
.step-note.amber { border-left-color:#FFB300; }
|
||||
.step-note.green { border-left-color:#00D166; }
|
||||
.connector { padding-left:33px; }
|
||||
.connector-line { width:2px; height:16px; background:#2A3140; border-radius:1px; }
|
||||
.field-grid { display:grid; grid-template-columns:1fr 1fr; gap:6px; margin-top:10px; }
|
||||
.field { background:#0D0D14; border-radius:6px; padding:6px 10px; display:flex; align-items:center; gap:8px; font-size:11px; }
|
||||
.fn { font-family:monospace; color:#00B4D8; }
|
||||
.ft { color:#5C6470; }
|
||||
.f-req { font-size:9px; color:#D64545; font-weight:700; margin-left:auto; }
|
||||
.f-opt { font-size:9px; color:#5C6470; margin-left:auto; }
|
||||
.dir-divider { height:1px; background:#2A3140; margin:12px 0; }
|
||||
.dir-section-label { font-size:11px; letter-spacing:2px; color:#7D8795; margin-bottom:10px; }
|
||||
.faq-item { background:#1D212B; border:1px solid #2A3140; border-radius:10px; padding:14px; }
|
||||
.faq-q { font-size:13px; font-weight:600; margin-bottom:5px; }
|
||||
.faq-a { font-size:13px; color:#B8C0CC; line-height:1.6; }
|
||||
.faq-a code { background:#0D0D14; border-radius:3px; padding:1px 5px; font-size:12px; color:#00B4D8; font-family:monospace; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- TAB BAR -->
|
||||
<div id="tab-bar">
|
||||
<span class="brand">言羽</span>
|
||||
<div class="divider"></div>
|
||||
<button class="tab-btn active" onclick="switchTab('homepage', this)">Homepage</button>
|
||||
<button class="tab-btn" onclick="switchTab('article', this)">Article Page</button>
|
||||
<button class="tab-btn" onclick="switchTab('colors', this)">Color System</button>
|
||||
<button class="tab-btn" onclick="switchTab('directus', this)">Directus Workflow</button>
|
||||
<span class="tab-hint">Design Reference · Kotobane</span>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════════════════════════════════
|
||||
PANEL: HOMEPAGE
|
||||
══════════════════════════════════════════ -->
|
||||
<div id="panel-homepage" class="panel active">
|
||||
|
||||
<nav class="site-nav">
|
||||
<div class="logo">言羽</div>
|
||||
<div class="nav-sep"></div>
|
||||
<a class="nav-a on">Anime</a>
|
||||
<a class="nav-a">VTubers</a>
|
||||
<a class="nav-a">Manga</a>
|
||||
<a class="nav-a">Games</a>
|
||||
<a class="nav-a">Music</a>
|
||||
<a class="nav-a">Japan</a>
|
||||
<a class="nav-a">Culture</a>
|
||||
<a class="nav-a">Industry</a>
|
||||
<div class="nav-search">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#5C6470" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
|
||||
<span>Search</span>
|
||||
<div class="kbd">⌘K</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="ticker">
|
||||
<div class="ticker-label">LATEST</div>
|
||||
<div class="ticker-items">
|
||||
<div class="ticker-item">Hololive EN Gen 4 debut schedule announced<div class="ticker-dot"></div></div>
|
||||
<div class="ticker-item">Blue Protocol global release date confirmed<div class="ticker-dot"></div></div>
|
||||
<div class="ticker-item">Chainsaw Man Part 3 begins serialization<div class="ticker-dot"></div></div>
|
||||
<div class="ticker-item">Yoasobi world tour dates revealed<div class="ticker-dot"></div></div>
|
||||
<div class="ticker-item">Summer 2026 anime season preview</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero">
|
||||
<div class="hero-main">
|
||||
<div class="hero-bg"></div>
|
||||
<div class="hero-c">
|
||||
<div class="hero-badges">
|
||||
<span class="badge b-feat">FEATURED</span>
|
||||
<span class="badge b-cat">ANIME</span>
|
||||
</div>
|
||||
<div class="hero-title">Frieren: Beyond Journey's End Season 2 Officially Announced — Production Already Underway at Madhouse</div>
|
||||
<div class="hero-excerpt">The beloved fantasy series returns for a second cour. Studio Madhouse confirmed production is already underway, with original staff and cast returning in full.</div>
|
||||
<div class="hero-meta">
|
||||
<span>28 May 2026</span><span>·</span><span>4 min read</span>
|
||||
<span class="hero-read">Read article →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-stack">
|
||||
<div class="hero-side">
|
||||
<div class="hero-side-bg"></div>
|
||||
<div class="hero-side-c">
|
||||
<div style="display:flex;gap:6px;align-items:center">
|
||||
<span class="badge b-trend" style="font-size:9px;padding:2px 6px">TRENDING</span>
|
||||
<span style="font-size:10px;color:#7D8795;font-weight:600;letter-spacing:1px">VTUBERS</span>
|
||||
</div>
|
||||
<div class="hero-side-title">Hololive EN Gen 4 — Full Lineup and Debut Schedule Revealed</div>
|
||||
<div class="hero-side-meta">27 May 2026 · 3 min read</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-side">
|
||||
<div class="hero-side-bg" style="background:linear-gradient(135deg,#1a1200,#2a1f00)"></div>
|
||||
<div class="hero-side-c">
|
||||
<div style="display:flex;gap:6px;align-items:center">
|
||||
<span class="badge b-new" style="font-size:9px;padding:2px 6px">NEW</span>
|
||||
<span style="font-size:10px;color:#7D8795;font-weight:600;letter-spacing:1px">GAMES</span>
|
||||
</div>
|
||||
<div class="hero-side-title">Blue Protocol Global Launch: Everything You Need to Know</div>
|
||||
<div class="hero-side-meta">26 May 2026 · 6 min read</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="sec-hd"><div class="sec-title">Latest</div><div class="sec-line"></div><div class="sec-more">See all →</div></div>
|
||||
<div class="g4">
|
||||
<div class="card">
|
||||
<div class="card-img ci-vt"></div>
|
||||
<div class="card-body">
|
||||
<div class="card-cat">VTubers <span class="badge b-trend" style="font-size:9px;padding:1px 5px">TRENDING</span></div>
|
||||
<div class="card-title">Nijisanji EN Announces Three New Wave Members</div>
|
||||
<div class="card-ft"><span class="card-date">28 May</span><span class="card-read">Read →</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-img ci-mg"></div>
|
||||
<div class="card-body">
|
||||
<div class="card-cat">Manga</div>
|
||||
<div class="card-title">Chainsaw Man Part 3 First Chapter Review: Fujimoto Raises the Stakes</div>
|
||||
<div class="card-ft"><span class="card-date">27 May</span><span class="card-read">Read →</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-img ci-gm"></div>
|
||||
<div class="card-body">
|
||||
<div class="card-cat">Games</div>
|
||||
<div class="card-title">Elden Ring DLC Shadow of the Erdtree — Final Sales Numbers Released</div>
|
||||
<div class="card-ft"><span class="card-date">27 May</span><span class="card-read">Read →</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-img ci-mu"></div>
|
||||
<div class="card-body">
|
||||
<div class="card-cat">Music <span class="badge b-new" style="font-size:9px;padding:1px 5px">NEW</span></div>
|
||||
<div class="card-title">Yoasobi Drops Surprise Single for Frieren S2 Opening Theme</div>
|
||||
<div class="card-ft"><span class="card-date">26 May</span><span class="card-read">Read →</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="sec-hd"><div class="sec-title">Anime</div><div class="sec-line"></div><div class="sec-more">See all →</div></div>
|
||||
<div class="g3">
|
||||
<div class="card-wide">
|
||||
<div class="cwi"></div>
|
||||
<div class="cwb">
|
||||
<div>
|
||||
<div class="card-cat" style="margin-bottom:8px">Anime</div>
|
||||
<div style="font-size:13px;font-weight:600;color:#F3F5F7;line-height:1.4;margin-bottom:6px">Summer 2026 Season Preview: Every Series Ranked</div>
|
||||
<div style="font-size:11px;color:#7D8795;line-height:1.5">A complete guide to every series airing this season.</div>
|
||||
</div>
|
||||
<div class="card-ft" style="margin-top:10px"><span class="card-date">25 May</span><span class="card-read">Read →</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-img" style="height:110px"></div>
|
||||
<div class="card-body">
|
||||
<div class="card-cat">Anime</div>
|
||||
<div class="card-title">Dungeon Meshi Season 2 — Confirmed for Winter 2027</div>
|
||||
<div class="card-ft"><span class="card-date">24 May</span><span class="card-read">Read →</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-img ci-mu" style="height:110px"></div>
|
||||
<div class="card-body">
|
||||
<div class="card-cat">Anime</div>
|
||||
<div class="card-title">Re:Zero Season 4 Production Confirmed by White Fox</div>
|
||||
<div class="card-ft"><span class="card-date">23 May</span><span class="card-read">Read →</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer style="border-top:1px solid #2A3140;padding:22px 32px;max-width:1200px;margin:0 auto;display:flex;justify-content:space-between;align-items:center">
|
||||
<div class="logo">言羽</div>
|
||||
<div style="font-size:11px;color:#5C6470">© 2026 Kotobane · Japanese pop culture news</div>
|
||||
<div style="display:flex;gap:16px">
|
||||
<span style="font-size:11px;color:#7D8795;cursor:pointer">About</span>
|
||||
<span style="font-size:11px;color:#7D8795;cursor:pointer">Archive</span>
|
||||
<span style="font-size:11px;color:#7D8795;cursor:pointer">RSS</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════════════════════════════════
|
||||
PANEL: ARTICLE
|
||||
══════════════════════════════════════════ -->
|
||||
<div id="panel-article" class="panel">
|
||||
<div class="art-wrap">
|
||||
<div class="breadcrumb">
|
||||
<a>Home</a><span>›</span><a>Anime</a><span>›</span>
|
||||
<span style="color:#7D8795">Frieren Season 2 Production Confirmed</span>
|
||||
</div>
|
||||
<div class="art-cat-label">Anime</div>
|
||||
<h1 class="art-title">Frieren: Beyond Journey's End Season 2 Officially Announced — Production Already Underway at Madhouse</h1>
|
||||
<p class="art-excerpt">The beloved fantasy series returns for a second cour. Studio Madhouse confirmed production is already underway, with the original staff and full cast returning.</p>
|
||||
<div class="art-meta">
|
||||
<span class="badge b-feat">FEATURED</span>
|
||||
<span class="badge b-trend">TRENDING</span>
|
||||
<div class="dot3"></div>
|
||||
<span style="font-size:12px;color:#7D8795">28 May 2026</span>
|
||||
<div class="dot3"></div>
|
||||
<span style="font-size:12px;color:#7D8795">4 min read</span>
|
||||
</div>
|
||||
<div class="art-hero-img"><span>Featured image — served from cms.achmad.dev/assets/</span></div>
|
||||
<div class="art-body">
|
||||
<p>Studio Madhouse has officially confirmed that <strong>Frieren: Beyond Journey's End</strong> will return for a second season. The announcement came via the series' official Twitter account and was accompanied by a short teaser image featuring the main cast.</p>
|
||||
<h2>What We Know So Far</h2>
|
||||
<p>According to the official announcement, the core production team remains unchanged. Director Keiichiro Saito and series composer Tomohiro Suzuki both confirmed their return, alongside the full original voice cast.</p>
|
||||
<blockquote><p>"We're incredibly grateful for the response to the first season. We want to give the second the same care and time it deserves."<br>— Director Keiichiro Saito</p></blockquote>
|
||||
<p>No release window has been confirmed yet, though industry sources suggest a late 2026 or early 2027 premiere is most likely given the production timeline.</p>
|
||||
<h3>Production at Madhouse</h3>
|
||||
<p>The series will again be animated at <strong>Madhouse</strong>, the studio behind acclaimed productions such as Death Note, Hunter x Hunter, and One Punch Man Season 1.</p>
|
||||
<h2>What Comes Next in the Story</h2>
|
||||
<p>The first season adapted through the Granat arc. Season 2 is expected to begin with the Auberst arc and move into the series' most celebrated material, including the Lügner confrontation.</p>
|
||||
</div>
|
||||
<div class="art-tags">
|
||||
<div class="tag">frieren</div><div class="tag">madhouse</div><div class="tag">anime season 2</div><div class="tag">fantasy anime</div><div class="tag">2026 anime</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<div class="sec-hd" style="margin-bottom:14px"><div class="sec-title">More from Anime</div><div class="sec-line"></div></div>
|
||||
<div class="g3">
|
||||
<div class="card"><div class="card-img"></div><div class="card-body"><div class="card-cat">Anime</div><div class="card-title">Summer 2026 Season Preview: Every Series Ranked</div><div class="card-read">Read →</div></div></div>
|
||||
<div class="card"><div class="card-img ci-mu"></div><div class="card-body"><div class="card-cat">Anime</div><div class="card-title">Re:Zero Season 4 Officially Confirmed by White Fox</div><div class="card-read">Read →</div></div></div>
|
||||
<div class="card"><div class="card-img ci-gm"></div><div class="card-body"><div class="card-cat">Anime</div><div class="card-title">Dungeon Meshi Season 2 — Confirmed for Winter 2027</div><div class="card-read">Read →</div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════════════════════════════════
|
||||
PANEL: COLORS
|
||||
══════════════════════════════════════════ -->
|
||||
<div id="panel-colors" class="panel">
|
||||
<div class="colors-wrap">
|
||||
|
||||
<div>
|
||||
<div class="pal-section-label">BRAND ACCENT</div>
|
||||
<div class="swatch-row" style="grid-template-columns:1fr 1fr">
|
||||
<div class="swatch" style="border-color:#00B4D844">
|
||||
<div class="swatch-color" style="background:#00B4D8"></div>
|
||||
<div class="swatch-hex" style="color:#00B4D8">#00B4D8</div>
|
||||
<div class="swatch-name">Sky Cyan — Primary</div>
|
||||
<div class="swatch-use">Buttons, links, active nav, "Read →", focus rings</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="swatch-color" style="background:#00D4FF"></div>
|
||||
<div class="swatch-hex" style="color:#00D4FF">#00D4FF</div>
|
||||
<div class="swatch-name">Sky Cyan — Hover</div>
|
||||
<div class="swatch-use">Hover state on links and buttons</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="pal-section-label">STATUS & BADGE COLORS</div>
|
||||
<div class="swatch-row" style="grid-template-columns:repeat(4,1fr)">
|
||||
<div class="swatch">
|
||||
<div class="swatch-color" style="background:#7C3AED;display:flex;align-items:center;justify-content:center"><span style="color:#fff;font-size:10px;font-weight:700">FEATURED</span></div>
|
||||
<div class="swatch-hex" style="color:#7C3AED">#7C3AED</div>
|
||||
<div class="swatch-name">Violet</div>
|
||||
<div class="swatch-use">Featured / hero badges, secondary actions</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="swatch-color" style="background:#D64545;display:flex;align-items:center;justify-content:center"><span style="color:#fff;font-size:10px;font-weight:700">TRENDING</span></div>
|
||||
<div class="swatch-hex" style="color:#D64545">#D64545</div>
|
||||
<div class="swatch-name">Coral</div>
|
||||
<div class="swatch-use">Trending, breaking news, error states</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="swatch-color" style="background:#FFB300;display:flex;align-items:center;justify-content:center"><span style="color:#000;font-size:10px;font-weight:700">NEW</span></div>
|
||||
<div class="swatch-hex" style="color:#FFB300">#FFB300</div>
|
||||
<div class="swatch-name">Amber</div>
|
||||
<div class="swatch-use">New indicator, alerts, warnings</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="swatch-color" style="background:#00D166;display:flex;align-items:center;justify-content:center"><span style="color:#000;font-size:10px;font-weight:700">PUBLISHED</span></div>
|
||||
<div class="swatch-hex" style="color:#00D166">#00D166</div>
|
||||
<div class="swatch-name">Green</div>
|
||||
<div class="swatch-use">Published status, success states</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="pal-section-label">BACKGROUNDS — layered dark</div>
|
||||
<div class="swatch-row" style="grid-template-columns:repeat(4,1fr)">
|
||||
<div class="swatch"><div class="swatch-color" style="background:#0D0D14;border:1px solid #1a1a1a"></div><div class="swatch-hex" style="color:#7D8795">#0D0D14</div><div class="swatch-use">Main page background</div></div>
|
||||
<div class="swatch"><div class="swatch-color" style="background:#171A21"></div><div class="swatch-hex" style="color:#7D8795">#171A21</div><div class="swatch-use">Navbar, elevated surfaces</div></div>
|
||||
<div class="swatch"><div class="swatch-color" style="background:#1D212B"></div><div class="swatch-hex" style="color:#7D8795">#1D212B</div><div class="swatch-use">Cards, inputs</div></div>
|
||||
<div class="swatch"><div class="swatch-color" style="background:#2A3140"></div><div class="swatch-hex" style="color:#7D8795">#2A3140</div><div class="swatch-use">All borders and dividers</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="pal-section-label">TEXT HIERARCHY</div>
|
||||
<div class="text-demo">
|
||||
<div class="text-demo-row"><span style="color:#F3F5F7;font-size:18px;font-weight:600">Headlines & primary body</span><span style="font-family:monospace;font-size:11px;color:#5C6470">#F3F5F7</span></div>
|
||||
<div class="text-demo-row"><span style="color:#B8C0CC;font-size:15px">Excerpts, captions, secondary text</span><span style="font-family:monospace;font-size:11px;color:#5C6470">#B8C0CC</span></div>
|
||||
<div class="text-demo-row"><span style="color:#7D8795;font-size:13px">Timestamps, category labels, metadata</span><span style="font-family:monospace;font-size:11px;color:#5C6470">#7D8795</span></div>
|
||||
<div class="text-demo-row"><span style="color:#5C6470;font-size:12px">Disabled / placeholder</span><span style="font-family:monospace;font-size:11px;color:#5C6470">#5C6470</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="pal-section-label">USAGE RULE — Category labels are always muted, never color-coded</div>
|
||||
<div style="background:#1D212B;border:1px solid #2A3140;border-radius:12px;padding:16px;display:flex;gap:10px;flex-wrap:wrap;align-items:center">
|
||||
<div style="background:#0D0D14;border-radius:8px;padding:10px 14px">
|
||||
<div style="font-size:10px;color:#7D8795;font-weight:600;letter-spacing:1px;text-transform:uppercase;margin-bottom:6px">Anime</div>
|
||||
<div style="font-size:12px;font-weight:600;color:#F3F5F7">Article title here</div>
|
||||
</div>
|
||||
<div style="font-size:20px;color:#2A3140">→</div>
|
||||
<div style="font-size:13px;color:#B8C0CC;flex:1;min-width:200px">Category name stays <span style="color:#7D8795">#7D8795</span> muted in all contexts. Colors only appear on status badges (TRENDING, FEATURED, NEW, PUBLISHED) and UI chrome (buttons, links, focus rings).</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════════════════════════════════
|
||||
PANEL: DIRECTUS
|
||||
══════════════════════════════════════════ -->
|
||||
<div id="panel-directus" class="panel">
|
||||
<div class="dir-wrap">
|
||||
|
||||
<div style="margin-bottom:8px">
|
||||
<h2 style="font-size:20px;font-weight:700;margin-bottom:4px">How to Add Content in Directus</h2>
|
||||
<p style="font-size:13px;color:#7D8795">cms.achmad.dev — your complete publishing workflow</p>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-num" style="background:#00B4D8;color:#000">1</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Go to Content → articles → + New Item</div>
|
||||
<div class="step-desc">In the Directus sidebar, click <strong>Content</strong>, select <strong>articles</strong>, then hit the <strong>+ New</strong> button. A form opens with all the fields.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="connector"><div class="connector-line"></div></div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-num" style="background:#00B4D8;color:#000">2</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Fill in the fields</div>
|
||||
<div class="step-desc">Only these are required to publish:</div>
|
||||
<div class="field-grid">
|
||||
<div class="field"><span class="fn">title</span><span class="ft">text</span><span class="f-req">required</span></div>
|
||||
<div class="field"><span class="fn">slug</span><span class="ft">auto-generated from title</span><span class="f-req">required</span></div>
|
||||
<div class="field"><span class="fn">content</span><span class="ft">WYSIWYG rich text</span><span class="f-req">required</span></div>
|
||||
<div class="field"><span class="fn">category</span><span class="ft">dropdown</span><span class="f-req">required</span></div>
|
||||
<div class="field"><span class="fn">excerpt</span><span class="ft">short blurb for cards</span><span class="f-opt">optional</span></div>
|
||||
<div class="field"><span class="fn">featured_image</span><span class="ft">file upload</span><span class="f-opt">optional</span></div>
|
||||
</div>
|
||||
<div class="step-note">The <strong>slug</strong> is the URL. Title "Frieren Season 2 Announced" → slug <code style="background:#1D212B;border-radius:3px;padding:1px 5px;font-size:11px;color:#00B4D8;font-family:monospace">frieren-season-2-announced</code> → URL <code style="background:#1D212B;border-radius:3px;padding:1px 5px;font-size:11px;color:#00B4D8;font-family:monospace">/anime/frieren-season-2-announced</code></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="connector"><div class="connector-line"></div></div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-num" style="background:#00B4D8;color:#000">3</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Write content in the WYSIWYG editor</div>
|
||||
<div class="step-desc">Works like Google Docs — paragraphs, H2/H3 headings, bold/italic, blockquotes, embedded images. Images inside articles are uploaded via the toolbar and served from Directus assets.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="connector"><div class="connector-line"></div></div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-num" style="background:#00B4D8;color:#000">4</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Switch status to "Published" → Save</div>
|
||||
<div class="step-desc">Top-right corner: change status from <strong>Draft → Published</strong>, then click <strong>Save</strong>.</div>
|
||||
<div class="step-note green">Directus fires a webhook → Next.js regenerates the article page, category page, and homepage. <strong>Live in seconds. No server access needed.</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dir-divider"></div>
|
||||
<div class="dir-section-label">SPECIAL TASKS</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-num" style="background:#7C3AED;color:#fff">★</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Change the homepage hero article</div>
|
||||
<div class="step-desc">Content → <strong>site_settings</strong> (one record only) → click <strong>hero_article</strong> → pick an article from the dropdown → Save. Homepage regenerates automatically.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="connector"><div class="connector-line"></div></div>
|
||||
<div class="step">
|
||||
<div class="step-num" style="background:#FFB300;color:#000">★</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Add a new category</div>
|
||||
<div class="step-desc">Content → <strong>categories</strong> → + New → enter name and slug → Save. Instantly appears in the article category dropdown. Zero code changes.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="connector"><div class="connector-line"></div></div>
|
||||
<div class="step">
|
||||
<div class="step-num" style="background:#D64545;color:#fff">★</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Fix a typo or update a published article</div>
|
||||
<div class="step-desc">Open the article in Directus → edit → Save. The webhook fires again, the page regenerates with corrected content within seconds.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dir-divider"></div>
|
||||
<div class="dir-section-label">FAQ</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<div class="faq-q">What is a "collection"?</div>
|
||||
<div class="faq-a">A collection is like a spreadsheet. <code>articles</code> holds all articles (one row per article), <code>categories</code> holds all categories. You'll mostly work inside <code>articles</code>.</div>
|
||||
</div>
|
||||
<div class="faq-item" style="margin-top:8px">
|
||||
<div class="faq-q">Can I save a draft and come back later?</div>
|
||||
<div class="faq-a">Yes — leave status as <strong>Draft</strong> and save. Nothing appears on the live site until you switch to <strong>Published</strong>.</div>
|
||||
</div>
|
||||
<div class="faq-item" style="margin-top:8px">
|
||||
<div class="faq-q">What about tags (hololive, spring 2026, etc.)?</div>
|
||||
<div class="faq-a">Tags are picked in the article editor — choose from existing ones or type to create new tags inline. They show as clickable chips below the article body and are searchable.</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function switchTab(name, btn) {
|
||||
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||||
document.getElementById('panel-' + name).classList.add('active');
|
||||
btn.classList.add('active');
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,272 @@
|
||||
# Kotobane — CMS-Driven Website Plan
|
||||
|
||||
## Context
|
||||
|
||||
Kotobane is a Japanese pop-culture news site (VTubers, Anime, Manga, Games, Music, Japan, Culture, Industry) with a fully-defined design system. The goal is to build a website where content can be added and updated from a Directus CMS without touching code or redeploying. The user runs everything on a VPS and already has a Directus instance (fresh install, no collections yet). There is a single author (no authors collection needed).
|
||||
|
||||
**Chosen architecture:** Next.js 14 App Router with ISR + on-demand revalidation. Pages are served as pre-built static HTML; when the editor publishes in Directus, a Directus Flow fires a webhook to `/api/revalidate` which surgically regenerates only the affected pages — no rebuild, no redeploy, content live in seconds.
|
||||
|
||||
**Design reference:** `docs/design-reference/kotobane-reference.html` — single file with four tabs: Homepage mockup, Article page mockup, Color system, Directus workflow. Open in a browser for full visual reference.
|
||||
|
||||
**Directus instance:** `https://cms.achmad.dev`
|
||||
|
||||
---
|
||||
|
||||
## Stack
|
||||
|
||||
- **Frontend:** Next.js 14 (App Router), TypeScript, Tailwind CSS
|
||||
- **CMS:** Directus (existing VPS instance)
|
||||
- **Fonts:** Inter (English) + Noto Sans JP (Japanese) via `next/font`
|
||||
- **Icons:** Lucide React
|
||||
- **Rich text rendering:** `@directus/sdk` + custom renderer for Directus rich text blocks
|
||||
- **Image optimization:** Next.js `<Image>` pointing at Directus `/assets/` endpoint
|
||||
|
||||
---
|
||||
|
||||
## Final Color Palette
|
||||
|
||||
### Brand Accent
|
||||
| Token | Hex | Usage |
|
||||
|---|---|---|
|
||||
| accent | `#00B4D8` | Primary buttons, links, active nav, "Read →", focus rings |
|
||||
| accent-hover | `#00D4FF` | Hover state |
|
||||
|
||||
### Status / Badge Colors
|
||||
| Token | Hex | Usage |
|
||||
|---|---|---|
|
||||
| violet | `#7C3AED` | Featured / hero badges, secondary actions |
|
||||
| coral | `#D64545` | Trending, breaking news, error states |
|
||||
| amber | `#FFB300` | New indicator, alerts, warnings |
|
||||
| green | `#00D166` | Published status, success states |
|
||||
|
||||
### Backgrounds
|
||||
| Token | Hex | Usage |
|
||||
|---|---|---|
|
||||
| bg | `#0D0D14` | Main page background |
|
||||
| bg-elevated | `#171A21` | Navbar, sidebars |
|
||||
| bg-card | `#1D212B` | Cards, inputs |
|
||||
| border | `#2A3140` | All borders and dividers |
|
||||
|
||||
### Text
|
||||
| Token | Hex | Usage |
|
||||
|---|---|---|
|
||||
| text-primary | `#F3F5F7` | Headlines, body |
|
||||
| text-secondary | `#B8C0CC` | Excerpts, captions |
|
||||
| text-muted | `#7D8795` | Timestamps, category labels, metadata |
|
||||
| text-disabled | `#5C6470` | Disabled / placeholder |
|
||||
|
||||
> Category names in nav and cards use `text-muted` — no per-category color coding. Colors are reserved for status badges only.
|
||||
|
||||
---
|
||||
|
||||
## Directus Data Model
|
||||
|
||||
Create these collections in Directus admin:
|
||||
|
||||
### `articles`
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| title | string | required |
|
||||
| slug | string | unique, required — used in URL |
|
||||
| status | string | `published` / `draft` — Directus built-in |
|
||||
| content | rich text (WYSIWYG) | full article body |
|
||||
| excerpt | text | short blurb shown on cards |
|
||||
| featured_image | file (M2O → directus_files) | hero image |
|
||||
| published_at | datetime | set on first publish |
|
||||
| is_featured | boolean | controls hero slot on homepage |
|
||||
| seo_title | string | optional override for `<title>` |
|
||||
| seo_description | string | optional override for meta description |
|
||||
| category | M2O → categories | required |
|
||||
| tags | M2M → tags (junction: articles_tags) | optional |
|
||||
|
||||
### `categories`
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| name | string | Anime, VTubers, Manga, Games, Music, Japan, Culture, Industry |
|
||||
| slug | string | unique — used in URL |
|
||||
| description | text | shown on category listing page |
|
||||
|
||||
### `tags`
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| name | string | e.g. "hololive", "Spring 2025" |
|
||||
| slug | string | unique — for URL filtering |
|
||||
|
||||
### `site_settings` (singleton)
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| site_name | string | default: "Kotobane" |
|
||||
| hero_article | M2O → articles | which article appears in the homepage hero |
|
||||
| nav_categories | JSON / M2M → categories | ordered list for navbar |
|
||||
|
||||
---
|
||||
|
||||
## Directus Flow (webhook trigger)
|
||||
|
||||
Create one Flow in Directus:
|
||||
- **Trigger:** "Event Hook" on `articles.items.update` and `articles.items.create` where `status = published`
|
||||
- **Action:** Webhook POST to `https://yourdomain.com/api/revalidate`
|
||||
- **Payload:** `{ "secret": "REVALIDATE_SECRET", "article_id": "{{$trigger.key}}" }`
|
||||
|
||||
The revalidate endpoint receives the article ID, then queries Directus itself to get the full `slug` and `category.slug` — this is more reliable than relying on the Flow payload containing all fields (Directus only sends changed fields, so `category` may be absent if only `status` changed).
|
||||
|
||||
Also add a second Flow trigger on `site_settings` update to revalidate the homepage.
|
||||
|
||||
---
|
||||
|
||||
## Next.js Project Structure
|
||||
|
||||
```
|
||||
kotobane/
|
||||
├── app/
|
||||
│ ├── layout.tsx # Root layout: fonts, navbar, footer
|
||||
│ ├── page.tsx # Homepage (ISR)
|
||||
│ ├── [category]/
|
||||
│ │ ├── page.tsx # Category listing (ISR)
|
||||
│ │ └── [slug]/
|
||||
│ │ └── page.tsx # Article detail (ISR)
|
||||
│ └── api/
|
||||
│ └── revalidate/
|
||||
│ └── route.ts # POST endpoint for Directus webhook
|
||||
├── components/
|
||||
│ ├── layout/
|
||||
│ │ ├── Navbar.tsx # Logo, nav categories, search icon
|
||||
│ │ └── Footer.tsx
|
||||
│ ├── home/
|
||||
│ │ ├── HeroSection.tsx # Featured article hero
|
||||
│ │ └── ArticleGrid.tsx # Latest articles grid
|
||||
│ ├── article/
|
||||
│ │ ├── ArticleCard.tsx # Card used in grids and listings
|
||||
│ │ ├── ArticleBody.tsx # Rich text renderer
|
||||
│ │ └── TagRow.tsx
|
||||
│ └── search/
|
||||
│ └── SearchOverlay.tsx # Cmd+K command palette search
|
||||
├── lib/
|
||||
│ ├── directus.ts # Directus SDK client + typed fetch helpers
|
||||
│ └── types.ts # TypeScript types mirroring Directus collections
|
||||
├── tailwind.config.ts # Design system tokens (colors, spacing, fonts)
|
||||
└── .env.local # DIRECTUS_URL, DIRECTUS_TOKEN, REVALIDATE_SECRET
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pages
|
||||
|
||||
### `/` — Homepage (`app/page.tsx`)
|
||||
- Fetch: `site_settings` (hero article) + latest 12 published articles
|
||||
- Sections: Hero → Latest grid → articles grouped by 2–3 featured categories
|
||||
- ISR: `revalidate = false` (on-demand only, via webhook)
|
||||
|
||||
### `/[category]` — Category listing (`app/[category]/page.tsx`)
|
||||
- Fetch: category by slug + articles filtered by that category, sorted by `published_at` desc
|
||||
- `generateStaticParams`: pre-build all category slugs at deploy time
|
||||
- Pagination: load more button (client-side, hits Directus with `offset`)
|
||||
|
||||
### `/[category]/[slug]` — Article (`app/[category]/[slug]/page.tsx`)
|
||||
- Fetch: single article by slug with `fields=*,tags.*,category.*`
|
||||
- Sections: Hero image → title → metadata → rich text body → tags → related articles (same category, limit 4)
|
||||
- `generateMetadata`: uses `seo_title` / `seo_description` if set, falls back to article title/excerpt
|
||||
- `generateStaticParams`: pre-build all published articles at deploy time; `dynamicParams = true` (Next.js default) ensures new articles published after deploy are generated on first request via ISR — no rebuild needed
|
||||
|
||||
### `/api/revalidate` — Webhook handler (`app/api/revalidate/route.ts`)
|
||||
- Accepts POST with `{ secret, slug, category_slug }`
|
||||
- Validates secret matches `REVALIDATE_SECRET` env var
|
||||
- Calls `revalidatePath('/')`, `revalidatePath('/[category]', 'page')`, `revalidatePath('/[category]/[slug]', 'page')`
|
||||
|
||||
### Search — `SearchOverlay` (global, client component)
|
||||
- Triggered by Cmd+K or clicking the search icon in Navbar
|
||||
- Debounced input (300ms) → live GET to Directus `/items/articles?search=query&limit=8&fields=title,slug,category.slug`
|
||||
- Results rendered as keyboard-navigable list (↑↓ Enter)
|
||||
- No ISR needed — always queries Directus live
|
||||
|
||||
---
|
||||
|
||||
## Design System Integration (`tailwind.config.ts`)
|
||||
|
||||
Map the design system tokens directly into Tailwind:
|
||||
|
||||
```ts
|
||||
colors: {
|
||||
bg: { DEFAULT: '#0D0D14', elevated: '#171A21', card: '#1D212B' },
|
||||
border: { DEFAULT: '#2A3140' },
|
||||
accent: { DEFAULT: '#00B4D8', hover: '#00D4FF' },
|
||||
violet: '#7C3AED',
|
||||
coral: '#D64545',
|
||||
amber: '#FFB300',
|
||||
green: '#00D166',
|
||||
text: { primary: '#F3F5F7', secondary: '#B8C0CC', muted: '#7D8795', disabled: '#5C6470' },
|
||||
},
|
||||
borderRadius: {
|
||||
sm: '8px', md: '12px', lg: '16px', xl: '20px', '2xl': '28px',
|
||||
},
|
||||
```
|
||||
|
||||
Standard Tailwind spacing (multiples of 4/8) maps cleanly to the 8px spacing system.
|
||||
|
||||
---
|
||||
|
||||
## Directus SDK Client (`lib/directus.ts`)
|
||||
|
||||
Use `@directus/sdk` with a typed schema. Key helpers:
|
||||
|
||||
```ts
|
||||
// Singleton client
|
||||
const directus = createDirectus(DIRECTUS_URL).with(rest())
|
||||
|
||||
// Typed fetch helpers
|
||||
getArticles(options) // list with filters
|
||||
getArticleBySlug(slug) // single article
|
||||
getCategoryBySlug(slug) // single category
|
||||
getAllCategories() // for navbar + generateStaticParams
|
||||
getSiteSettings() // homepage hero + nav order
|
||||
searchArticles(query) // search overlay
|
||||
```
|
||||
|
||||
All helpers use `readItems` / `readSingleton` from the SDK. Token auth via `staticToken(DIRECTUS_TOKEN)` — create a read-only token in Directus for the frontend.
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```
|
||||
DIRECTUS_URL=https://cms.achmad.dev
|
||||
DIRECTUS_TOKEN=<read-only static token from Directus>
|
||||
REVALIDATE_SECRET=<random string, shared with Directus Flow>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment (VPS)
|
||||
|
||||
- Run Next.js as a Node server: `next build && next start -p 3000`
|
||||
- Use PM2 or systemd to keep it running
|
||||
- Nginx reverse proxy in front (handles SSL, proxies to port 3000)
|
||||
- Directus already running on same VPS (separate port/domain)
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. **Bootstrap** — `create-next-app`, install deps (`@directus/sdk`, `lucide-react`), configure Tailwind with design tokens, set up fonts
|
||||
2. **Directus collections** — create all 4 collections + junction table in Directus admin, add a few test articles
|
||||
3. **SDK client** — `lib/directus.ts` with typed helpers + `lib/types.ts`
|
||||
4. **Layout** — `Navbar` + `Footer`, root `layout.tsx`
|
||||
5. **Homepage** — `HeroSection` + `ArticleGrid` + `page.tsx`
|
||||
6. **Article page** — `ArticleCard`, `ArticleBody` (rich text renderer), full article page
|
||||
7. **Category page** — listing with pagination
|
||||
8. **Search overlay** — `SearchOverlay` component wired to Cmd+K
|
||||
9. **Revalidate endpoint** — `/api/revalidate` + test with curl
|
||||
10. **Directus Flow** — set up webhook trigger in Directus
|
||||
11. **SEO** — `generateMetadata` on article + category pages, `sitemap.ts`, `robots.ts`
|
||||
12. **Deploy** — build, PM2/systemd, Nginx config
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
- Add a test article in Directus → confirm it appears on homepage within seconds (no restart)
|
||||
- Update an article → confirm revalidate endpoint fires and page updates
|
||||
- Test search overlay with Cmd+K
|
||||
- Check mobile layout (single-column per design system)
|
||||
- Confirm Directus going offline does not break cached pages for visitors
|
||||
- Lighthouse score: aim for 90+ performance (static HTML + lazy images)
|
||||
@@ -0,0 +1,263 @@
|
||||
# Kotobane — Brand & Design System
|
||||
|
||||
> "Words carried on wings." — 言羽
|
||||
|
||||
Kotobane is a Japanese pop-culture news site covering VTubers, Anime, Manga, Games, Music, Japan, Culture, and Industry.
|
||||
|
||||
---
|
||||
|
||||
## Brand Identity
|
||||
|
||||
**What it should feel like:**
|
||||
- A modern Japanese digital newspaper
|
||||
- A refined anime culture publication
|
||||
- A calm but fast-moving stream of information
|
||||
|
||||
**What it must never feel like:**
|
||||
- A loud clickbait anime blog
|
||||
- A generic SaaS dashboard
|
||||
- An overly playful otaku forum
|
||||
|
||||
**Emotional keywords:** intelligent · fast · atmospheric · minimal · editorial · futuristic · trustworthy
|
||||
|
||||
---
|
||||
|
||||
## Color Palette
|
||||
|
||||
### Brand Accent
|
||||
| Token | Hex | Usage |
|
||||
|---|---|---|
|
||||
| accent | `#00B4D8` | Buttons, links, active nav, "Read →", focus rings |
|
||||
| accent-hover | `#00D4FF` | Hover state |
|
||||
|
||||
### Status Colors
|
||||
| Token | Hex | Usage |
|
||||
|---|---|---|
|
||||
| violet | `#7C3AED` | Featured / hero badges, secondary actions |
|
||||
| coral | `#D64545` | Trending, breaking news, errors |
|
||||
| amber | `#FFB300` | New indicator, alerts, warnings |
|
||||
| green | `#00D166` | Published status, success |
|
||||
|
||||
### Backgrounds
|
||||
| Token | Hex | Usage |
|
||||
|---|---|---|
|
||||
| bg | `#0D0D14` | Main page background |
|
||||
| bg-elevated | `#171A21` | Navbar, elevated surfaces |
|
||||
| bg-card | `#1D212B` | Cards, inputs |
|
||||
| border | `#2A3140` | All borders and dividers |
|
||||
|
||||
### Text
|
||||
| Token | Hex | Usage |
|
||||
|---|---|---|
|
||||
| text-primary | `#F3F5F7` | Headlines, body |
|
||||
| text-secondary | `#B8C0CC` | Excerpts, captions |
|
||||
| text-muted | `#7D8795` | Timestamps, category labels, metadata |
|
||||
| text-disabled | `#5C6470` | Disabled / placeholder |
|
||||
|
||||
**Rules:**
|
||||
- Category names are always `text-muted` — never color-coded
|
||||
- No gradients on UI elements — flat colors only
|
||||
- Secondary accent colors are used sparingly, only for semantic meaning (trending, featured, etc.)
|
||||
|
||||
---
|
||||
|
||||
## Typography
|
||||
|
||||
**Font stack:**
|
||||
- English: **Inter**
|
||||
- Japanese: **Noto Sans JP**
|
||||
|
||||
Load both via `next/font`. Use Noto Sans JP as a fallback for any Japanese text in article titles or tags.
|
||||
|
||||
### Scale
|
||||
| Role | Size | Weight |
|
||||
|---|---|---|
|
||||
| Hero headline | 48–56px | 700 |
|
||||
| Article title | 34–40px | 700 |
|
||||
| Section title | 22–24px | 700 |
|
||||
| Card title | 13–15px | 600 |
|
||||
| Primary body | 16px | 400 |
|
||||
| Compact body (excerpts) | 13px | 400 |
|
||||
| Metadata / labels | 10–12px | 500–600 |
|
||||
|
||||
---
|
||||
|
||||
## Spacing
|
||||
|
||||
Base unit: **8px**. Only use these values — no arbitrary spacing.
|
||||
|
||||
`4 · 8 · 12 · 16 · 24 · 32 · 40 · 48 · 64 · 80 · 96`
|
||||
|
||||
---
|
||||
|
||||
## Layout
|
||||
|
||||
| Breakpoint | Grid | Max content width |
|
||||
|---|---|---|
|
||||
| Desktop | 12-column | 1200px |
|
||||
| Tablet | 8-column | — |
|
||||
| Mobile | Single column, content-first | — |
|
||||
|
||||
Article body width: **760–780px** (centered).
|
||||
|
||||
---
|
||||
|
||||
## Border Radius
|
||||
|
||||
| Token | Value | Usage |
|
||||
|---|---|---|
|
||||
| radius-sm | 8px | Inputs, small chips |
|
||||
| radius-md | 12px | Cards |
|
||||
| radius-lg | 16px | Large cards, hero panels |
|
||||
| radius-xl | 20px | Modals |
|
||||
| radius-2xl | 28px | Full-page panels |
|
||||
|
||||
---
|
||||
|
||||
## Shadows
|
||||
|
||||
Shadows must be soft and diffused. No hard black shadows.
|
||||
|
||||
| Token | Usage |
|
||||
|---|---|
|
||||
| shadow-sm | Subtle card lift |
|
||||
| shadow-md | Hover elevation |
|
||||
| shadow-lg | Modals, overlays |
|
||||
|
||||
---
|
||||
|
||||
## Motion
|
||||
|
||||
| Property | Value |
|
||||
|---|---|
|
||||
| Duration | 150ms – 250ms |
|
||||
| Easing | `cubic-bezier(0.22, 1, 0.36, 1)` |
|
||||
|
||||
**Rules:**
|
||||
- Motion communicates hierarchy and guides focus — never decorates
|
||||
- No bouncing, no exaggerated scaling, no neon pulse effects
|
||||
- Always respect `prefers-reduced-motion`
|
||||
|
||||
---
|
||||
|
||||
## Components
|
||||
|
||||
### Buttons
|
||||
- **Primary:** solid `#00B4D8` background, black text
|
||||
- **Secondary:** transparent background, `#00B4D8` border and text
|
||||
- Never: pill buttons, excessive gradients, loud CTA colors
|
||||
|
||||
### Cards
|
||||
- Background: `bg-card`
|
||||
- Border: 1px solid `border`
|
||||
- Radius: `radius-md` (12px)
|
||||
- Hover: border lightens slightly (`#3a4560`), no scale transform
|
||||
|
||||
### Badges
|
||||
- Use flat solid backgrounds, bold uppercase text, tight padding
|
||||
- Text on dark badges: white. Text on light badges (amber, green): black
|
||||
|
||||
---
|
||||
|
||||
## Icons
|
||||
|
||||
Style: **outline, geometric, lightweight**
|
||||
|
||||
Recommended library: **Lucide React**
|
||||
|
||||
---
|
||||
|
||||
## Article Pages
|
||||
|
||||
Article pages are the most important pages. They must feel:
|
||||
- Quiet and focused
|
||||
- Distraction-free
|
||||
- Premium and readable
|
||||
|
||||
Avoid: intrusive widgets, autoplay media, excessive sidebar clutter.
|
||||
|
||||
---
|
||||
|
||||
## Editorial Tone
|
||||
|
||||
Articles should be:
|
||||
- Concise and informative
|
||||
- Culturally aware and contextual
|
||||
- Written in a calm, analytical voice — modern magazine journalism
|
||||
|
||||
**Good headlines:** direct, informative, contextual
|
||||
**Bad headlines:** "YOU WON'T BELIEVE", "THIS CHANGES EVERYTHING", fake urgency
|
||||
|
||||
Never: clickbait, ragebait, excessive hype.
|
||||
|
||||
---
|
||||
|
||||
## Accessibility
|
||||
|
||||
Mandatory requirements:
|
||||
- WCAG AA contrast minimum on all text
|
||||
- Keyboard navigation throughout
|
||||
- Visible focus states (use `ring-accent` / `#00B4D8`)
|
||||
- Semantic HTML with proper heading hierarchy (one `<h1>` per page)
|
||||
- `alt` text on all images
|
||||
- `prefers-reduced-motion` support
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
|
||||
- SSR / ISR — no client-heavy rendering for core content
|
||||
- Lazy-load all images below the fold
|
||||
- `next/image` for all images (automatic WebP + sizing)
|
||||
- Minimal layout shift (CLS < 0.1)
|
||||
- Fonts loaded via `next/font` (no FOUT)
|
||||
|
||||
---
|
||||
|
||||
## SEO
|
||||
|
||||
Priority order: **readers → archives → search engines**
|
||||
|
||||
SEO must never dictate editorial tone. Every article and category page gets:
|
||||
- `<title>` — `seo_title` if set, otherwise article title + " — Kotobane"
|
||||
- `<meta description>` — `seo_description` if set, otherwise excerpt
|
||||
- `<link rel="canonical">`
|
||||
- Open Graph tags for social sharing
|
||||
|
||||
---
|
||||
|
||||
## AI Agent Rules
|
||||
|
||||
When modifying any UI in this project:
|
||||
|
||||
**Must:**
|
||||
- Maintain spacing system (8px grid, no arbitrary values)
|
||||
- Preserve typography hierarchy
|
||||
- Use flat colors only — no gradients on UI elements
|
||||
- Use accent colors sparingly and only for their defined semantic purpose
|
||||
- Keep category labels in `text-muted` — never color-code them
|
||||
|
||||
**Must not:**
|
||||
- Inject gradients onto UI elements
|
||||
- Overcrowd layouts
|
||||
- Add clickbait-style visual treatments
|
||||
- Break the spacing system
|
||||
- Add unnecessary UI chrome or decorations
|
||||
- Add per-category color coding
|
||||
|
||||
---
|
||||
|
||||
## Logo
|
||||
|
||||
The 言羽 kanji mark serves as the primary logo in all contexts.
|
||||
|
||||
Characteristics:
|
||||
- Rendered in `accent` color (`#00B4D8`) on dark backgrounds
|
||||
- High letter-spacing in the wordmark (tracking: 3px+)
|
||||
- Works at small sizes — suitable as favicon treatment
|
||||
- No gradients on the logo
|
||||
|
||||
---
|
||||
|
||||
*Design reference with visual mockups: `docs/design-reference/kotobane-reference.html`*
|
||||
*Implementation spec: `docs/superpowers/specs/2026-05-28-kotobane-cms-website-design.md`*
|
||||
Reference in New Issue
Block a user