Comics — Classic Panel Grid Page
A print-style single comic page for the fictional series Neon Ronin, art-directed in the classic panel-grid tradition — a full-width establishing splash, two half-panels, and a three-up action strip, all set in thick ink borders with gutters and Ben-Day halftone fills. Speech, narration, and shout balloons carry tails, while bold Bangers SFX letter the action. A preset switcher reflows the grid between Classic, Splash, Strip, and Mosaic, and a reading-order toggle numbers every panel and walks a guided highlight from one through N with live toast feedback.
MCP
程式碼
: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;
--ink-border: 3px solid var(--ink);
--gutter: 14px;
--shadow: 6px 6px 0 var(--ink);
}
* {
box-sizing: border-box;
}
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
min-height: 100vh;
font-family: "Inter", system-ui, sans-serif;
line-height: 1.5;
color: var(--ink);
background-color: var(--paper);
background-image:
var(--halftone),
radial-gradient(circle at 18% 12%, rgba(255, 46, 77, 0.08), transparent 42%),
radial-gradient(circle at 84% 88%, rgba(46, 107, 255, 0.08), transparent 45%);
background-size: 7px 7px, 100% 100%, 100% 100%;
padding: 26px 18px 48px;
display: flex;
justify-content: center;
}
.stage {
width: 100%;
max-width: 820px;
}
/* ---------- Control bar ---------- */
.bar {
background: var(--panel);
border: var(--ink-border);
border-radius: var(--r-md);
box-shadow: var(--shadow);
padding: 14px 16px;
margin-bottom: 22px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 14px;
}
.bar__brand {
display: flex;
align-items: center;
gap: 12px;
}
.bar__sigil {
display: grid;
place-items: center;
width: 44px;
height: 44px;
flex: none;
background: var(--accent-2);
border: var(--ink-border);
border-radius: var(--r-sm);
font-size: 22px;
color: var(--ink);
box-shadow: 3px 3px 0 var(--ink);
transform: rotate(-4deg);
}
.bar__title {
font-family: "Bangers", system-ui, cursive;
font-weight: 400;
font-size: clamp(26px, 5vw, 34px);
letter-spacing: 1px;
line-height: 0.95;
margin: 0;
color: var(--ink);
}
.bar__sub {
margin: 2px 0 0;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--muted);
}
.bar__controls {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
}
/* Segmented layout switch */
.seg {
display: flex;
align-items: center;
gap: 8px;
}
.seg__label {
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--muted);
}
.seg__btns {
display: flex;
border: var(--ink-border);
border-radius: var(--r-sm);
overflow: hidden;
background: var(--paper);
}
.seg__btn {
appearance: none;
border: 0;
border-right: 2px solid var(--ink);
background: transparent;
font: inherit;
font-weight: 700;
font-size: 12.5px;
padding: 8px 11px;
cursor: pointer;
color: var(--ink);
transition: background 0.12s ease, color 0.12s ease, transform 0.05s ease;
}
.seg__btn:last-child {
border-right: 0;
}
.seg__btn:hover {
background: var(--accent-2);
}
.seg__btn:active {
transform: translateY(1px);
}
.seg__btn.is-on {
background: var(--ink);
color: var(--paper);
}
.seg__btn:focus-visible,
.rdr:focus-visible,
.panel:focus-visible {
outline: 3px solid var(--accent-blue);
outline-offset: 2px;
}
/* Reading-order toggle */
.rdr {
appearance: none;
display: inline-flex;
align-items: center;
gap: 9px;
font: inherit;
font-weight: 800;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.04em;
padding: 9px 14px;
cursor: pointer;
color: var(--ink);
background: var(--accent);
border: var(--ink-border);
border-radius: var(--r-sm);
box-shadow: 3px 3px 0 var(--ink);
transition: transform 0.06s ease, box-shadow 0.06s ease, background 0.12s ease;
}
.rdr:hover {
transform: translate(-1px, -1px);
box-shadow: 4px 4px 0 var(--ink);
}
.rdr:active {
transform: translate(2px, 2px);
box-shadow: 1px 1px 0 var(--ink);
}
.rdr[aria-pressed="true"] {
background: var(--accent-blue);
color: var(--paper);
}
.rdr__num {
font-family: "Bangers", cursive;
font-size: 16px;
letter-spacing: 1px;
color: var(--ink);
background: var(--accent-2);
border: 2px solid var(--ink);
border-radius: 4px;
padding: 0 6px;
line-height: 1.3;
}
/* ---------- The comic page ---------- */
.page {
position: relative;
background: var(--panel);
border: 4px solid var(--ink);
border-radius: var(--r-md);
box-shadow: 10px 10px 0 var(--ink);
padding: var(--gutter);
display: grid;
gap: var(--gutter);
grid-template-columns: repeat(6, 1fr);
grid-auto-rows: 92px;
}
.page__halftone {
position: absolute;
inset: 0;
border-radius: var(--r-md);
background-image: var(--halftone);
background-size: 6px 6px;
opacity: 0.4;
pointer-events: none;
mix-blend-mode: multiply;
}
/* Panel base */
.panel {
position: relative;
overflow: hidden;
border: var(--ink-border);
border-radius: var(--r-sm);
background: var(--paper);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5);
min-width: 0;
cursor: pointer;
transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.panel:hover {
transform: translateY(-2px) rotate(-0.3deg);
box-shadow: 0 6px 0 rgba(14, 14, 18, 0.35);
z-index: 3;
}
.panel__art {
position: absolute;
inset: 0;
}
/* Distinct gradient "art" per panel */
.panel__art--city {
background:
linear-gradient(180deg, rgba(46, 107, 255, 0.55), rgba(255, 46, 77, 0.4) 70%, var(--ink)),
repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.07) 0 6px, transparent 6px 22px);
}
.panel__art--portrait {
background:
radial-gradient(circle at 50% 35%, var(--accent-2), transparent 45%),
linear-gradient(135deg, var(--ink-2), var(--accent));
}
.panel__art--tower {
background:
linear-gradient(0deg, var(--ink), rgba(46, 107, 255, 0.6)),
repeating-linear-gradient(180deg, rgba(255, 210, 63, 0.18) 0 4px, transparent 4px 18px);
}
.panel__art--blade {
background:
linear-gradient(120deg, var(--ink), var(--accent-blue)),
radial-gradient(circle at 70% 30%, rgba(255, 255, 255, 0.7), transparent 30%);
}
.panel__art--leap {
background:
radial-gradient(circle at 50% 60%, var(--accent-2), transparent 55%),
linear-gradient(45deg, var(--accent), var(--ink-2));
}
.panel__art--impact {
background:
radial-gradient(circle at 50% 50%, var(--accent-2) 0 18%, var(--accent) 18% 32%, var(--ink) 60%);
}
/* Grid placement — Classic (default) */
.p--splash {
grid-column: 1 / -1;
grid-row: span 2;
}
.p--halfL {
grid-column: 1 / 4;
grid-row: span 2;
}
.p--halfR {
grid-column: 4 / -1;
grid-row: span 2;
}
.p--strip {
grid-column: span 2;
grid-row: span 1;
}
/* Layout: Splash — big hero, rest small */
.page[data-layout="splash"] .p--splash {
grid-row: span 3;
}
.page[data-layout="splash"] .p--halfL,
.page[data-layout="splash"] .p--halfR {
grid-column: span 3;
grid-row: span 1;
}
.page[data-layout="splash"] .p--strip {
grid-column: span 2;
}
/* Layout: Strip — all full-width rows, vertical read */
.page[data-layout="strip"] .panel {
grid-column: 1 / -1 !important;
grid-row: span 1;
}
/* Layout: Mosaic — staggered asymmetric */
.page[data-layout="mosaic"] .p--splash {
grid-column: 1 / 5;
grid-row: span 2;
}
.page[data-layout="mosaic"] .p--halfL {
grid-column: 5 / -1;
grid-row: span 1;
}
.page[data-layout="mosaic"] .p--halfR {
grid-column: 5 / -1;
grid-row: span 1;
}
.page[data-layout="mosaic"] .p--strip:nth-of-type(4) {
grid-column: 1 / 3;
grid-row: span 2;
}
.page[data-layout="mosaic"] .p--strip:nth-of-type(5) {
grid-column: 3 / 5;
grid-row: span 2;
}
.page[data-layout="mosaic"] .p--strip:nth-of-type(6) {
grid-column: 5 / -1;
grid-row: span 1;
}
/* Panel number badge */
.panel__no {
position: absolute;
top: 7px;
left: 7px;
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-family: "Bangers", cursive;
font-size: 16px;
color: var(--ink);
background: var(--accent-2);
border: 2px solid var(--ink);
border-radius: 50%;
box-shadow: 2px 2px 0 var(--ink);
opacity: 0;
transform: scale(0.6);
transition: opacity 0.2s ease, transform 0.2s ease;
z-index: 6;
pointer-events: none;
}
.page.is-reading .panel__no {
opacity: 1;
transform: scale(1);
}
/* Caption boxes */
.cap {
position: absolute;
font-size: 10px;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink);
background: var(--accent-2);
border: 2px solid var(--ink);
padding: 3px 7px;
z-index: 4;
max-width: 72%;
}
.cap--tl {
top: 8px;
right: 8px;
}
.cap--br {
bottom: 8px;
left: 8px;
}
/* Balloons */
.balloon {
position: absolute;
z-index: 5;
max-width: 60%;
font-size: 12px;
font-weight: 600;
line-height: 1.35;
color: var(--ink);
background: var(--panel);
border: 2.5px solid var(--ink);
border-radius: 16px;
padding: 7px 11px;
box-shadow: 2px 2px 0 rgba(14, 14, 18, 0.4);
}
.balloon b {
font-weight: 800;
color: var(--accent);
}
.balloon--narr {
top: 10px;
left: 10px;
border-radius: 4px;
background: var(--paper);
font-style: italic;
font-weight: 500;
max-width: 50%;
}
.balloon--say {
bottom: 12px;
left: 12px;
}
.balloon--right {
left: auto;
right: 12px;
}
.balloon--tiny {
font-size: 10.5px;
max-width: 75%;
padding: 5px 8px;
bottom: 8px;
left: 8px;
}
.balloon--shout {
font-family: "Bangers", cursive;
font-weight: 400;
font-size: 17px;
letter-spacing: 0.5px;
border-width: 3px;
color: var(--accent);
border-radius: 6px;
}
/* Balloon tails */
.balloon__tail {
position: absolute;
width: 14px;
height: 14px;
background: var(--panel);
border-right: 2.5px solid var(--ink);
border-bottom: 2.5px solid var(--ink);
transform: rotate(45deg);
}
.balloon__tail--bl {
bottom: -8px;
left: 14px;
transform: rotate(135deg);
}
.balloon__tail--br {
bottom: -8px;
right: 14px;
transform: rotate(45deg);
}
/* SFX lettering */
.sfx {
position: absolute;
z-index: 5;
font-family: "Bangers", cursive;
font-weight: 400;
color: var(--accent-2);
-webkit-text-stroke: 2px var(--ink);
text-shadow: 3px 3px 0 var(--ink);
letter-spacing: 1px;
pointer-events: none;
transform: rotate(-7deg);
}
.sfx--splash {
top: 38%;
left: 36%;
font-size: 46px;
}
.sfx--mid {
bottom: 14px;
right: 12px;
font-size: 30px;
color: var(--accent);
}
.sfx--sm {
top: 10px;
right: 12px;
font-size: 20px;
transform: rotate(6deg);
}
.sfx--blue {
color: var(--paper);
-webkit-text-stroke: 1.5px var(--ink);
}
/* Reading highlight */
.panel.is-active {
z-index: 9;
transform: scale(1.025) rotate(-0.5deg);
box-shadow: 0 0 0 4px var(--accent), 8px 8px 0 var(--ink);
}
.panel.is-active::after {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(circle at 50% 50%, transparent 55%, rgba(46, 107, 255, 0.16));
pointer-events: none;
}
.page.is-reading .panel:not(.is-active) {
filter: grayscale(0.45) brightness(0.9);
}
/* ---------- Footer ---------- */
.foot {
margin-top: 18px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 8px;
font-size: 12px;
font-weight: 600;
color: var(--muted);
}
.foot b {
color: var(--ink);
}
.foot__page {
text-transform: uppercase;
letter-spacing: 0.06em;
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 130%);
background: var(--ink);
color: var(--paper);
font-weight: 700;
font-size: 13px;
padding: 11px 18px;
border: 2px solid var(--ink);
border-radius: var(--r-sm);
box-shadow: 4px 4px 0 var(--accent);
opacity: 0;
pointer-events: none;
transition: transform 0.28s cubic-bezier(0.2, 1.4, 0.4, 1), opacity 0.28s ease;
z-index: 50;
max-width: 86vw;
}
.toast.is-on {
transform: translate(-50%, 0);
opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
* {
transition: none !important;
animation: none !important;
}
}
/* ---------- Responsive ---------- */
@media (max-width: 520px) {
body {
padding: 16px 10px 36px;
}
.bar {
padding: 12px;
}
.bar__controls {
width: 100%;
justify-content: space-between;
}
.page {
grid-auto-rows: 74px;
--gutter: 10px;
box-shadow: 6px 6px 0 var(--ink);
}
/* Stack everything single-column on phones for readability */
.page .panel {
grid-column: 1 / -1 !important;
}
.p--splash {
grid-row: span 2 !important;
}
.balloon {
max-width: 70%;
font-size: 11px;
}
.balloon--narr {
max-width: 62%;
}
.sfx--splash {
font-size: 34px;
}
.seg {
width: 100%;
}
.seg__btns {
flex: 1;
}
.seg__btn {
flex: 1;
padding: 8px 4px;
}
}(function () {
"use strict";
var page = document.getElementById("page");
var readBtn = document.getElementById("readBtn");
var pageMeta = document.getElementById("pageMeta");
var layoutBtns = Array.prototype.slice.call(document.querySelectorAll(".seg__btn"));
var panels = Array.prototype.slice.call(document.querySelectorAll(".panel"));
var toastEl = document.getElementById("toast");
var LAYOUT_LABELS = {
classic: "Classic grid",
splash: "Splash hero",
strip: "Vertical strip",
mosaic: "Mosaic",
};
/* ---------- Toast helper ---------- */
var toastTimer = null;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("is-on");
window.clearTimeout(toastTimer);
toastTimer = window.setTimeout(function () {
toastEl.classList.remove("is-on");
}, 2200);
}
function reduceMotion() {
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}
function updateMeta() {
var layout = page.getAttribute("data-layout") || "classic";
pageMeta.textContent = panels.length + " panels · " + LAYOUT_LABELS[layout];
}
/* ---------- Layout switcher ---------- */
function setLayout(layout) {
if (reading) stopReading(true);
page.setAttribute("data-layout", layout);
layoutBtns.forEach(function (b) {
var on = b.getAttribute("data-layout") === layout;
b.classList.toggle("is-on", on);
b.setAttribute("aria-checked", on ? "true" : "false");
});
updateMeta();
toast("Layout: " + LAYOUT_LABELS[layout]);
}
layoutBtns.forEach(function (btn) {
btn.addEventListener("click", function () {
setLayout(btn.getAttribute("data-layout"));
});
// Arrow-key navigation across the radiogroup
btn.addEventListener("keydown", function (e) {
if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
e.preventDefault();
var i = layoutBtns.indexOf(btn);
var dir = e.key === "ArrowRight" ? 1 : -1;
var next = layoutBtns[(i + dir + layoutBtns.length) % layoutBtns.length];
next.focus();
setLayout(next.getAttribute("data-layout"));
});
});
/* ---------- Reading order (guided 1 -> N) ---------- */
var reading = false;
var stepTimer = null;
var stepIndex = 0;
// panels follow DOM source order, which equals reading order 1..N
var ordered = panels.slice().sort(function (a, b) {
return Number(a.dataset.panel) - Number(b.dataset.panel);
});
function clearActive() {
ordered.forEach(function (p) {
p.classList.remove("is-active");
});
}
function highlight(i) {
clearActive();
var p = ordered[i];
if (!p) return;
p.classList.add("is-active");
p.scrollIntoView({ behavior: reduceMotion() ? "auto" : "smooth", block: "center" });
}
function advance() {
highlight(stepIndex);
var n = stepIndex + 1;
toast("Reading panel " + n + " of " + ordered.length);
stepIndex += 1;
if (stepIndex >= ordered.length) {
// finished the page
window.clearTimeout(stepTimer);
stepTimer = window.setTimeout(function () {
stopReading(false);
toast("End of page — " + ordered.length + " panels read");
}, 1600);
return;
}
stepTimer = window.setTimeout(advance, 1500);
}
function startReading() {
reading = true;
stepIndex = 0;
page.classList.add("is-reading");
readBtn.setAttribute("aria-pressed", "true");
advance();
}
function stopReading(silent) {
reading = false;
window.clearTimeout(stepTimer);
page.classList.remove("is-reading");
clearActive();
readBtn.setAttribute("aria-pressed", "false");
if (!silent) toast("Reading guide off");
}
readBtn.addEventListener("click", function () {
if (reading) stopReading(false);
else startReading();
});
/* ---------- Manual panel focus: jump the guide to a panel ---------- */
function activatePanel(p) {
var i = ordered.indexOf(p);
if (i < 0) return;
window.clearTimeout(stepTimer);
if (!reading) {
reading = true;
page.classList.add("is-reading");
readBtn.setAttribute("aria-pressed", "true");
}
stepIndex = i;
highlight(i);
toast("Panel " + (i + 1) + " of " + ordered.length);
// resume the auto-walk from here
stepIndex += 1;
if (stepIndex < ordered.length) {
stepTimer = window.setTimeout(advance, 1700);
}
}
panels.forEach(function (p) {
p.addEventListener("click", function () {
activatePanel(p);
});
p.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
activatePanel(p);
}
});
});
/* ---------- Keyboard: Escape stops the guide ---------- */
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && reading) stopReading(false);
});
updateMeta();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Neon Ronin #07 — Classic Panel Grid Page</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>
<div class="stage">
<!-- Control bar -->
<header class="bar" aria-label="Page controls">
<div class="bar__brand">
<span class="bar__sigil" aria-hidden="true">★</span>
<div class="bar__titles">
<h1 class="bar__title">Neon Ronin</h1>
<p class="bar__sub">Issue #07 — “Static in the Rain” · Page 14</p>
</div>
</div>
<div class="bar__controls">
<div class="seg" role="group" aria-label="Layout preset">
<span class="seg__label">Layout</span>
<div class="seg__btns" role="radiogroup" aria-label="Choose page layout">
<button class="seg__btn is-on" role="radio" aria-checked="true" data-layout="classic">Classic</button>
<button class="seg__btn" role="radio" aria-checked="false" data-layout="splash">Splash</button>
<button class="seg__btn" role="radio" aria-checked="false" data-layout="strip">Strip</button>
<button class="seg__btn" role="radio" aria-checked="false" data-layout="mosaic">Mosaic</button>
</div>
</div>
<button id="readBtn" class="rdr" aria-pressed="false">
<span class="rdr__num" aria-hidden="true">1→N</span>
Reading order
</button>
</div>
</header>
<!-- The comic page -->
<main class="page" id="page" data-layout="classic" aria-label="Comic page, four panels">
<div class="page__halftone" aria-hidden="true"></div>
<!-- Panel 1: splash -->
<figure class="panel p--splash" data-panel="1" tabindex="0" aria-label="Panel 1, establishing splash">
<div class="panel__art panel__art--city" aria-hidden="true"></div>
<span class="panel__no" aria-hidden="true">1</span>
<figcaption class="cap cap--tl">RAIN CITY — 02:14</figcaption>
<div class="balloon balloon--narr" role="note">
Eight years since the Vanguard fell. The neon never stopped bleeding.
</div>
<div class="sfx sfx--splash" aria-hidden="true">KRAKOOM</div>
<div class="balloon balloon--say balloon--right" role="note">
<b>Kira:</b> Tell me the signal’s wrong.
<span class="balloon__tail balloon__tail--br" aria-hidden="true"></span>
</div>
</figure>
<!-- Panel 2: half -->
<figure class="panel p--halfL" data-panel="2" tabindex="0" aria-label="Panel 2, close on Kira">
<div class="panel__art panel__art--portrait" aria-hidden="true"></div>
<span class="panel__no" aria-hidden="true">2</span>
<div class="balloon balloon--say" role="note">
It’s not. He’s in the tower.
<span class="balloon__tail balloon__tail--bl" aria-hidden="true"></span>
</div>
<div class="sfx sfx--sm" aria-hidden="true">tk-tk</div>
</figure>
<!-- Panel 3: half -->
<figure class="panel p--halfR" data-panel="3" tabindex="0" aria-label="Panel 3, the tower">
<div class="panel__art panel__art--tower" aria-hidden="true"></div>
<span class="panel__no" aria-hidden="true">3</span>
<figcaption class="cap cap--br">IRON VANGUARD TOWER</figcaption>
<div class="balloon balloon--shout balloon--right" role="note">
THEN WE GO LOUD.
<span class="balloon__tail balloon__tail--bl" aria-hidden="true"></span>
</div>
</figure>
<!-- Panel 4-6: 3-up strip -->
<figure class="panel p--strip" data-panel="4" tabindex="0" aria-label="Panel 4, blade draw">
<div class="panel__art panel__art--blade" aria-hidden="true"></div>
<span class="panel__no" aria-hidden="true">4</span>
<div class="sfx sfx--sm sfx--blue" aria-hidden="true">SHINK</div>
</figure>
<figure class="panel p--strip" data-panel="5" tabindex="0" aria-label="Panel 5, the leap">
<div class="panel__art panel__art--leap" aria-hidden="true"></div>
<span class="panel__no" aria-hidden="true">5</span>
<div class="balloon balloon--say balloon--tiny" role="note">
For the Vanguard.
<span class="balloon__tail balloon__tail--bl" aria-hidden="true"></span>
</div>
</figure>
<figure class="panel p--strip" data-panel="6" tabindex="0" aria-label="Panel 6, impact">
<div class="panel__art panel__art--impact" aria-hidden="true"></div>
<span class="panel__no" aria-hidden="true">6</span>
<div class="sfx sfx--mid" aria-hidden="true">WHAM!</div>
</figure>
</main>
<footer class="foot">
<span class="foot__credit">Story & art — <b>Stealthis Studio</b></span>
<span class="foot__page" id="pageMeta">4 panels · Classic grid</span>
</footer>
</div>
<div id="toast" class="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Classic Panel Grid Page
A single comic page from the fictional Neon Ronin #07, built the way a print page is laid out: a six-column CSS grid carrying a full-width establishing splash, a pair of half-panels, and a three-up action strip. Every panel has a thick ink border, a gutter of breathing room, and a halftone Ben-Day dot fill over CSS-gradient art. Narration boxes, tailed speech balloons, a display-lettered shout, and oversized Bangers SFX (KRAKOOM, SHINK, WHAM!) sit on top in true comic style.
A layout-preset switcher reflows the same panels into four arrangements — Classic, Splash hero, vertical Strip, and an asymmetric Mosaic — by toggling a single data-layout attribute that drives the grid placement. The control is a keyboard-navigable radiogroup, so arrow keys cycle presets and the page meta line updates with the panel count and active layout.
The reading-order toggle is the centerpiece: it numbers each panel with an ink badge and animates a guided highlight from panel one through N, dimming the rest, scrolling each into view, and announcing progress through a toast helper. Clicking or pressing Enter on any panel jumps the guide there and resumes the auto-walk, and Escape stops the tour. Everything respects prefers-reduced-motion, and the page collapses to a single readable column below 520px.
Illustrative UI only — fictional series, characters, and data.