A loud sale banner in comic-book style. The word Boom is set in heavy slanted capitals with a thick black outline, a hard offset shadow and a yellow fill covered in red halftone dots. It pops in on a bouncy spring and then rocks gently. A second line in white with blue dots follows, above three boxed offers and a button, all on a blue dotted background with a slow sunburst. On phones the lettering scales down and the offers stack.
// Adapted from Magic UI (https://magicui.design/docs/components/comic-text)
// Copyright (c) Magic UI. MIT License: https://we.inc/components/licenses
import type { CSSProperties } from "react";
import { motion, useReducedMotion } from "framer-motion";
const deals = [
{ tag: "Back issues", line: "Three for the price of two" },
{ tag: "Graphic novels", line: "A quarter off every shelf" },
{ tag: "Kids' corner", line: "Free sketch class at noon" },
];
/**
* Comic-book lettering: a heavy slanted face with a thick black outline, a
* hard offset shadow and a halftone dot fill. Everything is sized in em, so
* set the size with a text class such as `text-[9rem]`.
*/
function ComicText({ children, fill = "#facc15", dots = "#ef4444", delay = 0, className = "" }: { children: string; fill?: string; dots?: string; delay?: number; className?: string }) {
const reduce = useReducedMotion();
const style: CSSProperties = {
lineHeight: 1,
fontFamily: "Impact, Haettenschweiler, 'Arial Narrow Bold', 'Arial Black', sans-serif",
fontWeight: 900,
letterSpacing: "0.02em",
textTransform: "uppercase",
WebkitTextStroke: "0.022em #000",
filter: `drop-shadow(0.06em 0.06em 0 #000) drop-shadow(0.035em 0.035em 0 ${dots})`,
backgroundColor: fill,
backgroundImage: `radial-gradient(circle at 1px 1px, ${dots} 1.2px, transparent 0)`,
backgroundSize: "8px 8px",
WebkitBackgroundClip: "text",
backgroundClip: "text",
WebkitTextFillColor: "transparent",
};
return (
<motion.span
className={`inline-block select-none px-2 ${className}`}
style={style}
initial={reduce ? false : { opacity: 0, scale: 0.4, rotate: -12, skewX: -10 }}
animate={{ opacity: 1, scale: 1, rotate: 0, skewX: -10 }}
transition={{ type: "spring", stiffness: 380, damping: 13, delay }}
>
{children}
</motion.span>
);
}
export default function ComicBookHalftoneHeadlineText() {
return (
<section className="relative overflow-hidden bg-blue-600 px-5 py-16 sm:px-6 sm:py-20">
<style>{`
@keyframes comic-book-halftone-headline-text-wobble {
0%, 100% { transform: rotate(-2deg) scale(1); }
50% { transform: rotate(2deg) scale(1.04); }
}
@keyframes comic-book-halftone-headline-text-spin {
to { transform: rotate(360deg); }
}
.comic-book-halftone-headline-text-wobble { animation: comic-book-halftone-headline-text-wobble 2.4s ease-in-out infinite; }
.comic-book-halftone-headline-text-spin { animation: comic-book-halftone-headline-text-spin 60s linear infinite; }
@media (prefers-reduced-motion: reduce) {
.comic-book-halftone-headline-text-wobble,
.comic-book-halftone-headline-text-spin { animation: none; }
}
`}</style>
{/* Halftone dots and a slow sunburst behind everything. */}
<span aria-hidden="true" className="pointer-events-none absolute inset-0 opacity-30 [background-image:radial-gradient(circle_at_2px_2px,#1e3a8a_2px,transparent_0)] [background-size:14px_14px]" />
<span aria-hidden="true" className="comic-book-halftone-headline-text-spin pointer-events-none absolute left-1/2 top-1/2 -ml-[700px] -mt-[700px] h-[1400px] w-[1400px] opacity-20 [background-image:repeating-conic-gradient(#fde047_0deg_8deg,transparent_8deg_20deg)] [mask-image:radial-gradient(circle,black_10%,transparent_60%)]" />
<div className="relative mx-auto max-w-4xl text-center">
<p className="inline-block -rotate-2 border-4 border-black bg-white px-4 py-1.5 text-sm font-black uppercase tracking-wider text-black shadow-[4px_4px_0_#000]">Panel Nine Comics, this Saturday only</p>
<h2 className="mt-8 flex flex-col items-center gap-3 sm:gap-5">
<span className="sr-only">Boom! Half price day</span>
<span aria-hidden="true" className="comic-book-halftone-headline-text-wobble block">
<ComicText className="text-[5.5rem] sm:text-[9rem]">Boom!</ComicText>
</span>
<span aria-hidden="true" className="block">
<ComicText fill="#ffffff" dots="#38bdf8" delay={0.25} className="text-[2.6rem] sm:text-[4.5rem]">
Half price day
</ComicText>
</span>
</h2>
<ul className="mt-12 grid gap-4 text-left sm:grid-cols-3">
{deals.map((d, i) => (
<li key={d.tag} className={`border-4 border-black bg-yellow-300 p-4 shadow-[6px_6px_0_#000] ${i === 1 ? "sm:rotate-1" : "sm:-rotate-1"}`}>
<p className="text-xs font-black uppercase tracking-wider text-red-700">{d.tag}</p>
<p className="mt-1 text-lg font-extrabold leading-snug text-black">{d.line}</p>
</li>
))}
</ul>
<a href="#" className="mt-10 inline-flex h-14 items-center border-4 border-black bg-red-600 px-8 text-lg font-black uppercase tracking-wide text-white shadow-[6px_6px_0_#000] transition-transform hover:-translate-y-1 focus:outline-none focus-visible:ring-4 focus-visible:ring-yellow-300 motion-reduce:transition-none">
Save my spot in line
</a>
</div>
</section>
);
}
Install with one command: npx shadcn@latest add https://we.inc/r/comic-book-halftone-headline-text.json
The text has a solid background colour plus a tiny repeating radial gradient that draws one dot per 8px tile. `background-clip: text` with a transparent text fill cuts that dotted background to the shape of the letters.
`-webkit-text-stroke` draws a black line centred on the edge of each letter. Despite the prefix it is supported in all current browsers. The hard shadow is two `drop-shadow` filters with no blur, one black and one in the dot colour.
No. The demo uses Impact and similar condensed system faces, which are heavy enough to carry the outline. A real comic face will look closer to print lettering if you already load one.
The styled word is uppercase, skewed and split across lines, which can be read out oddly. A hidden copy of the full headline gives assistive technology a clean sentence instead.
Yes. Replace `animate` on the `motion.span` with `whileInView` and add `viewport={{ once: true }}`.
Adapted from Comic Text in Magic UI, used under its open-source licence. Licences and credits
Related: Shimmering Chrome Gradient Headline, Text Scramble Decode Headline, Call-to-Action Banner on a Brand Colour.
More text effects or all free UI components. Start for free to have a full site built around it.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.