30 CSS Text Animations10 / 30
CSS Split Text Reveal Animation Overflow Hidden
The agency-site classic: multi-line headlines where each line climbs out of an invisible overflow:hidden mask — shown as a warm editorial three-liner and a brutalist skew-rise where lines shear upright as they land.
Published Updated
The code
<div class="cat-10-group">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap" rel="stylesheet">
<section class="cat-10a" aria-label="Editorial line mask reveal">
<h1 class="cat-10a__title">
<span class="cat-10a__mask"><span class="cat-10a__line" style="--i:0">We craft</span></span>
<span class="cat-10a__mask"><span class="cat-10a__line" style="--i:1">interfaces that</span></span>
<span class="cat-10a__mask"><span class="cat-10a__line cat-10a__line--em" style="--i:2">feel inevitable.</span></span>
</h1>
</section>
<link href="https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,500..900;1,500..900&display=swap" rel="stylesheet">
<section class="cat-10b" aria-label="Skewed line mask reveal">
<h1 class="cat-10b__title">
<span class="cat-10b__mask"><span class="cat-10b__line" style="--i:0">BOLD</span></span>
<span class="cat-10b__mask"><span class="cat-10b__line cat-10b__line--ghost" style="--i:1">MOVES</span></span>
<span class="cat-10b__mask"><span class="cat-10b__line cat-10b__line--acid" style="--i:2">ONLY.</span></span>
</h1>
</section>
</div><div class="cat-10-group">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap" rel="stylesheet">
<section class="cat-10a" aria-label="Editorial line mask reveal">
<h1 class="cat-10a__title">
<span class="cat-10a__mask"><span class="cat-10a__line" style="--i:0">We craft</span></span>
<span class="cat-10a__mask"><span class="cat-10a__line" style="--i:1">interfaces that</span></span>
<span class="cat-10a__mask"><span class="cat-10a__line cat-10a__line--em" style="--i:2">feel inevitable.</span></span>
</h1>
</section>
<link href="https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,500..900;1,500..900&display=swap" rel="stylesheet">
<section class="cat-10b" aria-label="Skewed line mask reveal">
<h1 class="cat-10b__title">
<span class="cat-10b__mask"><span class="cat-10b__line" style="--i:0">BOLD</span></span>
<span class="cat-10b__mask"><span class="cat-10b__line cat-10b__line--ghost" style="--i:1">MOVES</span></span>
<span class="cat-10b__mask"><span class="cat-10b__line cat-10b__line--acid" style="--i:2">ONLY.</span></span>
</h1>
</section>
</div>.cat-10-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.cat-10-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.cat-10a,
.cat-10a *,
.cat-10a *::before,
.cat-10a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cat-10a {
--ink: #26201a;
--accent: oklch(0.6 0.13 60);
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(180deg,#f7f2e9,#efe6d6);
font-family: 'Playfair Display',Georgia,serif;
}
.cat-10a__title {
font-size: clamp(36px,6vw,76px);
font-weight: 600;
line-height: 1.12;
color: var(--ink);
letter-spacing: -.015em;
}
.cat-10a__mask {
display: block;
overflow: hidden;
padding-bottom: .1em;
margin-bottom: -.1em;
}
.cat-10a__line {
display: block;
transform: translateY(112%);
animation: cat-10a-rise .85s cubic-bezier(.16,.84,.3,1) calc(var(--i)*140ms + .25s) both;
}
.cat-10a__line--em {
font-style: italic;
color: var(--accent);
}
@keyframes cat-10a-rise {
to {
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.cat-10a__line {
animation: none;
transform: none;
}
}
.cat-10b,
.cat-10b *,
.cat-10b *::before,
.cat-10b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cat-10b {
--acid: oklch(0.9 0.21 125);
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: #101010;
font-family: 'Archivo','Segoe UI',system-ui,sans-serif;
}
.cat-10b__title {
font-size: clamp(52px,9.4vw,128px);
font-weight: 900;
line-height: .96;
letter-spacing: -.03em;
color: #f4f4f0;
text-transform: uppercase;
}
.cat-10b__mask {
display: block;
overflow: hidden;
padding-bottom: .06em;
margin-bottom: -.06em;
}
.cat-10b__line {
display: block;
transform-origin: left bottom;
transform: translateY(115%) skewY(7deg);
animation: cat-10b-rise .8s cubic-bezier(.2,.85,.25,1) calc(var(--i)*120ms + .2s) both;
}
.cat-10b__line--ghost {
color: transparent;
-webkit-text-stroke: 2px #f4f4f0;
}
.cat-10b__line--acid {
color: var(--acid);
}
@keyframes cat-10b-rise {
to {
transform: translateY(0) skewY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.cat-10b__line {
animation: none;
transform: none;
}
}.cat-10-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.cat-10-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.cat-10a,
.cat-10a *,
.cat-10a *::before,
.cat-10a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cat-10a {
--ink: #26201a;
--accent: oklch(0.6 0.13 60);
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(180deg,#f7f2e9,#efe6d6);
font-family: 'Playfair Display',Georgia,serif;
}
.cat-10a__title {
font-size: clamp(36px,6vw,76px);
font-weight: 600;
line-height: 1.12;
color: var(--ink);
letter-spacing: -.015em;
}
.cat-10a__mask {
display: block;
overflow: hidden;
padding-bottom: .1em;
margin-bottom: -.1em;
}
.cat-10a__line {
display: block;
transform: translateY(112%);
animation: cat-10a-rise .85s cubic-bezier(.16,.84,.3,1) calc(var(--i)*140ms + .25s) both;
}
.cat-10a__line--em {
font-style: italic;
color: var(--accent);
}
@keyframes cat-10a-rise {
to {
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.cat-10a__line {
animation: none;
transform: none;
}
}
.cat-10b,
.cat-10b *,
.cat-10b *::before,
.cat-10b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cat-10b {
--acid: oklch(0.9 0.21 125);
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: #101010;
font-family: 'Archivo','Segoe UI',system-ui,sans-serif;
}
.cat-10b__title {
font-size: clamp(52px,9.4vw,128px);
font-weight: 900;
line-height: .96;
letter-spacing: -.03em;
color: #f4f4f0;
text-transform: uppercase;
}
.cat-10b__mask {
display: block;
overflow: hidden;
padding-bottom: .06em;
margin-bottom: -.06em;
}
.cat-10b__line {
display: block;
transform-origin: left bottom;
transform: translateY(115%) skewY(7deg);
animation: cat-10b-rise .8s cubic-bezier(.2,.85,.25,1) calc(var(--i)*120ms + .2s) both;
}
.cat-10b__line--ghost {
color: transparent;
-webkit-text-stroke: 2px #f4f4f0;
}
.cat-10b__line--acid {
color: var(--acid);
}
@keyframes cat-10b-rise {
to {
transform: translateY(0) skewY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.cat-10b__line {
animation: none;
transform: none;
}
}/* No JavaScript — each line lives in its own overflow:hidden mask and climbs from translateY(112%) with a 140ms stagger. */ /* No JavaScript — lines rise while unskewing from skewY(7deg), transform-origin left-bottom, so each lands with a whip of momentum. */
/* No JavaScript — each line lives in its own overflow:hidden mask and climbs from translateY(112%) with a 140ms stagger. */
/* No JavaScript — lines rise while unskewing from skewY(7deg), transform-origin left-bottom, so each lands with a whip of momentum. */Here's a working CSS Text Animation from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Split Text Reveal Animation Overflow Hidden
Source: https://codefronts.com/motion/css-text-animations/css-letter-spacing-animation/
The agency-site classic: multi-line headlines where each line climbs out of an invisible overflow:hidden mask — shown as a warm editorial three-liner and a brutalist skew-rise where lines shear upright as they land.
## HTML
```html
<div class="cat-10-group">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap" rel="stylesheet">
<section class="cat-10a" aria-label="Editorial line mask reveal">
<h1 class="cat-10a__title">
<span class="cat-10a__mask"><span class="cat-10a__line" style="--i:0">We craft</span></span>
<span class="cat-10a__mask"><span class="cat-10a__line" style="--i:1">interfaces that</span></span>
<span class="cat-10a__mask"><span class="cat-10a__line cat-10a__line--em" style="--i:2">feel inevitable.</span></span>
</h1>
</section>
<link href="https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,500..900;1,500..900&display=swap" rel="stylesheet">
<section class="cat-10b" aria-label="Skewed line mask reveal">
<h1 class="cat-10b__title">
<span class="cat-10b__mask"><span class="cat-10b__line" style="--i:0">BOLD</span></span>
<span class="cat-10b__mask"><span class="cat-10b__line cat-10b__line--ghost" style="--i:1">MOVES</span></span>
<span class="cat-10b__mask"><span class="cat-10b__line cat-10b__line--acid" style="--i:2">ONLY.</span></span>
</h1>
</section>
</div>
```
## CSS
```css
.cat-10-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.cat-10-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.cat-10a,
.cat-10a *,
.cat-10a *::before,
.cat-10a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cat-10a {
--ink: #26201a;
--accent: oklch(0.6 0.13 60);
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(180deg,#f7f2e9,#efe6d6);
font-family: 'Playfair Display',Georgia,serif;
}
.cat-10a__title {
font-size: clamp(36px,6vw,76px);
font-weight: 600;
line-height: 1.12;
color: var(--ink);
letter-spacing: -.015em;
}
.cat-10a__mask {
display: block;
overflow: hidden;
padding-bottom: .1em;
margin-bottom: -.1em;
}
.cat-10a__line {
display: block;
transform: translateY(112%);
animation: cat-10a-rise .85s cubic-bezier(.16,.84,.3,1) calc(var(--i)*140ms + .25s) both;
}
.cat-10a__line--em {
font-style: italic;
color: var(--accent);
}
@keyframes cat-10a-rise {
to {
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.cat-10a__line {
animation: none;
transform: none;
}
}
.cat-10b,
.cat-10b *,
.cat-10b *::before,
.cat-10b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cat-10b {
--acid: oklch(0.9 0.21 125);
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: #101010;
font-family: 'Archivo','Segoe UI',system-ui,sans-serif;
}
.cat-10b__title {
font-size: clamp(52px,9.4vw,128px);
font-weight: 900;
line-height: .96;
letter-spacing: -.03em;
color: #f4f4f0;
text-transform: uppercase;
}
.cat-10b__mask {
display: block;
overflow: hidden;
padding-bottom: .06em;
margin-bottom: -.06em;
}
.cat-10b__line {
display: block;
transform-origin: left bottom;
transform: translateY(115%) skewY(7deg);
animation: cat-10b-rise .8s cubic-bezier(.2,.85,.25,1) calc(var(--i)*120ms + .2s) both;
}
.cat-10b__line--ghost {
color: transparent;
-webkit-text-stroke: 2px #f4f4f0;
}
.cat-10b__line--acid {
color: var(--acid);
}
@keyframes cat-10b-rise {
to {
transform: translateY(0) skewY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.cat-10b__line {
animation: none;
transform: none;
}
}
```
## JavaScript
```js
/* No JavaScript — each line lives in its own overflow:hidden mask and climbs from translateY(112%) with a 140ms stagger. */
/* No JavaScript — lines rise while unskewing from skewY(7deg), transform-origin left-bottom, so each lands with a whip of momentum. */
```Here's a working CSS Text Animation from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Split Text Reveal Animation Overflow Hidden
Source: https://codefronts.com/motion/css-text-animations/css-letter-spacing-animation/
The agency-site classic: multi-line headlines where each line climbs out of an invisible overflow:hidden mask — shown as a warm editorial three-liner and a brutalist skew-rise where lines shear upright as they land.
## HTML
```html
<div class="cat-10-group">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap" rel="stylesheet">
<section class="cat-10a" aria-label="Editorial line mask reveal">
<h1 class="cat-10a__title">
<span class="cat-10a__mask"><span class="cat-10a__line" style="--i:0">We craft</span></span>
<span class="cat-10a__mask"><span class="cat-10a__line" style="--i:1">interfaces that</span></span>
<span class="cat-10a__mask"><span class="cat-10a__line cat-10a__line--em" style="--i:2">feel inevitable.</span></span>
</h1>
</section>
<link href="https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,500..900;1,500..900&display=swap" rel="stylesheet">
<section class="cat-10b" aria-label="Skewed line mask reveal">
<h1 class="cat-10b__title">
<span class="cat-10b__mask"><span class="cat-10b__line" style="--i:0">BOLD</span></span>
<span class="cat-10b__mask"><span class="cat-10b__line cat-10b__line--ghost" style="--i:1">MOVES</span></span>
<span class="cat-10b__mask"><span class="cat-10b__line cat-10b__line--acid" style="--i:2">ONLY.</span></span>
</h1>
</section>
</div>
```
## CSS
```css
.cat-10-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.cat-10-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.cat-10a,
.cat-10a *,
.cat-10a *::before,
.cat-10a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cat-10a {
--ink: #26201a;
--accent: oklch(0.6 0.13 60);
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(180deg,#f7f2e9,#efe6d6);
font-family: 'Playfair Display',Georgia,serif;
}
.cat-10a__title {
font-size: clamp(36px,6vw,76px);
font-weight: 600;
line-height: 1.12;
color: var(--ink);
letter-spacing: -.015em;
}
.cat-10a__mask {
display: block;
overflow: hidden;
padding-bottom: .1em;
margin-bottom: -.1em;
}
.cat-10a__line {
display: block;
transform: translateY(112%);
animation: cat-10a-rise .85s cubic-bezier(.16,.84,.3,1) calc(var(--i)*140ms + .25s) both;
}
.cat-10a__line--em {
font-style: italic;
color: var(--accent);
}
@keyframes cat-10a-rise {
to {
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.cat-10a__line {
animation: none;
transform: none;
}
}
.cat-10b,
.cat-10b *,
.cat-10b *::before,
.cat-10b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cat-10b {
--acid: oklch(0.9 0.21 125);
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: #101010;
font-family: 'Archivo','Segoe UI',system-ui,sans-serif;
}
.cat-10b__title {
font-size: clamp(52px,9.4vw,128px);
font-weight: 900;
line-height: .96;
letter-spacing: -.03em;
color: #f4f4f0;
text-transform: uppercase;
}
.cat-10b__mask {
display: block;
overflow: hidden;
padding-bottom: .06em;
margin-bottom: -.06em;
}
.cat-10b__line {
display: block;
transform-origin: left bottom;
transform: translateY(115%) skewY(7deg);
animation: cat-10b-rise .8s cubic-bezier(.2,.85,.25,1) calc(var(--i)*120ms + .2s) both;
}
.cat-10b__line--ghost {
color: transparent;
-webkit-text-stroke: 2px #f4f4f0;
}
.cat-10b__line--acid {
color: var(--acid);
}
@keyframes cat-10b-rise {
to {
transform: translateY(0) skewY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.cat-10b__line {
animation: none;
transform: none;
}
}
```
## JavaScript
```js
/* No JavaScript — each line lives in its own overflow:hidden mask and climbs from translateY(112%) with a 140ms stagger. */
/* No JavaScript — lines rise while unskewing from skewY(7deg), transform-origin left-bottom, so each lands with a whip of momentum. */
```How this works
Each line gets two elements: a mask (display:block; overflow:hidden) and the moving line inside it. Because the mask hugs the line box, a translateY(110%) hides the text completely with no clip-path math:
.mask{overflow:hidden}
.line{transform:translateY(110%);
animation:rise .85s cubic-bezier(.16,.84,.3,1) both}
.mask:nth-child(2) .line{animation-delay:.14s}The skew variant adds skewY(7deg) to the hidden state with transform-origin:left bottom; unskewing during the rise gives the line a whip of momentum. Padding on the mask (with negative margin compensation) keeps descenders like g/y from shaving against the mask edge.
Make it yours
- Delay lines by 120–160ms each — slower reads editorial, faster reads app-like.
- Swap rise direction per line (alternate up/down) for a shutter feel.
- Tint one line or word as the accent — one, not three.
- Trigger on scroll instead of load by pairing the same keyframes with animation-timeline:view() (see demo 16).
Gotchas — read before shipping
- Give masks a little bottom padding (offset by negative margin) or descenders get clipped mid-animation.
- Each line needs its own mask — one mask around all lines reveals them as a block.
- line-height below 1.1 will clip ascenders in the mask; test with real copy.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 15.4+ | 113+ | 111+ |
Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome 26+.
Pure transform + overflow — works everywhere, including old mobile WebViews. Reduced-motion shows all lines settled.