Justified text laid on a plane that tilts away into a starfield. Hover and it scrolls off into the distance, fading as it goes, then fades back in to loop. Needs only rotateX and translateY.
Also known as: opening crawl text css, receding scroll text, 3d text crawl.
The launch is two weeks away. Forty pull requests have merged, the last blocker is closed and the product has been renamed twice. Only the landing page remains, and the team is ready.
<div class="hfx-perspective-crawl-text">
<div class="hfx-perspective-crawl-text-view">
<div class="hfx-perspective-crawl-text-copy">
<strong>Chapter seven</strong>
<p>The launch is two weeks away. Forty pull requests have merged, the last blocker is closed and the product has been renamed twice. Only the landing page remains, and the team is ready.</p>
</div>
</div>
</div>.hfx-perspective-crawl-text {
--c: var(--hfx-color, #3b82f6);
font-size: var(--hfx-size, 16px);
position: relative;
width: 17em;
height: 9em;
overflow: hidden;
border-radius: 0.8em;
background:
radial-gradient(circle at 12% 18%, #fff 0 0.05em, transparent 0.08em),
radial-gradient(circle at 34% 8%, #fff 0 0.04em, transparent 0.07em),
radial-gradient(circle at 58% 22%, #fff 0 0.05em, transparent 0.08em),
radial-gradient(circle at 82% 12%, #fff 0 0.06em, transparent 0.09em),
radial-gradient(circle at 92% 40%, #fff 0 0.04em, transparent 0.07em),
radial-gradient(circle at 6% 52%, #fff 0 0.04em, transparent 0.07em),
linear-gradient(color-mix(in srgb, var(--c) 25%, #020617), #020617);
font-family: system-ui, sans-serif;
}
.hfx-perspective-crawl-text-view {
position: absolute;
inset: 0;
perspective: 11em;
perspective-origin: 50% 0;
-webkit-mask-image: linear-gradient(transparent 8%, #000 55%);
mask-image: linear-gradient(transparent 8%, #000 55%);
}
.hfx-perspective-crawl-text-copy {
position: absolute;
top: 3.2em;
left: 2.6em;
right: 2.6em;
color: color-mix(in srgb, var(--c) 30%, #fff);
text-align: center;
transform-origin: 50% 0;
transform: rotateX(46deg) translateY(0);
}
.hfx-perspective-crawl-text-copy strong {
display: block;
margin-bottom: 0.5em;
font-size: 0.9em;
letter-spacing: 0.2em;
text-transform: uppercase;
}
.hfx-perspective-crawl-text-copy p {
margin: 0;
font-size: 0.75em;
line-height: 1.5;
text-align: justify;
}
.hfx-perspective-crawl-text:hover .hfx-perspective-crawl-text-copy {
animation: hfx-perspective-crawl-text-scroll 14s linear infinite;
}
@keyframes hfx-perspective-crawl-text-scroll {
0% { transform: rotateX(46deg) translateY(0); opacity: 1; }
88% { transform: rotateX(46deg) translateY(-12em); opacity: 1; }
90% { transform: rotateX(46deg) translateY(-12em); opacity: 0; }
91% { transform: rotateX(46deg) translateY(0); opacity: 0; }
100% { transform: rotateX(46deg) translateY(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.hfx-perspective-crawl-text, .hfx-perspective-crawl-text *, .hfx-perspective-crawl-text::before, .hfx-perspective-crawl-text::after, .hfx-perspective-crawl-text *::before, .hfx-perspective-crawl-text *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--hfx-color on the element or a parent.
Currently previewing #3b82f6.--hfx-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 16px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.