<h1 align="center">
<a href="https://prompts.chat">
Graphic novel. Panel grid. Multiple panels visible at once. Speech bubbles.
Loading actions...
<a href="https://prompts.chat">
TypeScript and ESLint rules that MUST be followed when creating, modifying, or reviewing any file under apps/frontend/, including .ts, .tsx, .js, and .jsx files. Also apply when discussing frontend linting, type safety, or ESLint configuration.
risks
Graphic novel. Panel grid. Multiple panels visible at once. Speech bubbles.
The comic template breaks from single-panel-per-viewport and shows multiple panels at once in a CSS grid — like a physical comic page. Panels have borders, gutters between them, and text lives INSIDE panels as speech bubbles or caption boxes, not in separate overlays. This is the only template where the reader sees 2-3 panels simultaneously, creating the juxtaposition and rhythm that makes comics a distinct medium. Key moments get full-page splash panels.
object-fit: cover within panel bounds — images fill their cell{
"layout": "comic",
"show_speaker_badge": false,
"image_sizing": "cover",
"text_position": "in-panel",
"transition": "page-turn",
"typewriter_speed": 0,
"viewer_style": "comic"
}
/* Comic page container */
.comic-page {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
max-width: 1100px;
margin: 0 auto;
padding: 8px;
background: #0a0a0a;
min-height: 100vh;
}
/* Standard panel */
.comic-panel {
position: relative;
border: 2px solid #222;
overflow: hidden;
background: #111;
min-height: 300px;
}
.comic-panel img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Splash panel — full width, key moment */
.comic-panel.splash {
grid-column: 1 / -1;
min-height: 500px;
}
/* Tall panel */
.comic-panel.tall {
grid-row: span 2;
}
/* Speech bubble */
.speech-bubble {
position: absolute;
background: #f5f5f0;
border: 1px solid #333;
border-radius: 16px;
padding: 0.6rem 1rem;
max-width: 55%;
font-family: 'Inter', sans-serif;
font-size: 0.85rem;
line-height: 1.4;
color: #1a1a1a;
z-index: 3;
}
/* Speech bubble tail */
.speech-bubble::after {
content: '';
position: absolute;
bottom: -10px;
left: 20%;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 12px solid #f5f5f0;
}
.speech-bubble.tail-right::after {
left: auto;
right: 20%;
}
/* Caption box (narration) */
.caption-box {
position: absolute;
top: 8px;
left: 8px;
background: rgba(212, 168, 67, 0.9);
padding: 0.4rem 0.8rem;
max-width: 60%;
font-family: 'Crimson Pro', serif;
font-style: italic;
font-size: 0.8rem;
line-height: 1.5;
color: #1a1a1a;
z-index: 3;
}
.caption-box.bottom {
top: auto;
bottom: 8px;
left: auto;
right: 8px;
}
/* Sound effect */
.sfx {
position: absolute;
font-family: 'Impact', 'Arial Black', sans-serif;
font-size: 3rem;
font-weight: 900;
color: #ff4444;
text-transform: uppercase;
-webkit-text-stroke: 2px #000;
transform: rotate(-8deg);
z-index: 4;
pointer-events: none;
}
/* Gutter — the dark space between panels */
.comic-page {
gap: 8px;
background: #0a0a0a;
}
/* Page turn transition */
.comic-page.exiting {
animation: page-slide-out 0.5s ease-in forwards;
}
.comic-page.entering {
animation: page-slide-in 0.5s ease-out forwards;
}
@keyframes page-slide-out {
to { transform: translateX(-100%); opacity: 0; }
}
@keyframes page-slide-in {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
Path to the HTML reference template: viewer-templates/comic.html