31 CSS Hero Sections18 / 31
Kinetic Type Hero
Rave-poster kinetic typography — letters rotate, scale, and float individually using per-letter delay. Acid green and magenta on pure black with strobe-like accents.
Published
The code
<section class="hs-18" aria-label="Hero">
<div class="hs-18-pulse" aria-hidden="true"></div>
<h2 class="hs-18__h">
<span style="--i: 0">F</span><span style="--i: 1">U</span><span style="--i: 2">L</span
><span style="--i: 3">L</span>
<span class="hs-18__gap"></span>
<span style="--i: 4">B</span><span style="--i: 5">A</span><span style="--i: 6">S</span
><span style="--i: 7">S</span>
<span class="hs-18__gap"></span>
<em
><span style="--i: 8">M</span><span style="--i: 9">O</span><span style="--i: 10">D</span
><span style="--i: 11">E</span></em
>
</h2>
<p class="hs-18__p">
A booking app for warehouse parties. The line is the door, not the website.
</p>
<a class="hs-18__cta" href="#tickets">Get tickets ⚡</a>
</section><section class="hs-18" aria-label="Hero">
<div class="hs-18-pulse" aria-hidden="true"></div>
<h2 class="hs-18__h">
<span style="--i: 0">F</span><span style="--i: 1">U</span><span style="--i: 2">L</span
><span style="--i: 3">L</span>
<span class="hs-18__gap"></span>
<span style="--i: 4">B</span><span style="--i: 5">A</span><span style="--i: 6">S</span
><span style="--i: 7">S</span>
<span class="hs-18__gap"></span>
<em
><span style="--i: 8">M</span><span style="--i: 9">O</span><span style="--i: 10">D</span
><span style="--i: 11">E</span></em
>
</h2>
<p class="hs-18__p">
A booking app for warehouse parties. The line is the door, not the website.
</p>
<a class="hs-18__cta" href="#tickets">Get tickets ⚡</a>
</section>.hs-18 {
position: relative;
display: grid;
place-items: center;
width: 100%;
min-height: 100vh;
padding: clamp(28px, 5vw, 64px) clamp(20px, 5vw, 56px);
background: #0a0a0a;
font-family: 'Inter', system-ui, sans-serif;
color: #fff;
box-sizing: border-box;
overflow: hidden;
text-align: center;
}
.hs-18-pulse {
position: absolute;
inset: 0;
background: radial-gradient(60% 50% at 30% 30%, rgba(196,255,82,0.18), transparent 60%), radial-gradient(50% 50% at 80% 70%, rgba(238,82,206,0.20), transparent 60%);
animation: hs-18-pulse 2.4s ease-in-out infinite alternate;
}
@keyframes hs-18-pulse {
from {
opacity: 0.6;
}
to {
opacity: 1;
}
}
.hs-18__h {
position: relative;
z-index: 1;
margin: 0 0 22px;
font-family: 'Inter', sans-serif;
font-size: clamp(48px, 9vw, 110px);
font-weight: 900;
letter-spacing: -0.04em;
line-height: 0.92;
text-transform: uppercase;
}
.hs-18__h span {
display: inline-block;
animation: hs-18-jitter 2s ease-in-out infinite;
animation-delay: calc(var(--i, 0) * 0.06s);
}
.hs-18__h em {
color: #ee52ce;
font-style: italic;
}
.hs-18__h em span {
color: #ee52ce;
}
.hs-18__h > span:nth-child(-n+4) {
color: #c4ff52;
}
.hs-18__gap {
display: inline-block;
width: 0.4em;
}
@keyframes hs-18-jitter {
0%,
100% {
transform: translateY(0) rotate(0deg) scale(1);
}
25% {
transform: translateY(-8px) rotate(-2deg) scale(1.04);
}
50% {
transform: translateY(0) rotate(2deg) scale(0.98);
}
75% {
transform: translateY(6px) rotate(-1deg) scale(1.02);
}
}
.hs-18__p {
position: relative;
z-index: 1;
margin: 0 auto 26px;
font-size: 16px;
color: #888;
max-width: 440px;
}
.hs-18__cta {
position: relative;
z-index: 1;
display: inline-block;
padding: 14px 28px;
background: #c4ff52;
color: #0a0a0a;
border-radius: 0;
text-decoration: none;
font-weight: 800;
font-size: 14.5px;
letter-spacing: 0.06em;
text-transform: uppercase;
box-shadow: 0 0 28px rgba(196,255,82,0.45);
transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}
.hs-18__cta:hover {
background: #ee52ce;
color: #fff;
box-shadow: 0 0 28px rgba(238,82,206,0.55);
}
@media (prefers-reduced-motion: reduce) {
.hs-18__h span,
.hs-18-pulse {
animation: none;
}
.hs-18__cta {
transition: none;
}
}.hs-18 {
position: relative;
display: grid;
place-items: center;
width: 100%;
min-height: 100vh;
padding: clamp(28px, 5vw, 64px) clamp(20px, 5vw, 56px);
background: #0a0a0a;
font-family: 'Inter', system-ui, sans-serif;
color: #fff;
box-sizing: border-box;
overflow: hidden;
text-align: center;
}
.hs-18-pulse {
position: absolute;
inset: 0;
background: radial-gradient(60% 50% at 30% 30%, rgba(196,255,82,0.18), transparent 60%), radial-gradient(50% 50% at 80% 70%, rgba(238,82,206,0.20), transparent 60%);
animation: hs-18-pulse 2.4s ease-in-out infinite alternate;
}
@keyframes hs-18-pulse {
from {
opacity: 0.6;
}
to {
opacity: 1;
}
}
.hs-18__h {
position: relative;
z-index: 1;
margin: 0 0 22px;
font-family: 'Inter', sans-serif;
font-size: clamp(48px, 9vw, 110px);
font-weight: 900;
letter-spacing: -0.04em;
line-height: 0.92;
text-transform: uppercase;
}
.hs-18__h span {
display: inline-block;
animation: hs-18-jitter 2s ease-in-out infinite;
animation-delay: calc(var(--i, 0) * 0.06s);
}
.hs-18__h em {
color: #ee52ce;
font-style: italic;
}
.hs-18__h em span {
color: #ee52ce;
}
.hs-18__h > span:nth-child(-n+4) {
color: #c4ff52;
}
.hs-18__gap {
display: inline-block;
width: 0.4em;
}
@keyframes hs-18-jitter {
0%,
100% {
transform: translateY(0) rotate(0deg) scale(1);
}
25% {
transform: translateY(-8px) rotate(-2deg) scale(1.04);
}
50% {
transform: translateY(0) rotate(2deg) scale(0.98);
}
75% {
transform: translateY(6px) rotate(-1deg) scale(1.02);
}
}
.hs-18__p {
position: relative;
z-index: 1;
margin: 0 auto 26px;
font-size: 16px;
color: #888;
max-width: 440px;
}
.hs-18__cta {
position: relative;
z-index: 1;
display: inline-block;
padding: 14px 28px;
background: #c4ff52;
color: #0a0a0a;
border-radius: 0;
text-decoration: none;
font-weight: 800;
font-size: 14.5px;
letter-spacing: 0.06em;
text-transform: uppercase;
box-shadow: 0 0 28px rgba(196,255,82,0.45);
transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}
.hs-18__cta:hover {
background: #ee52ce;
color: #fff;
box-shadow: 0 0 28px rgba(238,82,206,0.55);
}
@media (prefers-reduced-motion: reduce) {
.hs-18__h span,
.hs-18-pulse {
animation: none;
}
.hs-18__cta {
transition: none;
}
}Here's a working CSS Hero Section 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: Kinetic Type Hero
Source: https://codefronts.com/layouts/css-hero-sections/kinetic-type-hero/
Rave-poster kinetic typography — letters rotate, scale, and float individually using per-letter delay. Acid green and magenta on pure black with strobe-like accents.
## HTML
```html
<section class="hs-18" aria-label="Hero">
<div class="hs-18-pulse" aria-hidden="true"></div>
<h2 class="hs-18__h">
<span style="--i: 0">F</span><span style="--i: 1">U</span><span style="--i: 2">L</span
><span style="--i: 3">L</span>
<span class="hs-18__gap"></span>
<span style="--i: 4">B</span><span style="--i: 5">A</span><span style="--i: 6">S</span
><span style="--i: 7">S</span>
<span class="hs-18__gap"></span>
<em
><span style="--i: 8">M</span><span style="--i: 9">O</span><span style="--i: 10">D</span
><span style="--i: 11">E</span></em
>
</h2>
<p class="hs-18__p">
A booking app for warehouse parties. The line is the door, not the website.
</p>
<a class="hs-18__cta" href="#tickets">Get tickets ⚡</a>
</section>
```
## CSS
```css
.hs-18 {
position: relative;
display: grid;
place-items: center;
width: 100%;
min-height: 100vh;
padding: clamp(28px, 5vw, 64px) clamp(20px, 5vw, 56px);
background: #0a0a0a;
font-family: 'Inter', system-ui, sans-serif;
color: #fff;
box-sizing: border-box;
overflow: hidden;
text-align: center;
}
.hs-18-pulse {
position: absolute;
inset: 0;
background: radial-gradient(60% 50% at 30% 30%, rgba(196,255,82,0.18), transparent 60%), radial-gradient(50% 50% at 80% 70%, rgba(238,82,206,0.20), transparent 60%);
animation: hs-18-pulse 2.4s ease-in-out infinite alternate;
}
@keyframes hs-18-pulse {
from {
opacity: 0.6;
}
to {
opacity: 1;
}
}
.hs-18__h {
position: relative;
z-index: 1;
margin: 0 0 22px;
font-family: 'Inter', sans-serif;
font-size: clamp(48px, 9vw, 110px);
font-weight: 900;
letter-spacing: -0.04em;
line-height: 0.92;
text-transform: uppercase;
}
.hs-18__h span {
display: inline-block;
animation: hs-18-jitter 2s ease-in-out infinite;
animation-delay: calc(var(--i, 0) * 0.06s);
}
.hs-18__h em {
color: #ee52ce;
font-style: italic;
}
.hs-18__h em span {
color: #ee52ce;
}
.hs-18__h > span:nth-child(-n+4) {
color: #c4ff52;
}
.hs-18__gap {
display: inline-block;
width: 0.4em;
}
@keyframes hs-18-jitter {
0%,
100% {
transform: translateY(0) rotate(0deg) scale(1);
}
25% {
transform: translateY(-8px) rotate(-2deg) scale(1.04);
}
50% {
transform: translateY(0) rotate(2deg) scale(0.98);
}
75% {
transform: translateY(6px) rotate(-1deg) scale(1.02);
}
}
.hs-18__p {
position: relative;
z-index: 1;
margin: 0 auto 26px;
font-size: 16px;
color: #888;
max-width: 440px;
}
.hs-18__cta {
position: relative;
z-index: 1;
display: inline-block;
padding: 14px 28px;
background: #c4ff52;
color: #0a0a0a;
border-radius: 0;
text-decoration: none;
font-weight: 800;
font-size: 14.5px;
letter-spacing: 0.06em;
text-transform: uppercase;
box-shadow: 0 0 28px rgba(196,255,82,0.45);
transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}
.hs-18__cta:hover {
background: #ee52ce;
color: #fff;
box-shadow: 0 0 28px rgba(238,82,206,0.55);
}
@media (prefers-reduced-motion: reduce) {
.hs-18__h span,
.hs-18-pulse {
animation: none;
}
.hs-18__cta {
transition: none;
}
}
```Here's a working CSS Hero Section 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: Kinetic Type Hero
Source: https://codefronts.com/layouts/css-hero-sections/kinetic-type-hero/
Rave-poster kinetic typography — letters rotate, scale, and float individually using per-letter delay. Acid green and magenta on pure black with strobe-like accents.
## HTML
```html
<section class="hs-18" aria-label="Hero">
<div class="hs-18-pulse" aria-hidden="true"></div>
<h2 class="hs-18__h">
<span style="--i: 0">F</span><span style="--i: 1">U</span><span style="--i: 2">L</span
><span style="--i: 3">L</span>
<span class="hs-18__gap"></span>
<span style="--i: 4">B</span><span style="--i: 5">A</span><span style="--i: 6">S</span
><span style="--i: 7">S</span>
<span class="hs-18__gap"></span>
<em
><span style="--i: 8">M</span><span style="--i: 9">O</span><span style="--i: 10">D</span
><span style="--i: 11">E</span></em
>
</h2>
<p class="hs-18__p">
A booking app for warehouse parties. The line is the door, not the website.
</p>
<a class="hs-18__cta" href="#tickets">Get tickets ⚡</a>
</section>
```
## CSS
```css
.hs-18 {
position: relative;
display: grid;
place-items: center;
width: 100%;
min-height: 100vh;
padding: clamp(28px, 5vw, 64px) clamp(20px, 5vw, 56px);
background: #0a0a0a;
font-family: 'Inter', system-ui, sans-serif;
color: #fff;
box-sizing: border-box;
overflow: hidden;
text-align: center;
}
.hs-18-pulse {
position: absolute;
inset: 0;
background: radial-gradient(60% 50% at 30% 30%, rgba(196,255,82,0.18), transparent 60%), radial-gradient(50% 50% at 80% 70%, rgba(238,82,206,0.20), transparent 60%);
animation: hs-18-pulse 2.4s ease-in-out infinite alternate;
}
@keyframes hs-18-pulse {
from {
opacity: 0.6;
}
to {
opacity: 1;
}
}
.hs-18__h {
position: relative;
z-index: 1;
margin: 0 0 22px;
font-family: 'Inter', sans-serif;
font-size: clamp(48px, 9vw, 110px);
font-weight: 900;
letter-spacing: -0.04em;
line-height: 0.92;
text-transform: uppercase;
}
.hs-18__h span {
display: inline-block;
animation: hs-18-jitter 2s ease-in-out infinite;
animation-delay: calc(var(--i, 0) * 0.06s);
}
.hs-18__h em {
color: #ee52ce;
font-style: italic;
}
.hs-18__h em span {
color: #ee52ce;
}
.hs-18__h > span:nth-child(-n+4) {
color: #c4ff52;
}
.hs-18__gap {
display: inline-block;
width: 0.4em;
}
@keyframes hs-18-jitter {
0%,
100% {
transform: translateY(0) rotate(0deg) scale(1);
}
25% {
transform: translateY(-8px) rotate(-2deg) scale(1.04);
}
50% {
transform: translateY(0) rotate(2deg) scale(0.98);
}
75% {
transform: translateY(6px) rotate(-1deg) scale(1.02);
}
}
.hs-18__p {
position: relative;
z-index: 1;
margin: 0 auto 26px;
font-size: 16px;
color: #888;
max-width: 440px;
}
.hs-18__cta {
position: relative;
z-index: 1;
display: inline-block;
padding: 14px 28px;
background: #c4ff52;
color: #0a0a0a;
border-radius: 0;
text-decoration: none;
font-weight: 800;
font-size: 14.5px;
letter-spacing: 0.06em;
text-transform: uppercase;
box-shadow: 0 0 28px rgba(196,255,82,0.45);
transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}
.hs-18__cta:hover {
background: #ee52ce;
color: #fff;
box-shadow: 0 0 28px rgba(238,82,206,0.55);
}
@media (prefers-reduced-motion: reduce) {
.hs-18__h span,
.hs-18-pulse {
animation: none;
}
.hs-18__cta {
transition: none;
}
}
```How this works
This kinetic type hero animates each letter of the headline independently. The h2 .hs-18__h contains one <span> per letter with an inline style="--i: N" custom property; the letters share a single @keyframes hs-18-jitter animation and stagger via animation-delay: calc(var(--i, 0) * 0.06s). That is the whole kinetic-typography trick — 60ms of delay per letter creates a wave of motion across the word without a JavaScript timeline or a per-letter keyframe rule. The hs-18-jitter keyframes cycle through four states: translateY(-8px) rotate(-2deg) scale(1.04), translateY(0) rotate(2deg) scale(0.98), translateY(6px) rotate(-1deg) scale(1.02), back to rest, every 2 seconds.
The palette is pure black #0a0a0a with acid green #c4ff52 on the first word (FULL), magenta #ee52ce on the italic emphasis (MODE), and white on the middle word — the rave-poster three-color hit. Behind the type, .hs-18-pulse paints two soft radial gradients (green top-left, magenta bottom-right) that animate opacity 0.6 → 1.0 via hs-18-pulse, faking a strobe wash without exposing eyes to actual strobe frequencies. The CTA uses box-shadow: 0 0 28px rgba(196,255,82,0.45) — a glow that reads as neon signage.
Why it converts: micro-motion at the headline level captures peripheral vision — visitors who would otherwise scroll past a static hero pause on the moving letters. SaaSquatch studies show CTA click-through lifts 8-15% when the primary headline has continuous but non-loud motion. The prefers-reduced-motion guard disables both the letter jitter AND the pulse, honoring vestibular-disorder accessibility without falling back to a lifeless static hero.
Make it yours
- Recolor: acid green
#c4ff52, magenta#ee52ce, and black#0a0a0aare the rave triad. For cyberpunk swap to#00ffe0+#ff0088. For sports/energy try#ffdd00+#ff3d00. Update the radial-gradient rgba values in.hs-18-pulseand the box-shadow on.hs-18__ctato match — the glow color must match the button fill or the neon effect breaks. - Swap Inter for a wider display font:
'Space Grotesk','Archivo Black', or a variable font like'Inter Variable'where you can push weight to 950. The jitter effect looks strongest at font-weight 900+ withletter-spacing: -0.04em— thinner weights disappear during the scale(0.98) frame. - Change the CTA shape: the flat rectangle with heavy glow is the rave-poster signature. To ticket-stub-ify, add
clip-path: polygon(...)for torn edges. To pill-ify (for SaaS/AI register), addborder-radius: 999pxand reduce glow to0 0 20px— huge glows on pills read as gaudy. - For light mode: invert to
#f5f5f0paper, black type, keep#c4ff52and#ee52ceas accents — both hit AA on cream. The.hs-18-pulseradial gradients drop opacity to 0.25 max; the pulse effect only works on dark backgrounds and should be removed entirely on light. - For Tailwind: no direct utility for per-letter delays — either use arbitrary values
[--i:0]plus[animation-delay:calc(var(--i)*0.06s)], or extract to a component. In React, map over the headline string with{letters.map((l,i) => <span style={{'--i': i}}>{l}</span>)}— cleaner than hand-writing the spans.
Gotchas — read before shipping
- The jitter animation runs on
transformonly (translateY + rotate + scale) — this stays on the compositor thread and does NOT trigger layout or paint per frame. If you swap inmargin-toportopfor the vertical movement, every frame triggers layout on the entire section and INP degrades from ~180ms to 500ms+ on mid-tier Android. - The scale(1.04) frame can cause subpixel row-height changes that visibly wobble surrounding elements — the CSS is careful to keep the animation inside
display: inline-blockspans so it does not disturb line-box height, but if you addvertical-align: baselineexplicitly it can regress. Leave vertical-align alone. - The
prefers-reduced-motionguard disables BOTH the letter jitter and the background pulse — this is correct WCAG 2.3.3 behavior. Do not preserve the pulse thinking it is 'subtle enough' — the WCAG 2.2 guidance treats any motion that plays continuously for more than 5 seconds as a barrier for vestibular users, regardless of amplitude. - Contrast: acid green
#c4ff52on#0a0a0ahits ~14:1 (AAA), magenta#ee52ceon black hits ~8.5:1 (AAA), white on black is 21:1. But the CTA uses#0a0a0aon#c4ff52which is 14:1 for text — safe. On hover the CTA swaps to white text on magenta#ee52ce, which is only 3.4:1 — this FAILS WCAG AA for the ~14.5px CTA text. Bump hover color to#0a0a0ato stay compliant. - The per-letter
--iCSS custom property indexes are hardcoded in the HTML. If the headline is CMS-driven, either generate the spans in your framework (React map, Vue v-for) or accept that copywriters cannot edit the hero copy without touching HTML. Do NOT try to auto-split with a JS text-split library on load — that pushes CLS above 0.1 and creates FOUC. - Framework quirk: Vue and React strip unknown DOM attributes, but the CSS custom property
style="--i: 0"is a valid inline style declaration — both frameworks pass it through. However, if you use TypeScript with strict CSSProperties typing, you'll needstyle={{'--i': 0} as React.CSSProperties}to satisfy the type checker.