Comics — Series Page (synopsis · chapters list)
A comic-book series detail page for the fictional Neon Ronin, art-directed with thick ink borders, halftone texture, and Bangers display lettering. A hero banner pairs gradient cover art and a SLASH SFX with status, rating, age and genre badges, a clamped synopsis with a read-more toggle, and a stats row for chapters, views, likes and read progress. Start-reading and add-to-library CTAs sit above a chapter list with halftone thumbnails, dates and a read/unread state; sorting flips newest or oldest and marking chapters read fills a live progress bar.
MCP
Código
:root {
--ink: #0e0e12;
--ink-2: #23232b;
--paper: #fdfcf7;
--panel: #ffffff;
--accent: #ff2e4d;
--accent-2: #ffd23f;
--accent-blue: #2e6bff;
--muted: #6b6b78;
--line: rgba(14, 14, 18, 0.14);
--line-2: rgba(14, 14, 18, 0.28);
--halftone: radial-gradient(circle, rgba(14, 14, 18, 0.18) 1px, transparent 1.6px);
--r-sm: 6px;
--r-md: 12px;
--r-lg: 18px;
--shadow: 4px 4px 0 var(--ink);
--shadow-sm: 2px 2px 0 var(--ink);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: "Inter", system-ui, sans-serif;
line-height: 1.5;
color: var(--ink);
background-color: var(--paper);
background-image: var(--halftone);
background-size: 6px 6px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a { color: inherit; }
/* ---------- Topbar ---------- */
.topbar {
position: sticky;
top: 0;
z-index: 30;
display: flex;
align-items: center;
gap: 1rem;
padding: 0.7rem clamp(1rem, 4vw, 2.5rem);
background: var(--panel);
border-bottom: 3px solid var(--ink);
}
.brand {
display: inline-flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
font-family: "Bangers", cursive;
font-size: 1.7rem;
letter-spacing: 0.04em;
}
.brand__mark {
display: grid;
place-items: center;
width: 1.7rem;
height: 1.7rem;
background: var(--accent);
color: var(--paper);
border: 2px solid var(--ink);
border-radius: var(--r-sm);
transform: rotate(-6deg);
box-shadow: var(--shadow-sm);
}
.topbar__nav {
display: flex;
gap: 1.25rem;
margin-left: 0.5rem;
}
.topbar__nav a {
text-decoration: none;
font-weight: 600;
font-size: 0.95rem;
padding: 0.2rem 0;
border-bottom: 2px solid transparent;
}
.topbar__nav a:hover { border-bottom-color: var(--accent); }
.topbar .btn { margin-left: auto; }
/* ---------- Buttons ---------- */
.btn {
font-family: "Inter", sans-serif;
font-weight: 700;
font-size: 0.95rem;
cursor: pointer;
border: 2px solid var(--ink);
border-radius: var(--r-md);
padding: 0.6rem 1.1rem;
background: var(--panel);
color: var(--ink);
box-shadow: var(--shadow-sm);
transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.15s ease;
}
.btn:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--ink); }
.btn:active { transform: translate(2px, 2px); box-shadow: 0 0 0 var(--ink); }
.btn:focus-visible { outline: 3px solid var(--accent-blue); outline-offset: 2px; }
.btn--sm { padding: 0.4rem 0.7rem; font-size: 0.85rem; }
.btn--primary { background: var(--accent); color: var(--paper); }
.btn--accent { background: var(--accent-2); }
.btn--ghost { background: var(--panel); }
.btn--accent[aria-pressed="true"] {
background: var(--ink);
color: var(--accent-2);
}
/* ---------- Page ---------- */
.page {
max-width: 1080px;
margin: 0 auto;
padding: clamp(1rem, 4vw, 2.5rem);
}
/* ---------- Hero ---------- */
.hero {
display: grid;
grid-template-columns: minmax(0, 280px) 1fr;
gap: clamp(1.25rem, 4vw, 2.5rem);
background: var(--panel);
border: 3px solid var(--ink);
border-radius: var(--r-lg);
box-shadow: var(--shadow);
padding: clamp(1.25rem, 4vw, 2rem);
}
.hero__art {
position: relative;
}
.hero__sfx {
position: absolute;
top: -0.7rem;
right: -0.7rem;
z-index: 2;
font-family: "Bangers", cursive;
font-size: 1.9rem;
letter-spacing: 0.03em;
color: var(--accent-2);
background: var(--accent);
-webkit-text-stroke: 1px var(--ink);
padding: 0.1rem 0.55rem;
border: 2.5px solid var(--ink);
border-radius: var(--r-sm);
transform: rotate(8deg);
box-shadow: var(--shadow-sm);
}
.hero__cover {
position: relative;
aspect-ratio: 3 / 4;
border: 3px solid var(--ink);
border-radius: var(--r-md);
overflow: hidden;
background:
var(--halftone),
linear-gradient(135deg, #1b1140 0%, #4d1d6b 45%, #ff2e4d 100%);
background-size: 7px 7px, cover;
display: grid;
place-items: center;
box-shadow: var(--shadow);
}
.cover__kanji {
position: absolute;
font-size: 12rem;
line-height: 1;
color: rgba(255, 210, 63, 0.16);
font-weight: 700;
}
.cover__title {
position: relative;
font-family: "Bangers", cursive;
font-size: clamp(2.2rem, 6vw, 3.1rem);
text-align: center;
line-height: 0.92;
color: var(--accent-2);
-webkit-text-stroke: 2px var(--ink);
letter-spacing: 0.02em;
}
.hero__info { min-width: 0; }
.hero__meta {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 0.85rem;
}
.badge {
font-size: 0.8rem;
font-weight: 700;
padding: 0.25rem 0.6rem;
border: 2px solid var(--ink);
border-radius: 999px;
background: var(--panel);
}
.badge--status { color: #1a7f37; }
.badge--rating { background: var(--accent-2); }
.badge--age { color: var(--muted); }
.hero__title {
font-family: "Bangers", cursive;
font-size: clamp(2.4rem, 7vw, 3.6rem);
line-height: 0.95;
margin: 0 0 0.25rem;
letter-spacing: 0.02em;
}
.hero__author {
margin: 0 0 0.85rem;
color: var(--ink-2);
font-size: 0.95rem;
}
.tags {
list-style: none;
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
margin: 0 0 1rem;
padding: 0;
}
.tag {
font-size: 0.8rem;
font-weight: 600;
padding: 0.2rem 0.6rem;
border: 2px solid var(--ink);
border-radius: var(--r-sm);
background: var(--paper);
}
.synopsis { margin-bottom: 1rem; }
.synopsis__text {
margin: 0;
color: var(--ink-2);
}
.synopsis__text[data-collapsed="true"] {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.readmore {
margin-top: 0.4rem;
background: none;
border: none;
padding: 0.1rem 0;
font: inherit;
font-weight: 700;
color: var(--accent);
cursor: pointer;
}
.readmore:hover { text-decoration: underline; }
.readmore:focus-visible { outline: 2px solid var(--accent-blue); outline-offset: 2px; }
.stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.6rem;
margin: 0 0 1.1rem;
}
.stat {
border: 2px solid var(--ink);
border-radius: var(--r-md);
padding: 0.55rem 0.6rem;
background: var(--paper);
text-align: center;
}
.stat dt {
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--muted);
font-weight: 700;
}
.stat dd {
margin: 0.1rem 0 0;
font-size: 1.2rem;
font-weight: 800;
}
.stat--progress { background: var(--accent-2); }
.cta {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
}
/* ---------- Chapters ---------- */
.chapters { margin-top: clamp(1.5rem, 5vw, 2.5rem); }
.chapters__head {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
margin-bottom: 1rem;
}
.section-title {
font-family: "Bangers", cursive;
font-size: 2.2rem;
letter-spacing: 0.03em;
margin: 0;
}
.chapters__controls {
display: flex;
align-items: center;
gap: 0.8rem;
}
.progress {
display: flex;
align-items: center;
gap: 0.5rem;
}
.progress__bar {
width: 120px;
height: 12px;
border: 2px solid var(--ink);
border-radius: 999px;
background: var(--panel);
overflow: hidden;
}
.progress__bar span {
display: block;
height: 100%;
background: var(--accent);
transition: width 0.35s ease;
}
.progress__label {
font-size: 0.85rem;
font-weight: 700;
min-width: 2.5em;
}
.chapter-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.7rem;
}
.chapter {
display: grid;
grid-template-columns: 72px 1fr auto;
align-items: center;
gap: 0.9rem;
width: 100%;
text-align: left;
font: inherit;
cursor: pointer;
background: var(--panel);
border: 2.5px solid var(--ink);
border-radius: var(--r-md);
padding: 0.6rem 0.9rem 0.6rem 0.6rem;
box-shadow: var(--shadow-sm);
transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.15s ease;
}
.chapter:hover { transform: translate(-1px, -1px); box-shadow: 4px 4px 0 var(--ink); }
.chapter:active { transform: translate(1px, 1px); box-shadow: 1px 1px 0 var(--ink); }
.chapter:focus-visible { outline: 3px solid var(--accent-blue); outline-offset: 2px; }
.chapter[data-read="true"] { background: #f1f0ea; }
.chapter[data-read="true"] .chapter__title { color: var(--muted); }
.chapter__thumb {
position: relative;
width: 72px;
height: 56px;
border: 2px solid var(--ink);
border-radius: var(--r-sm);
overflow: hidden;
background-size: 6px 6px, cover;
display: grid;
place-items: center;
color: var(--paper);
font-family: "Bangers", cursive;
font-size: 1.4rem;
-webkit-text-stroke: 1px var(--ink);
}
.chapter__body { min-width: 0; display: flex; flex-direction: column; }
.chapter__kicker {
display: block;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--accent);
}
.chapter__title {
display: block;
font-weight: 700;
font-size: 1.02rem;
margin: 0.1rem 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.chapter__date {
font-size: 0.82rem;
color: var(--muted);
}
.chapter__state {
justify-self: end;
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-size: 0.78rem;
font-weight: 700;
padding: 0.25rem 0.55rem;
border: 2px solid var(--ink);
border-radius: 999px;
background: var(--paper);
white-space: nowrap;
}
.chapter[data-read="true"] .chapter__state {
background: #1a7f37;
color: var(--paper);
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 1.5rem;
transform: translate(-50%, 1.5rem);
z-index: 50;
background: var(--ink);
color: var(--paper);
font-weight: 700;
padding: 0.7rem 1.1rem;
border: 2.5px solid var(--accent-2);
border-radius: var(--r-md);
box-shadow: var(--shadow);
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease, transform 0.2s ease;
}
.toast.show {
opacity: 1;
transform: translate(-50%, 0);
}
@media (prefers-reduced-motion: reduce) {
.btn, .chapter, .toast, .progress__bar span { transition: none; }
}
/* ---------- Responsive ---------- */
@media (max-width: 720px) {
.hero { grid-template-columns: 1fr; }
.hero__art { max-width: 240px; }
}
@media (max-width: 520px) {
.topbar__nav { display: none; }
.stats { grid-template-columns: repeat(2, 1fr); }
.cta .btn { flex: 1 1 auto; }
.chapters__head { flex-direction: column; align-items: flex-start; }
.chapters__controls { width: 100%; justify-content: space-between; }
.chapter { grid-template-columns: 56px 1fr; }
.chapter__thumb { width: 56px; height: 48px; font-size: 1.1rem; }
.chapter__state { grid-column: 2; justify-self: start; margin-top: 0.2rem; }
.progress__bar { width: 90px; }
}(function () {
"use strict";
// ---------- In-memory data ----------
var chapters = [
{ n: 1, title: "Severance Chip", date: "2025-09-02", grad: "linear-gradient(135deg,#1b1140,#ff2e4d)", read: false },
{ n: 2, title: "Thirteen Districts", date: "2025-09-16", grad: "linear-gradient(135deg,#0d2b4d,#2e6bff)", read: false },
{ n: 3, title: "Firewall Blade", date: "2025-10-01", grad: "linear-gradient(135deg,#3a0f3f,#ffd23f)", read: false },
{ n: 4, title: "The Helix Debt", date: "2025-10-19", grad: "linear-gradient(135deg,#14233b,#ff2e4d)", read: false },
{ n: 5, title: "Rewritten Memory", date: "2025-11-04", grad: "linear-gradient(135deg,#2a0d2e,#2e6bff)", read: false },
{ n: 6, title: "Loyalty Subscription", date: "2025-11-23", grad: "linear-gradient(135deg,#10283f,#ffd23f)", read: false },
{ n: 7, title: "A Clean Death", date: "2025-12-08", grad: "linear-gradient(135deg,#1b1140,#ff2e4d)", read: false }
];
var sortNewest = true;
// ---------- Elements ----------
var listEl = document.getElementById("chapterList");
var sortBtn = document.getElementById("sortBtn");
var libBtn = document.getElementById("libBtn");
var startBtn = document.getElementById("startBtn");
var topReadBtn = document.getElementById("topReadBtn");
var readMoreBtn = document.getElementById("readMore");
var synopsis = document.getElementById("synopsis");
var progressFill = document.getElementById("progressFill");
var progressLabel = document.getElementById("progressLabel");
var statRead = document.getElementById("statRead");
var statChapters = document.getElementById("statChapters");
var statTotal = document.getElementById("statTotal");
var toastEl = document.getElementById("toast");
statChapters.textContent = chapters.length;
statTotal.textContent = chapters.length;
// ---------- Toast helper ----------
var toastTimer = null;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2200);
}
function fmtDate(iso) {
var d = new Date(iso + "T00:00:00");
return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
}
// ---------- Render ----------
function render() {
var ordered = chapters.slice().sort(function (a, b) {
return sortNewest ? b.n - a.n : a.n - b.n;
});
listEl.innerHTML = "";
ordered.forEach(function (ch) {
var li = document.createElement("li");
var btn = document.createElement("button");
btn.type = "button";
btn.className = "chapter";
btn.dataset.n = String(ch.n);
btn.dataset.read = String(ch.read);
btn.setAttribute("aria-pressed", String(ch.read));
var thumb = document.createElement("span");
thumb.className = "chapter__thumb";
thumb.setAttribute("aria-hidden", "true");
thumb.style.backgroundImage = "var(--halftone)," + ch.grad;
thumb.textContent = "#" + ch.n;
var body = document.createElement("span");
body.className = "chapter__body";
var kicker = document.createElement("span");
kicker.className = "chapter__kicker";
kicker.textContent = "Chapter " + ch.n;
var title = document.createElement("span");
title.className = "chapter__title";
title.textContent = ch.title;
var date = document.createElement("span");
date.className = "chapter__date";
date.textContent = fmtDate(ch.date);
body.appendChild(kicker);
body.appendChild(title);
body.appendChild(date);
var state = document.createElement("span");
state.className = "chapter__state";
state.textContent = ch.read ? "✓ Read" : "Unread";
btn.appendChild(thumb);
btn.appendChild(body);
btn.appendChild(state);
btn.setAttribute("aria-label",
"Chapter " + ch.n + ", " + ch.title + ", " + (ch.read ? "read" : "unread"));
btn.addEventListener("click", function () {
markRead(ch.n);
});
li.appendChild(btn);
listEl.appendChild(li);
});
updateProgress();
}
function updateProgress() {
var readCount = chapters.filter(function (c) { return c.read; }).length;
var pct = Math.round((readCount / chapters.length) * 100);
progressFill.style.width = pct + "%";
progressLabel.textContent = pct + "%";
statRead.textContent = readCount;
}
// ---------- Interactions ----------
function markRead(n) {
var ch = chapters.find(function (c) { return c.n === n; });
if (!ch) return;
if (ch.read) {
toast("Already read — Chapter " + n);
updateProgress();
return;
}
ch.read = true;
render();
toast("Marked Chapter " + n + " as read");
}
sortBtn.addEventListener("click", function () {
sortNewest = !sortNewest;
sortBtn.textContent = sortNewest ? "↕ Newest" : "↕ Oldest";
sortBtn.setAttribute("aria-label",
"Sort chapters, currently " + (sortNewest ? "newest" : "oldest") + " first");
render();
toast("Sorted " + (sortNewest ? "newest first" : "oldest first"));
});
var inLibrary = false;
libBtn.addEventListener("click", function () {
inLibrary = !inLibrary;
libBtn.setAttribute("aria-pressed", String(inLibrary));
libBtn.textContent = inLibrary ? "✓ In library" : "+ Add to library";
toast(inLibrary ? "Added Neon Ronin to your library" : "Removed from library");
});
function continueReading() {
// first unread by chapter number
var next = chapters.slice().sort(function (a, b) { return a.n - b.n; })
.find(function (c) { return !c.read; });
if (!next) {
toast("You're all caught up!");
return;
}
markRead(next.n);
}
startBtn.addEventListener("click", continueReading);
topReadBtn.addEventListener("click", continueReading);
readMoreBtn.addEventListener("click", function () {
var collapsed = synopsis.dataset.collapsed === "true";
synopsis.dataset.collapsed = String(!collapsed);
readMoreBtn.setAttribute("aria-expanded", String(collapsed));
readMoreBtn.textContent = collapsed ? "Read less ▴" : "Read more ▾";
});
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Neon Ronin — Series</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Bangers&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header class="topbar">
<a class="brand" href="#" aria-label="InkPulse home">
<span class="brand__mark" aria-hidden="true">!</span>
<span class="brand__name">InkPulse</span>
</a>
<nav class="topbar__nav" aria-label="Primary">
<a href="#">Browse</a>
<a href="#">Genres</a>
<a href="#">Trending</a>
</nav>
<button class="btn btn--ghost" type="button" id="topReadBtn">Continue</button>
</header>
<main class="page">
<!-- HERO -->
<section class="hero" aria-labelledby="seriesTitle">
<div class="hero__art" role="img" aria-label="Cover art for Neon Ronin">
<span class="hero__sfx" aria-hidden="true">SLASH!</span>
<div class="hero__cover">
<span class="cover__kanji" aria-hidden="true">侍</span>
<span class="cover__title">NEON<br>RONIN</span>
</div>
</div>
<div class="hero__info">
<div class="hero__meta">
<span class="badge badge--status">● Ongoing</span>
<span class="badge badge--rating" aria-label="Rating 4.8 of 5">★ 4.8</span>
<span class="badge badge--age">Teen 13+</span>
</div>
<h1 class="hero__title" id="seriesTitle">Neon Ronin</h1>
<p class="hero__author">Story & Art by <strong>Mika Tendo</strong> · Letters by R. Okafor</p>
<ul class="tags" aria-label="Genres">
<li class="tag">Action</li>
<li class="tag">Cyberpunk</li>
<li class="tag">Sci-Fi</li>
<li class="tag">Drama</li>
</ul>
<div class="synopsis">
<p class="synopsis__text" id="synopsis" data-collapsed="true">
In the rain-soaked sprawl of <strong>Neo-Kanazawa</strong>, the last free swordsmith forges blades that cut through firewalls as easily as flesh. When the megacorp <em>Helix Dynamics</em> brands the wandering mercenary <strong>Kaze</strong> a domestic threat, she trades her severance chip for a stolen katana and a debt she can never repay. Hunted across thirteen districts, she discovers the corporation's deepest secret is wired straight into her own spine — and that every duel she wins rewrites a memory she didn't know she'd lost. Loyalty is a subscription, honor is contraband, and the only thing the city can't pirate is a clean death.
</p>
<button class="readmore" type="button" id="readMore" aria-expanded="false" aria-controls="synopsis">Read more ▾</button>
</div>
<dl class="stats" aria-label="Series statistics">
<div class="stat">
<dt>Chapters</dt>
<dd id="statChapters">7</dd>
</div>
<div class="stat">
<dt>Views</dt>
<dd>4.2M</dd>
</div>
<div class="stat">
<dt>Likes</dt>
<dd>318K</dd>
</div>
<div class="stat stat--progress">
<dt>Read</dt>
<dd><span id="statRead">0</span>/<span id="statTotal">7</span></dd>
</div>
</dl>
<div class="cta">
<button class="btn btn--primary" type="button" id="startBtn">▶ Start reading</button>
<button class="btn btn--accent" type="button" id="libBtn" aria-pressed="false">+ Add to library</button>
</div>
</div>
</section>
<!-- CHAPTERS -->
<section class="chapters" aria-labelledby="chaptersHeading">
<div class="chapters__head">
<h2 class="section-title" id="chaptersHeading">Chapters</h2>
<div class="chapters__controls">
<div class="progress" aria-hidden="true">
<div class="progress__bar"><span id="progressFill" style="width:0%"></span></div>
<span class="progress__label" id="progressLabel">0%</span>
</div>
<button class="btn btn--ghost btn--sm" type="button" id="sortBtn" aria-label="Sort chapters, currently newest first">↕ Newest</button>
</div>
</div>
<ul class="chapter-list" id="chapterList" aria-label="Chapter list"></ul>
</section>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Series Page (synopsis · chapters list)
A full series detail page for the fictional cyberpunk title Neon Ronin, built in a bold comic-book idiom: hard ink panel borders, Ben-Day halftone dots over the paper background, drop-shadowed cards, and oversized Bangers lettering for the title and a tilted SLASH! sound effect. The hero banner pairs a gradient cover panel with a status pill (Ongoing), a star rating, an age badge, genre tags, the author credit, and a four-cell stats row covering chapters, views, likes, and read progress.
The synopsis is clamped to three lines with a Read more / Read less toggle that updates its aria-expanded state. Two primary CTAs sit beneath it — Start reading jumps to the first unread chapter and marks it read, while Add to library is a pressable toggle. The chapters list renders from an in-memory array as keyboard-usable buttons, each with a halftone gradient thumbnail, kicker, title, formatted date, and a read/unread badge.
Clicking a chapter marks it read (persisted in memory for the session), turns its badge green, and advances a live progress bar and the Read stat. A sort control flips the list between newest-first and oldest-first, the Continue button in the header resumes from the first unread chapter, and every action surfaces a small toast for feedback. The layout reflows to a single column and a two-up stats grid below ~520px.
Illustrative UI only — fictional series, characters, and data.