20 CSS Gradient Text Designs15 / 20

CSS + JSMIT licensed

CSS Scroll Gradient Text Reveal on Scroll

Each section's headline fades in from below with a gradient fill sliding from washed-out to vivid, triggered by IntersectionObserver as the element enters the viewport.

Published

Live Demo
Try it

The code

<div class="gt-15">
  <div class="gt-15__top">
    <p class="gt-15__hint">Scroll to reveal gradient text</p>
    <span class="gt-15__arrow">↓</span>
  </div>
  <div class="gt-15__section">
    <div class="gt-15__reveal">DISCOVER</div>
  </div>
  <div class="gt-15__section">
    <div class="gt-15__reveal">YOUR</div>
  </div>
  <div class="gt-15__section">
    <div class="gt-15__reveal">POTENTIAL</div>
  </div>
  <div class="gt-15__section">
    <div class="gt-15__reveal">THROUGH COLOUR</div>
  </div>
</div>
.gt-15,
.gt-15 *,
.gt-15 *::before,
.gt-15 *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.gt-15 {
  --bg: #0c0c0c;
  font-family: 'Syne', sans-serif;
  background: var(--bg);
  min-height: 300vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 2rem;
}

.gt-15__top {
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
}

.gt-15__hint {
  font-size: .8rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: #555;
  animation: gt-15-bounce 2s ease-in-out infinite;
}

.gt-15__arrow {
  font-size: 1.5rem;
  color: #444;
  animation: gt-15-bounce 2s ease-in-out infinite .3s;
}

.gt-15__section {
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 900px;
}

.gt-15__reveal {
  font-size: clamp(3rem, 10vw, 7rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.01em;
  color: #2a2a2a;
  background: linear-gradient(90deg, #ff6b6b, #ffd93d, #6bcb77, #4d96ff);
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  opacity: 0;
  transform: translateY(40px);
  transition: opacity .8s ease, transform .8s ease;
  background-position: 100% center;
}

.gt-15__reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
  background-position: 0% center;
  transition: opacity .8s ease, transform .8s ease, background-position 1.2s ease;
}

.gt-15__reveal:nth-child(1) {
  transition-delay: 0s;
}

.gt-15__reveal:nth-child(2) {
  transition-delay: .1s;
}

@keyframes gt-15-bounce {
  0%,
    100% {
    transform: translateY(0);
  }

  50% {
    transform: translateY(8px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .gt-15__reveal {
    opacity: 1;
    transform: none;
    transition: none;
    background-position: 0% center;
  }

  .gt-15__hint,
    .gt-15__arrow {
    animation: none;
  }
}
(function() {
  const items = document.querySelectorAll('.gt-15 .gt-15__reveal');
  if (!items.length) return;
  const io = new IntersectionObserver((entries) => {
    entries.forEach(e => {
      if (e.isIntersecting) {
        e.target.classList.add('is-visible');
      }
    });
  }, { threshold: 0.3 });
  items.forEach(el => io.observe(el));
})();
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Gradient Text Design 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 Scroll Gradient Text Reveal on Scroll
Source: https://codefronts.com/design-styles/css-gradient-text/css-scroll-gradient-text-reveal-on-scroll/

Each section's headline fades in from below with a gradient fill sliding from washed-out to vivid, triggered by IntersectionObserver as the element enters the viewport.
## HTML
```html
<div class="gt-15">
  <div class="gt-15__top">
    <p class="gt-15__hint">Scroll to reveal gradient text</p>
    <span class="gt-15__arrow">↓</span>
  </div>
  <div class="gt-15__section">
    <div class="gt-15__reveal">DISCOVER</div>
  </div>
  <div class="gt-15__section">
    <div class="gt-15__reveal">YOUR</div>
  </div>
  <div class="gt-15__section">
    <div class="gt-15__reveal">POTENTIAL</div>
  </div>
  <div class="gt-15__section">
    <div class="gt-15__reveal">THROUGH COLOUR</div>
  </div>
</div>
```
## CSS
```css
.gt-15,
.gt-15 *,
.gt-15 *::before,
.gt-15 *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.gt-15 {
  --bg: #0c0c0c;
  font-family: 'Syne', sans-serif;
  background: var(--bg);
  min-height: 300vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 2rem;
}

.gt-15__top {
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
}

.gt-15__hint {
  font-size: .8rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: #555;
  animation: gt-15-bounce 2s ease-in-out infinite;
}

.gt-15__arrow {
  font-size: 1.5rem;
  color: #444;
  animation: gt-15-bounce 2s ease-in-out infinite .3s;
}

.gt-15__section {
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 900px;
}

.gt-15__reveal {
  font-size: clamp(3rem, 10vw, 7rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.01em;
  color: #2a2a2a;
  background: linear-gradient(90deg, #ff6b6b, #ffd93d, #6bcb77, #4d96ff);
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  opacity: 0;
  transform: translateY(40px);
  transition: opacity .8s ease, transform .8s ease;
  background-position: 100% center;
}

.gt-15__reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
  background-position: 0% center;
  transition: opacity .8s ease, transform .8s ease, background-position 1.2s ease;
}

.gt-15__reveal:nth-child(1) {
  transition-delay: 0s;
}

.gt-15__reveal:nth-child(2) {
  transition-delay: .1s;
}

@keyframes gt-15-bounce {
  0%,
    100% {
    transform: translateY(0);
  }

  50% {
    transform: translateY(8px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .gt-15__reveal {
    opacity: 1;
    transform: none;
    transition: none;
    background-position: 0% center;
  }

  .gt-15__hint,
    .gt-15__arrow {
    animation: none;
  }
}
```

## JavaScript
```js
(function() {
  const items = document.querySelectorAll('.gt-15 .gt-15__reveal');
  if (!items.length) return;
  const io = new IntersectionObserver((entries) => {
    entries.forEach(e => {
      if (e.isIntersecting) {
        e.target.classList.add('is-visible');
      }
    });
  }, { threshold: 0.3 });
  items.forEach(el => io.observe(el));
})();
```

How this works

Headline elements start with opacity: 0, transform: translateY(40px), and background-position: 100% center — the gradient is off-screen to the right, appearing washed out. An IntersectionObserver with threshold: 0.3 watches each element and adds the .is-visible class when 30% of the element enters the viewport.

The .is-visible rule transitions opacity and transform in 0.8s, and simultaneously transitions background-position from 100% to 0% in 1.2s — a slightly longer duration so the colour washes in after the element has settled into place, creating a two-stage reveal effect.

Make it yours

  • Change the threshold to 0.1 for an earlier trigger — the gradient starts washing in as soon as 10% of the element is visible.
  • Add io.unobserve(e.target) after adding is-visible to fire the animation only once per element rather than every time it enters the viewport.
  • Adjust translateY(40px) to translateX(-40px) for a horizontal slide-in that differs from the standard vertical reveal.

Gotchas — read before shipping

  • background-position transition requires the element to have an explicit background-size set; the default auto size makes the position animation non-functional in some browsers.
  • IntersectionObserver does not fire for elements already in the viewport at page load on some browsers — call io.observe after a requestAnimationFrame delay if initial elements fail to reveal.
  • prefers-reduced-motion should immediately set opacity: 1 and transform: none without transition, so content is accessible to users with vestibular disorders even when JavaScript is active.

Browser support

ChromeSafariFirefoxEdge
58+12.1+55+58+

IntersectionObserver is well-supported in all modern browsers; a simple scroll-event fallback can serve IE11.

Techniques used in this demo

Search CodeFronts

Loading…