Scroll Assemble

Entrance & Kinetic · Animated · pure CSS

The word assembles letter-by-letter under your scroll: every glyph rides the same view() timeline but with its own staggered animation-range window, so letters rise, fade and untilt into place left-to-right as you scroll down, and come apart right-to-left as you scroll back up. A fully reversible, reader-scrubbed entrance in pure CSS with no JavaScript.

Scroll Assemble

How it works

Scroll Assemble is an animated entrance & kinetic text effect rendered entirely in CSS. Each character is wrapped in its own span so it can animate independently — the HTML and JSX exports include that per-letter markup. It is driven by a scroll-linked animation timeline.

Controls

Scroll Assemble exposes 3 dedicated controls — Stagger, Rise and Rotate — on top of the shared type controls (font, weight, letter-spacing and case). Open it in the generator to tune every value live, then copy the updated CSS.

CSS

/* Scroll Assemble — made with TEXT-FX · https://text-fx.app
 * HTML: each character is wrapped in a <span> — see the HTML export; uses scroll-driven animation.
 * Font: 'Unbounded', sans-serif (load from Google Fonts).
 */

.text-effect {
  font-family: 'Unbounded', sans-serif;
  font-weight: 700;
  letter-spacing: -2px;
  text-transform: none;
}

.text-effect {
  color: hsl(265 85% 72%);
  white-space: pre;
}
.text-effect .fx-ch {
  display: inline-block;
  text-shadow: 0 0 1px hsl(265 90% 82% / 0.5), 0 4px 22px hsl(285 90% 62% / 0.28);
}

@supports (animation-timeline: view()) {
  .text-effect .fx-ch {
    animation: text-effect-assemble linear both;
    animation-timeline: view();
    will-change: opacity, transform, filter;
  }
  .text-effect .fx-ch:nth-child(1) { animation-range: cover 5% cover 15.95%; }
  .text-effect .fx-ch:nth-child(2) { animation-range: cover 9.26% cover 20.2%; }
  .text-effect .fx-ch:nth-child(3) { animation-range: cover 13.51% cover 24.46%; }
  .text-effect .fx-ch:nth-child(4) { animation-range: cover 17.77% cover 28.72%; }
  .text-effect .fx-ch:nth-child(5) { animation-range: cover 22.03% cover 32.97%; }
  .text-effect .fx-ch:nth-child(6) { animation-range: cover 26.28% cover 37.23%; }
  .text-effect .fx-ch:nth-child(7) { animation-range: cover 30.54% cover 41.49%; }
  .text-effect .fx-ch:nth-child(8) { animation-range: cover 34.8% cover 45.74%; }
  .text-effect .fx-ch:nth-child(9) { animation-range: cover 39.05% cover 50%; }
}

@keyframes text-effect-assemble {
  from { opacity: 0; transform: translateY(29px) rotate(7deg); filter: blur(2.5px); }
  to   { opacity: 1; transform: translateY(0) rotate(0deg); filter: blur(0); }
}

HTML

This effect needs the markup below (per-letter spans, SVG defs, or a data-text attribute).

<!-- Made with TEXT-FX · https://text-fx.app -->

<style>
.text-effect {
  font-family: 'Unbounded', sans-serif;
  font-weight: 700;
  letter-spacing: -2px;
  text-transform: none;
}

.text-effect {
  color: hsl(265 85% 72%);
  white-space: pre;
}
.text-effect .fx-ch {
  display: inline-block;
  text-shadow: 0 0 1px hsl(265 90% 82% / 0.5), 0 4px 22px hsl(285 90% 62% / 0.28);
}

@supports (animation-timeline: view()) {
  .text-effect .fx-ch {
    animation: text-effect-assemble linear both;
    animation-timeline: view();
    will-change: opacity, transform, filter;
  }
  .text-effect .fx-ch:nth-child(1) { animation-range: cover 5% cover 15.95%; }
  .text-effect .fx-ch:nth-child(2) { animation-range: cover 9.26% cover 20.2%; }
  .text-effect .fx-ch:nth-child(3) { animation-range: cover 13.51% cover 24.46%; }
  .text-effect .fx-ch:nth-child(4) { animation-range: cover 17.77% cover 28.72%; }
  .text-effect .fx-ch:nth-child(5) { animation-range: cover 22.03% cover 32.97%; }
  .text-effect .fx-ch:nth-child(6) { animation-range: cover 26.28% cover 37.23%; }
  .text-effect .fx-ch:nth-child(7) { animation-range: cover 30.54% cover 41.49%; }
  .text-effect .fx-ch:nth-child(8) { animation-range: cover 34.8% cover 45.74%; }
  .text-effect .fx-ch:nth-child(9) { animation-range: cover 39.05% cover 50%; }
}

@keyframes text-effect-assemble {
  from { opacity: 0; transform: translateY(29px) rotate(7deg); filter: blur(2.5px); }
  to   { opacity: 1; transform: translateY(0) rotate(0deg); filter: blur(0); }
}
</style>

<div class="text-effect"><span class="fx-ch" style="--i:0;--n:9;--rev:8;--mid:4">Y</span><span class="fx-ch" style="--i:1;--n:9;--rev:7;--mid:4">o</span><span class="fx-ch" style="--i:2;--n:9;--rev:6;--mid:4">u</span><span class="fx-ch" style="--i:3;--n:9;--rev:5;--mid:4">r</span><span class="fx-ch" style="--i:4;--n:9;--rev:4;--mid:4"> </span><span class="fx-ch" style="--i:5;--n:9;--rev:3;--mid:4">t</span><span class="fx-ch" style="--i:6;--n:9;--rev:2;--mid:4">e</span><span class="fx-ch" style="--i:7;--n:9;--rev:1;--mid:4">x</span><span class="fx-ch" style="--i:8;--n:9;--rev:0;--mid:4">t</span></div>
Category
Entrance & Kinetic
Type
Animated
Browser support
Scroll-scrubbed in Chromium & Safari; static elsewhere
Capabilities
perLetter, scroll

Related Entrance & Kinetic effects