Text Reveal
<quiet-text-reveal>
Reveals text with a per-character animation effect.
Text reveals animate text into view one character at a time, adding a polished entrance effect to headings, callouts, and other text content.
<div id="text-reveal__overview"> <!-- The text to reveal --> <quiet-text-reveal effect="fade"> Every cat lands on its feet. </quiet-text-reveal> <!-- Demo controls --> <quiet-select label="Effect" value="fade"> <option value="blur">Blur</option> <option value="curtain">Curtain</option> <option value="fade">Fade</option> <option value="fall">Fall</option> <option value="flip">Flip</option> <option value="glow">Glow</option> <option value="rise">Rise</option> <option value="rotate">Rotate</option> <option value="scale">Scale</option> <option value="skew">Skew</option> <option value="slide-down">Slide down</option> <option value="slide-up">Slide up</option> <option value="stretch">Stretch</option> </quiet-select> <quiet-slider label="Duration (ms)" description="How long each character's transition takes." min="100" max="2000" value="600" step="50" with-tooltip class="duration-slider" ></quiet-slider> <quiet-slider label="Stagger (ms)" description="The delay between each character's animation start." min="5" max="100" value="20" step="5" with-tooltip class="stagger-slider" ></quiet-slider> <div class="buttons"> <quiet-button variant="primary" class="replay-btn">Replay</quiet-button> <quiet-button class="reset-btn">Reset</quiet-button> </div> </div> <script type="module"> import { allDefined } from '/dist/quiet.js'; const container = document.getElementById('text-reveal__overview'); const reveal = container.querySelector('quiet-text-reveal'); const effectSelect = container.querySelector('quiet-select'); const durationSlider = container.querySelector('.duration-slider'); const staggerSlider = container.querySelector('.stagger-slider'); const replayButton = container.querySelector('.replay-btn'); const resetButton = container.querySelector('.reset-btn'); await allDefined(); effectSelect.addEventListener('quiet-change', async () => { reveal.effect = effectSelect.value; await reveal.updateComplete; reveal.restart(); }); durationSlider.addEventListener('quiet-input', () => { reveal.duration = durationSlider.value; }); durationSlider.addEventListener('quiet-change', () => { reveal.restart(); }); staggerSlider.addEventListener('quiet-input', () => { reveal.stagger = staggerSlider.value; }); staggerSlider.addEventListener('quiet-change', () => { reveal.restart(); }); replayButton.addEventListener('click', () => { reveal.restart(); }); resetButton.addEventListener('click', async () => { effectSelect.value = 'fade'; durationSlider.value = 600; staggerSlider.value = 20; reveal.effect = 'fade'; reveal.duration = 600; reveal.stagger = 20; await reveal.updateComplete; reveal.restart(); }); </script> <style> #text-reveal__overview { display: flex; flex-direction: column; gap: 1.5rem; quiet-text-reveal { font-size: 2rem; } .buttons { display: flex; gap: 0.5rem; } } </style>
Text reveal honors the user's
prefers-reduced-motion
setting. If you're not seeing animations, this might be why. To override this behavior, which is generally
not recommended, use the ignore-reduced-motion attribute.
Examples Jump to heading
Providing content Jump to heading
Pass the text you want to animate as the element's content. Only the element's textContent is
used. Any HTML tags inside the component will be stripped and only the resulting text will be animated.
<quiet-text-reveal> The text to reveal goes here. </quiet-text-reveal>
If you update the content programmatically, the component will automatically re-animate the new text.
Inheriting text styles Jump to heading
Font styles are inherited from their parent, so you can place them inside headings, paragraphs, links, and other elements and they will match the surrounding text.
Remember that only the element's textContent is used for the animation. Any HTML tags
inside the component will be ignored, and due to the way characters are rendered, some child
elements may not work as expected.
<div id="text-reveal__styles"> <p style="font-family: Georgia, serif; font-size: 2rem; font-style: italic; color: var(--quiet-primary-text-mid);"> <quiet-text-reveal effect="rise"> Elegance is a cat in italics. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__styles'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
You can also use text reveals inside elements such as headings, giving them semantic meaning.
A heading with a reveal
<div id="text-reveal__headings"> <h2 style="margin: 0 0 1.5rem 0;"> <quiet-text-reveal effect="glow"> A heading with a reveal </quiet-text-reveal> </h2> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__headings'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Controlling speed Jump to heading
Use the duration attribute to set how long each character takes to transition, and
stagger to control the delay between each character's start. Both values are in milliseconds.
<div id="text-reveal__slow"> <p style="font-size: 1.5rem;"> <quiet-text-reveal duration="1000" stagger="40"> Slow blink means I love you. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__slow'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
A shorter duration and stagger creates a faster, snappier reveal.
<div id="text-reveal__fast"> <p style="font-size: 1.5rem;"> <quiet-text-reveal duration="300" stagger="10"> The fastest paws in the west. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__fast'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Adding a delay Jump to heading
Use the delay attribute to wait before the animation begins. This is useful when coordinating
with other entrance animations.
<div id="text-reveal__delay"> <p style="font-size: 2rem; font-weight: var(--quiet-font-weight-semibold);"> <quiet-text-reveal delay="800"> Meow. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__delay'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Toggling visibility Jump to heading
Use the reveal() and hide() methods to animate the text in and out
programmatically. The hide() method reverses the stagger so the last character disappears
first.
<div id="text-reveal__toggle"> <p style="font-size: 1.5rem;"> <quiet-text-reveal> Now you see me, now you cat. </quiet-text-reveal> </p> <quiet-button toggle="on">Visible</quiet-button> </div> <script> const container = document.getElementById('text-reveal__toggle'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { if (button.toggle === 'on') { reveal.reveal(); } else { reveal.hide(); } }); </script>
Replaying the animation Jump to heading
Use the restart() method to reset the animation and play it again from the beginning.
<div id="text-reveal__restart"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="scale"> The cat sat back down and stood up again. </quiet-text-reveal> </p> <quiet-button>Restart</quiet-button> </div> <script> const container = document.getElementById('text-reveal__restart'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Starting when in view Jump to heading
Use the start-on-view attribute to begin the animation only when the element scrolls into the
viewport. This is ideal for content further down a page.
<quiet-text-reveal start-on-view style="font-size: 1.5rem;"> A wild cat appears as you scroll into view. </quiet-text-reveal>
Listening for completion Jump to heading
Listen for the quiet-animation-complete event to trigger actions when the animation finishes.
This event fires after both reveal() and hide() complete.
<div id="text-reveal__complete"> <p style="font-size: 1.5rem;"> <quiet-text-reveal> The cat has finished its performance. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__complete'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); reveal.addEventListener('quiet-animation-complete', () => { console.log('Text reveal animation completed!'); }); button.addEventListener('click', () => { reveal.restart(); }); </script>
Effects Jump to heading
Text reveal ships with a variety of built-in effects. Use the effect attribute to choose one.
Each effect can be further customized with CSS custom properties.
Blur Jump to heading
Set effect="blur" for characters that start blurred and slide up into position. You
can customize the blur with --blur-amount (defaults to 12px) and
--translate-y (defaults to 10px).
<div id="text-reveal__blur"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="blur"> Curiosity never stopped the cat. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__blur'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
You can customize the blur with the --blur-amount (defaults to 12px) and
--translate-y (defaults to 10px) custom properties.
<div id="text-reveal__blur-custom"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="blur" style="--blur-amount: 20px; --translate-y: 20px;"> A big stretch before the pounce. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__blur-custom'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Curtain Jump to heading
Set effect="curtain" for characters that are revealed from top to bottom, as if a
curtain is being drawn.
<div id="text-reveal__curtain"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="curtain"> Behind every curtain, a cat. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__curtain'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Fade Jump to heading
This is the default effect, so you can omit the effect attribute.
<div id="text-reveal__fade"> <p style="font-size: 1.5rem;"> <quiet-text-reveal> Silent paws, sharp claws. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__fade'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Fall Jump to heading
Set effect="fall" for characters that fall in from above with a bouncy overshoot, as
if they have weight. You can tune the fall distance with --translate-y, which defaults to
-0.75em.
<div id="text-reveal__fall"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="fall"> Cats always land on their feet. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__fall'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Flip Jump to heading
Set effect="flip" for characters that rotate in on the Y-axis, spinning horizontally
into place. You can adjust the starting angle with --rotate-from, which defaults to
-45deg.
<div id="text-reveal__flip"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="flip"> Spin and land with style. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__flip'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Glow Jump to heading
Set effect="glow" for characters that fade in with a luminous glow that dissipates as
they settle. You can customize it with --glow-radius (defaults to 1em) and
--glow-color (defaults to currentColor).
<div id="text-reveal__glow"> <p style="font-size: 1.5rem; color: var(--quiet-primary-text-mid);"> <quiet-text-reveal effect="glow" style="--glow-color: var(--quiet-primary-fill-mid);"> Eyes that glow in the dark. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__glow'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Rise Jump to heading
Set effect="rise" for characters that rise up from below with a bouncy overshoot, the
opposite of the fall effect. You can tune the distance with --translate-y, which defaults to
0.75em.
<div id="text-reveal__rise"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="rise"> From catnap to conqueror. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__rise'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Rotate Jump to heading
Set effect="rotate" for characters that spin in from a rotated angle with a playful
overshoot. You can adjust the starting rotation with --rotate-from, which defaults to
-45deg.
<div id="text-reveal__rotate"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="rotate"> Chasing tails, catching dreams. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__rotate'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Use --rotate-from to customize the starting angle, such as a full half-turn.
<div id="text-reveal__rotate-custom"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="rotate" style="--rotate-from: -180deg;"> Round and round we go. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__rotate-custom'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Scale Jump to heading
Set effect="scale" for characters that scale up from a smaller size with a slight
overshoot for a bouncy, playful feel. You can adjust the starting scale with --scale-from,
which defaults to 0.6.
<div id="text-reveal__scale"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="scale"> Think big, pounce bigger. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__scale'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Skew Jump to heading
Set effect="skew" for characters that arrive at an angle and straighten out. You can
adjust the starting angle with --skew-from, which defaults to 25deg.
<div id="text-reveal__skew"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="skew"> Leaning into the wind. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__skew'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Slide down Jump to heading
Set effect="slide-down" for characters that slide from above with a smooth
deceleration. The slide distance scales with font size and you can tune it with --translate-y,
which defaults to -0.5em.
<div id="text-reveal__slide-down"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="slide-down"> The cat descends from the shelf. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__slide-down'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Slide up Jump to heading
Set effect="slide-up" for characters that slide from below with a smooth
deceleration. The slide distance scales with font size and you can tune it with --translate-y,
which defaults to 0.5em.
<div id="text-reveal__slide-up"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="slide-up"> Nine lives, zero regrets. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__slide-up'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
Stretch Jump to heading
Set effect="stretch" for characters that expand horizontally from nothing, as if
being pulled apart from their center. A mechanical, typeface-specimen feel.
<div id="text-reveal__stretch"> <p style="font-size: 1.5rem;"> <quiet-text-reveal effect="stretch"> A full-body stretch, head to tail. </quiet-text-reveal> </p> <quiet-button>Replay</quiet-button> </div> <script> const container = document.getElementById('text-reveal__stretch'); const reveal = container.querySelector('quiet-text-reveal'); const button = container.querySelector('quiet-button'); button.addEventListener('click', () => { reveal.restart(); }); </script>
API Jump to heading
Importing Jump to heading
The autoloader is the recommended way to import components but, if you prefer to do it manually, the following code snippets will be helpful.
To manually import <quiet-text-reveal> from the CDN, use the following code.
import 'https://cdn.quietui.org/v5.0.0/components/text-reveal/text-reveal.js';
To manually import <quiet-text-reveal> from a self-hosted distribution, use the
following code. Remember to replace /path/to/quiet with the appropriate local path.
import '/path/to/quiet/components/text-reveal/text-reveal.js';
Slots Jump to heading
Text Reveal supports the following slots. Learn more about using slots
| Name | Description |
|---|---|
| (default) |
The text to reveal. Note that the element's textContent is used, so any elements
slotted in will be discarded.
|
Properties Jump to heading
Text Reveal has the following properties that can be set with corresponding attributes. In many cases, the attribute's name is the same as the property's name. If an attribute is different, it will be displayed after the property. Learn more about attributes and properties
| Property | Description | Reflects | Type | Default |
|---|---|---|---|---|
effect
|
The reveal effect to use. |
|
'blur'
|
'fade'
|
delay
|
Delay in milliseconds before the animation starts. |
|
number
|
0
|
duration
|
The transition duration in milliseconds for each character. |
|
number
|
600
|
stagger
|
The delay in milliseconds between each character's animation start. |
|
number
|
20
|
startOnView
start-on-view
|
Whether to start the animation when the component comes into view. |
|
boolean
|
false
|
ignoreReducedMotion
ignore-reduced-motion
|
By default, no animation will occur when the user indicates a preference for reduced motion. Use this attribute to override this behavior when necessary. |
|
boolean
|
false
|
Methods Jump to heading
Text Reveal supports the following methods. You can obtain a reference to the element and call them like functions in JavaScript. Learn more about methods
| Name | Description | Arguments |
|---|---|---|
reveal() |
Animates the text in. | |
hide() |
Animates the text out with a reverse stagger. | |
restart() |
Resets the animation and plays it again from the beginning. |
Events Jump to heading
Text Reveal dispatches the following custom events. You can listen to them the same way was native events. Learn more about custom events
| Name | Description |
|---|---|
quiet-animation-complete |
Emitted when the reveal or hide animation has completed. |
CSS custom properties Jump to heading
Text Reveal supports the following CSS custom properties. You can style them like any other CSS property. Learn more about CSS custom properties
| Name | Description | Default |
|---|---|---|
--blur-amount |
The blur radius used by the blur effect. |
12px
|
--translate-y |
The vertical offset used by the blur (10px), fall (-0.75em),
rise (0.75em), slide-up (0.5em), and slide-down (-0.5em)
effects.
|
|
--scale-from |
The starting scale used by the scale effect. |
0.6
|
--skew-from |
The starting skew angle used by the skew effect. |
25deg
|
--glow-radius |
The glow radius used by the glow effect. |
1em
|
--glow-color |
The glow color used by the glow effect. |
currentColor
|
--rotate-from |
The starting rotation used by the flip and rotate effects.
|
-45deg
|