Scalable Text Reveal Animations in Webflow (Custom Element)

Intermediate 31:00 webflowcustom-elementtext-revealanimationinteractions

Build reusable text reveal animations in Webflow with the Custom Element — one action, per-letter stagger via inline CSS, and multi-line support built in.

Key takeaways

  • The Custom Element lets you define your own semantic tag (h2, span) right on the canvas — so you can build the exact nested markup a text reveal needs, natively, without a code embed.
  • The whole animation collapses into one action plus one initial condition, because the real motion lives in a transform transition on the span class — not in the timeline.
  • The stagger comes from an inline style attribute (transition-delay) on each letter, not from timeline delays — which is exactly why the setup scales without ever reopening the animation.
  • A line wrapper with overflow: hidden plus a span wrapper you push down 100% is what masks the letters; the single action moves them back up by -100% to reveal them.
  • Multi-line is essentially free: duplicate the line wrapper, reuse the same animation, and only tweak the delays — and it generates far less code than the old span-per-letter way.

Video chapters

  1. 00:00 Intro
  2. 01:07 What is a Webflow Custom Element
  3. 01:56 Text reveal animation: the old way
  4. 12:07 Drawbacks of the old way
  5. 13:03 The new way: single-line heading
  6. 22:01 Advantages of the new way
  7. 23:23 The new way: multi-line heading
  8. 24:35 The new way: button on hover
  9. 30:21 Outro

You can build a text reveal animation in Webflow that’s reusable, scalable, and easy to apply — to a single-line heading, a multi-line heading, or a button on hover — with a single action inside the animation timeline. The trick is to stop fighting Webflow’s native elements and reach for one you might have overlooked: the Custom Element.

I’ll start with the “old way” first, because it’s the version most people build, and it’s worth seeing exactly where it falls apart. Then I’ll rebuild the same effect the new way. When I timed myself, the old way took over six minutes per heading; the new way took under four — and, more importantly, the new one scales to longer and multi-line headings with almost no extra work.

The whole idea rests on a small shift in thinking: let a CSS transition do the animating, and let the Custom Element carry the per-letter timing as inline CSS. Once that clicks, the Webflow timeline shrinks to a single action.

How it works

The old way, and why it hurts. The classic setup masks a heading (overflow: hidden), splits it into one span per letter, sets each span to display: inline-block so it can take a transform, then builds a scroll-into-view interaction that moves every letter up with a hand-tuned delay. It works, but each letter needs its own action, longer headings force you back into the timeline to add more, and native Webflow headings can’t do multi-line reveals at all without an accessibility-hurting hack. There’s also a Webflow quirk worth knowing: two actions created from the same span get linked to the same target, so you build the initial condition from the last span to keep them independent.

The Custom Element. A Custom Element is a placeholder you drop on the canvas (Ctrl/Cmd + E → “custom element”) and then assign any HTML tag to via the settings panel. Set its Tag to h2 and it is an h2. Set another’s Tag to span and it becomes a span you can also give a Text value to. Crucially, a Custom Element accepts any custom attribute — including style, which lets you inject inline CSS. That single capability is what makes the new approach possible.

Why the animation shrinks to one action. In the new setup, the shared span class carries a transform transition (700ms, ease-out-cubic). Webflow animations act on CSS properties, so when your timeline changes a property that already has a matching transition, the transition does the animating — you only specify the end value, not a duration or easing. The stagger, meanwhile, comes from a transition-delay set individually on each span through its inline style attribute (0, 100, 200… milliseconds). So the timeline needs just an initial condition (push the span wrapper down to hide the letters) and one action (move the letters up by -100%). Everything else is CSS.

How to use it

  1. Build the heading with Custom Elements. Add a Custom Element and set its Tag to h2. Inside it, add a div block as a heading line and give that overflow: hidden (it’s the mask). Inside the line, add another div as the span wrapper. Inside the wrapper, add a Custom Element per letter (and per blank space), setting each one’s Tag to span and its Text to the letter it represents.

  2. Style the spans once. Apply a single base class to every span with display: inline-block (so transforms apply) and a transform transition of 700ms, ease-out-cubic. No combo classes per letter this time — one shared class is enough.

  3. Add the per-letter timing as inline CSS. On each span’s style attribute, set transition-delay to a stepped value — 0ms, 100ms, 200ms, and so on. This is the stagger, and it lives entirely outside the timeline.

  4. Build the one-action animation. Select the heading, add a Scroll into view interaction. Initial condition: move the span wrapper down by 100% (it hides below the overflow-hidden line). Then one action: move the spans up by -100%. No duration, no delay, no easing on the action itself — the transition and the inline delays handle all of it.

  5. Extend to multi-line. Duplicate the heading line (wrapper + spans) for each additional line, and reuse the same animation untouched. Only adjust the transition-delay values — e.g. offset the second line’s letters by 50ms so the two lines cascade.

  6. Adapt for a button-on-hover reveal. Give the button’s text wrapper overflow: hidden and position: relative. Put two copies of the text inside; set the second to position: absolute with a top offset of 100% so it starts hidden below. Split both into delayed spans as above, then add a hover interaction with a single action moving the spans up by -100%, and a hover-out action returning them to 0%.

Note: the exact per-letter markup (every span’s Text value and the full transition-delay sequence for each heading) is set element by element in the Designer rather than pasted as code, so the fastest way to see the precise structure is to clone the project above and inspect it.

Resources

Frequently asked questions

What is a Webflow Custom Element?
It's a placeholder element you can turn into any HTML tag — h2, span, and so on — right on the canvas, and add custom attributes to, including an inline style attribute. It's how you get semantic markup and inline CSS that Webflow doesn't expose through its native elements.
Why is the Custom Element approach better than the old span-per-letter interaction?
The old way targets each letter with its own timeline action and a combo class, which takes several minutes and breaks on long or multi-line headings. The new way needs one action plus an initial condition; the stagger lives in a per-letter transition-delay, so it scales without touching the timeline again.
How do I stagger the letters without adding delays in the timeline?
Give each letter''s span a style attribute of transition-delay with a stepped value (0ms, 100ms, 200ms, and so on). A transform transition on the shared span class runs the actual motion, and the per-span delay staggers when each one starts — no timeline delays required.
Does this work with multi-line headings?
Yes, and that''s the point. You duplicate the line wrapper (the overflow: hidden div holding the span wrapper) for each line and reuse the exact same animation. You only adjust the transition-delay values — for example, offsetting the second line by 50ms so the lines cascade.
Can I reuse the technique for a button hover reveal?
Yes. Use an overflow: hidden text wrapper set to position: relative, two stacked copies of the text (the second absolutely positioned with a top offset of 100% so it hides below), spans carrying the transition-delay, and a single hover action that moves the spans up by -100% — with a hover-out action returning them to 0%.

← Back to the course