A loud editorial hero on cream paper. Selected words in the headline, and the three big figures below it, carry an offset shadow drawn as fine diagonal hatching instead of a solid block, and the hatching slides slowly so the words seem to shimmer like a print that is slightly out of register. The demo is a type foundry page with a headline, a paragraph, two buttons and a three-cell figure strip. On phones the headline scales with the screen width and the cells stack.
// Adapted from Magic UI (https://magicui.design/docs/components/line-shadow-text)
// Copyright (c) Magic UI. MIT License: https://we.inc/components/licenses
import type { CSSProperties } from "react";
import { motion, useReducedMotion } from "framer-motion";
const figures = [
{ value: "148", label: "typefaces drawn since the studio opened", shadow: "#ea580c" },
{ value: "31", label: "writing systems covered, Latin to Tifinagh", shadow: "#0d9488" },
{ value: "9", label: "people, two cats and one proofing press", shadow: "#1c1917" },
];
/**
* Text with a hatched shadow that slides behind it.
* The shadow is a second copy of the same text, filled with thin diagonal stripes.
*/
function LineShadowText({ children, shadowColor = "#1c1917", className = "" }: { children: string; shadowColor?: string; className?: string }) {
return (
<span
data-text={children}
className={`headline-with-moving-striped-line-shadow-text ${className}`}
style={{ "--line-shadow-color": shadowColor } as CSSProperties}
>
{children}
</span>
);
}
export default function HeadlineWithMovingStripedLineShadow() {
const reduce = useReducedMotion();
const rise = (delay: number) => ({
initial: reduce ? false : { opacity: 0, y: 24 },
animate: { opacity: 1, y: 0 },
transition: { type: "spring" as const, stiffness: 120, damping: 20, delay },
});
return (
<section className="bg-[#f6f1e7] px-4 py-20 text-stone-900 sm:px-6 sm:py-28">
<style>{`
@keyframes headline-with-moving-striped-line-shadow-slide {
from { background-position: 0 0; }
to { background-position: 100% -100%; }
}
.headline-with-moving-striped-line-shadow-text {
position: relative;
z-index: 0;
display: inline-block;
white-space: nowrap;
}
.headline-with-moving-striped-line-shadow-text::after {
content: attr(data-text);
position: absolute;
top: 0.09em;
left: 0.09em;
z-index: -1;
background-image: linear-gradient(45deg, transparent 38%, var(--line-shadow-color) 38%, var(--line-shadow-color) 62%, transparent 0);
background-size: 0.08em 0.08em;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: headline-with-moving-striped-line-shadow-slide 15s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.headline-with-moving-striped-line-shadow-text::after { animation: none; }
}
`}</style>
<div className="mx-auto max-w-6xl">
<motion.p {...rise(0)} className="flex items-center gap-3 font-mono text-sm uppercase tracking-widest text-stone-600">
<span className="h-px w-10 bg-stone-900" aria-hidden="true" />
Ostrander Type Foundry
</motion.p>
<motion.h1 {...rise(0.08)} className="mt-8 text-[14.5vw] font-black uppercase italic leading-[0.92] tracking-tighter sm:text-7xl lg:text-[6.75rem]">
Type with <LineShadowText shadowColor="#ea580c">weight</LineShadowText>
<br />
and some <LineShadowText shadowColor="#0d9488">noise</LineShadowText>
</motion.h1>
<motion.div {...rise(0.16)} className="mt-10 flex flex-col gap-8 lg:flex-row lg:items-end lg:justify-between">
<p className="max-w-xl text-lg leading-relaxed text-stone-700">
Display faces for posters, packaging and mastheads. Every family ships with variable weights, a trial licence and the drawings we threw away.
</p>
<div className="flex flex-wrap gap-3">
<a href="#" className="inline-flex h-12 items-center border-2 border-stone-900 bg-stone-900 px-6 text-sm font-bold uppercase tracking-wide text-[#f6f1e7] transition-colors hover:bg-orange-600 hover:border-orange-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-orange-600 focus-visible:ring-offset-2 focus-visible:ring-offset-[#f6f1e7]">
Browse the catalogue
</a>
<a href="#" className="inline-flex h-12 items-center border-2 border-stone-900 px-6 text-sm font-bold uppercase tracking-wide transition-colors hover:bg-stone-900 hover:text-[#f6f1e7] focus:outline-none focus-visible:ring-2 focus-visible:ring-orange-600 focus-visible:ring-offset-2 focus-visible:ring-offset-[#f6f1e7]">
Get trial fonts
</a>
</div>
</motion.div>
<dl className="mt-16 grid gap-px border-2 border-stone-900 bg-stone-900 sm:grid-cols-3">
{figures.map((f, i) => (
<motion.div key={f.label} {...rise(0.24 + i * 0.07)} className="flex flex-col-reverse justify-end gap-3 bg-[#f6f1e7] p-6 sm:p-8">
<dt className="max-w-[16rem] text-sm leading-snug text-stone-700">{f.label}</dt>
<dd className="text-7xl font-black italic leading-none tracking-tighter sm:text-8xl">
<LineShadowText shadowColor={f.shadow}>{f.value}</LineShadowText>
</dd>
</motion.div>
))}
</dl>
</div>
</section>
);
}
Install with one command: npx shadcn@latest add https://we.inc/r/headline-with-moving-striped-line-shadow.json
The component writes its text into a `data-text` attribute. A `::after` pseudo-element prints that text again, shifted down and right, with a tiny repeating diagonal gradient as its background. `background-clip: text` with a transparent text colour means the stripes only show inside the letter shapes.
The copy is produced from the `data-text` attribute, which can only hold text. If you need a line break or nested markup, split the phrase into several `LineShadowText` pieces.
A keyframe animation slides the background position of the pseudo-element diagonally. Because the pattern repeats every few pixels, the movement reads as a slow shimmer along the letters instead of a visible scroll.
Yes, but it is designed for heavy display weights. On thin letters the offset copy has very little area and the hatching is hard to see.
The component sets `white-space: nowrap` so the copy always lines up with the original. Keep each wrapped piece to a word or a short phrase.
Adapted from Line Shadow Text in Magic UI, used under its open-source licence. Licences and credits
Related: Shimmering Chrome Gradient Headline, Editorial Hero with Sliding Letters and Drawn Underline, Big Headline Hero with Stats Row.
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.