惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
Microsoft Azure Blog
Microsoft Azure Blog
Stack Overflow Blog
Stack Overflow Blog
Jina AI
Jina AI
Vercel News
Vercel News
H
Help Net Security
Martin Fowler
Martin Fowler
美团技术团队
云风的 BLOG
云风的 BLOG
Y
Y Combinator Blog
阮一峰的网络日志
阮一峰的网络日志
MyScale Blog
MyScale Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
博客园 - 司徒正美
人人都是产品经理
人人都是产品经理
Engineering at Meta
Engineering at Meta
G
Google Developers Blog
Blog — PlanetScale
Blog — PlanetScale
MongoDB | Blog
MongoDB | Blog
宝玉的分享
宝玉的分享
小众软件
小众软件
T
Tailwind CSS Blog
WordPress大学
WordPress大学

CSS-Tricks

Let’s Use the Emergent CSS random() Function in all the Browsers | CSS-Tricks What’s !important #18: <geolocation>, Syntax ::highlight()ing, named-feature(), and More | CSS-Tricks Creating Web Widgets Using the Document Picture-in-Picture API | CSS-Tricks animation-trigger | CSS-Tricks MicroLighter: Syntax Highlighter | CSS-Tricks WordPress PHP-Only Block Registration | CSS-Tricks Resolved: CSS Class Prefix Selector | CSS-Tricks CSS Navigation Matching, Early Days | CSS-Tricks WordPress.com Student Plan | CSS-Tricks Dark mode toggles: two states are enough | CSS-Tricks What’s !important #17: Custom Highlight API, CSS Navigation Matching, Fixing text-stroke, and More | CSS-Tricks Blocked aria-hidden: The Warning is Right, and Every Fix You've Found is Wrong | CSS-Tricks Animating CSS border-image | CSS-Tricks SmashingConf Freiburg 2026, September 7-10 | CSS-Tricks Using and Styling the Dialog Element | CSS-Tricks 2026 State of CSS, Devs Surveys | CSS-Tricks Gap Decorations Are Now Available, Here’s What’s New | CSS-Tricks What’s !important #16: sibling-index() Animations, Use Cases for the infinity Keyword, Container Stuck Queries, and More | CSS-Tricks writing-mode | CSS-Tricks pointer-events | CSS-Tricks What’s !important #15: Boundary-aware CSS, Time-based CSS, Full-bleed CSS, and More | CSS-Tricks Get Ready For the Powerful CSS border-shape Property! | CSS-Tricks What’s !important #14: Gap Decorations, random(), <select> field sizing, and More | CSS-Tricks The Shifting Line Between CSS States and JavaScript Events | CSS-Tricks translateZ() | CSS-Tricks translateY() | CSS-Tricks translateX() | CSS-Tricks translate() | CSS-Tricks Using Scroll-Driven Animations for Opposing Scroll Directions | CSS-Tricks A First Look at Scroll-Triggered Animations | CSS-Tricks
Revealing Text With CSS letter-spacing | CSS-Tricks
Preethi · 2026-05-27 · via CSS-Tricks

Some text effects are relatively hard to pull in CSS, the main reason being we are unable to target individual characters (something many of us want in the form of ::nth-letter(), although we have basis for it with ::first-letter that gives us access to a box element’s first glyph.

But maybe there are a few things we can use today with what we already have.

For example, the CSS letter-spacing property adjusts the space between all characters in a block of text. Positive values add space to the right side of each glyph (in a left-to-right writing mode), and negative values shrink the width of the glyph box, causing letters to overlap and even move the other way.

The letter-spacing accepts length units, and percentage (relative to font size). It is animateable, and as we saw before, the negative values can shrink it down or reverse it. Which is something we can make use of.

Overlapping and separating letters

It’s quite easy to completely overlap the characters as a starting point and setting it’s color to transparent to visually hide it.

label {
  letter-spacing: -1ch;
  color: transparent;
  /* etc. */
}

From there, we can reveal the text by animating that letter-spacing value to a positive value and updating the color to a visible value, like when a checkbox is :checked:

li:nth-of-type(2) label {
  text-align: center;
}
li:nth-of-type(3) label {
  text-align: right;
}
input:checked + label {
  letter-spacing: 0ch;
  color: black;
  transition: letter-spacing 0.6s, color 0.4s;
}

Note: The CSS ch unit is a relative length representing the width of the zero (0) glyph.

The labels go from negative letter-spacing to normal spacing and the color updates to black. Both these changes happen over a transition.

The second and third labels are given center and right text alignments and thus when negative letter spacing is applied they bundle up at the given alignment position, center and right, respectively. When letter``-``spacing goes from negative to zero (or any positive value) the letters separate from that same alignment position.

Thus, we get a text reveal effect! Let’s look at some more.

Showing and hiding text

Check this out. We can toggle a checkbox label as a fun interactive UI touch:

<!-- Simplified for brevity; additional accessibility considerations -->
<input type="checkbox" id="cb">
<label for="cb">
  <span>Join the global club</span>
  <span>You've begun your journey!</span>
</label>
label {
  overflow: clip;
  /* etc. */
}

span {
  /* The first label */
  &:nth-of-type(1) {
    /* Default spacing: letters are fully visible */
    letter-spacing: 0ch;
    /* When the checkbox is checked, target this text */
    :checked + * & {
      /* collapse letters on top of each other, hiding them */
      letter-spacing: -2ch;
      text-indent: -1.5ch;
      /* Use a "bouncy" cubic-bezier for spacing */
      transition: 0.4s letter-spacing cubic-bezier(.8, -.5, .2, 1.4), 
                  0.1s text-indent;
    }
  }
  
  /* The second label */
  &:nth-of-type(2) { 
    /* Initially collapsed (letters overlap) */
    letter-spacing: -1ch;
    color: transparent;
    /* When the checkbox is checked, target this text */
    :checked + * & {
      /* Returns to normal spacing */
      letter-spacing: 0ch;
      color: black;
      /* Slightly delay the appearance so it starts after the first text begins to hide */
      transition:
        0.4s letter-spacing cubic-bezier(.8, -.5, .2, 1.4) 0.3s, 
        0.8s color 0.4s;
    }
  }
}

When the box is checked, a negative letter-spacing value (-2ch) and text-indent value (-1.5ch) is used on the first <span> to slide it out of the container box. We use overflow: clip to completely hide the text.

Concurrently, the text in the second <span> text goes from a letter-spacing value of -1ch to 0ch, which reveals it. To hide this overlapped text at -1ch, a transparent color was given that’s turned to black when the checkbox is checked.

Using with other glyph box styling

Here’s another fun one. We can start with an acronym that reveal the full text on hover. Again, we have existing features to help us pull this off, including ::first-letter and ::first-line.

We’ll start with this markup:

<!-- Simplified for brevity -->
<p id="acronym">
  <span class="words">United</span>
  <span class="words">Nations</span>
  <span class="words">International</span>
  <span class="words">Children's</span>
  <span class="words">Emergency</span>
  <span class="words">Fund</span>
</p>
.words {
  letter-spacing: -1ch;
  color: transparent;
  /* etc. */

  &::first-letter {
    color: black;
  }

  figure:hover + #acronym & {
    letter-spacing: 0ch;
    color: black;
    transition: letter-spacing 0.4s cubic-bezier(.8, -.5, .2, 1.4) /* etc. */;
  }
}

Each word in the UNICEF acronym initially has letter-spacing: -1ch to shrink the text, and color: transparent to keep the shrunk text hidden, except the ::first-letter that has color: black so it remains visible even though the rest of the text is stacked beneath it.

Now, we can target the image on :hover and select the entire text so that the letter-spacing value for each word decreases to 0ch and color: black is applied, showing what’s remaining of the words:

What else can we do?

I don’t know! But that’s where you come in. Obviously, a hypothetical ::nth-letter selector would be amazing for all kinds of text effects. But it’s neat that we can create some semblance of it today with existing features, like letter-spacing, ::first-letter, and ::first-line.

What can you cook up knowing we have these constraints?