Fun with line-height!

Chris Coyier on

The line-height property in CSS controls the space between lines of text. It is often set in a unitless value (e.g. line-height: 1.4;) so that it is proportional to the font-size. It’s a vital property for typographic control. Too low and lines are awkwardly squished together; too high and lines are awkwardly far apart. Both inhibit readability. But you probably already know that.

In this article we’ll focus on some trickery. If you know (or can figure out) the exact value of line-height, you can do some neat stuff!

Style each line of text a different color

There is no ::nth-line(), unfortunately. We can’t really even use <span>s reliably, as there are tons of different things that can cause text to break at different points.

There is a way, albeit non-standard, to use the background of an element as the background of text.

.text {
 -webkit-background-clip: text;
 -webkit-text-fill-color: transparent;
}

There is another trick where you can use a linear-gradient() with color-stops such that the color doesn’t fade into another, it just abruptly ends and another starts. Let’s say we know that the line-height is 22px, we can make the gradient breaks right at that.

.text {
 background-image: linear-gradient(
 to bottom,
 #9588DD,
 #9588DD 22px,
 #DD88C8 22px,
 #DD88C8 44px,
 #D3DD88 44px,
 #D3DD88 66px,
 #88B0DD 66px,
 #88B0DD);
}

Combining those two tricks:

In a browser that doesn’t support the text background clipping, like Firefox, you would get solid bars of color behind the text. Maybe that’s cool and you like it. Maybe you’d rather just fall back to a solid color text. In that case, you can use @supports to only apply it if supported anyway.

Also, since you’re using the value of line-height over and over, might be nice to variablize it. I’ll use SCSS here, but this would be kinda neat to do with real CSS variables someday so you could change it even after rendering and watch it all keep working.

$lh: 1.4em;
body {
 font-size: 1em;
 line-height: $lh;
}
@supports (-webkit-background-clip: text) {
 p {
 -webkit-background-clip: text;
 -webkit-text-fill-color: transparent;
 background-image: linear-gradient(
 to bottom,
 #9588DD,
 #9588DD $lh,
 #DD88C8 $lh,
 #DD88C8 $lh*2,
 #D3DD88 $lh*2,
 #D3DD88 $lh*3,
 #88B0DD $lh*3,
 #88B0DD);
 }
}

Using this behavior at the top of the element is easiest. Here’s an example where the first few lines are altered for emphasis.

.text {
 -webkit-background-clip: text;
 -webkit-text-fill-color: transparent;
 background-image: linear-gradient(
 to bottom,
 rgba(white, 0.8),
 rgba(white, 0.8) $lh,
 rgba(white, 0.6) $lh,
 rgba(white, 0.6) $lh*2,
 rgba(white, 0.4) $lh*2,
 rgba(white, 0.4) $lh*3,
 rgba(white, 0.2) $lh*3,
 rgba(white, 0.2));
}

It gets more difficult if we’re trying to target the last few lines of an arbitrary amount of text. In that case, we’ll need the first color band to go from the top to all-the-way-down-minus-a-few-lines. Fortunately we can do that with calc()!

.text {
 -webkit-background-clip: text;
 -webkit-text-fill-color: transparent;
 background-image: linear-gradient(
 to bottom,
 rgba(white, 0.8),
 rgba(white, 0.8) calc(100% - 66px),
 rgba(white, 0.6) calc(100% - 66px),
 rgba(white, 0.6) calc(100% - 44px),
 rgba(white, 0.4) calc(100% - 44px),
 rgba(white, 0.4) calc(100% - 22px),
 rgba(white, 0.2) calc(100% - 22px),
 rgba(white, 0.2));
 background-position: bottom center;
}

There are other ways to do this kind of thing as well, like overlaying a pseudo element gradient (with pointer-events: none; so it’s not annoying).

Lines Between Text

Using a similar technique to the solid-color-stops technique we used above, we can create a 1px line gradient that repeats exactly at the known line-height. The easiest way is to use repeating-linear-gradient(), as well as make sure all other elements play nicely (like padding that is also based on line-height).

.parent {
 padding: $lh*2;
 background: #082838;
 background-image: repeating-linear-gradient(
 to bottom,
 rgba(white, 0) 0,
 rgba(white, 0) $lh/1em*16px-1,
 rgba(white, 0.1) $lh/1em*16px-1,
 rgba(white, 0.1) $lh/1em*16px
 );
}

In order to get the 1px line, we need to know what the line-height is in pixels, then subtract one. The goal is that the gradient repeats at exactly the known line-height, so the last pixel in that space can be the line. Because we’ve left the body font-size at 1em, that’s 16px. And since the line-height is set in ems, we can divide by 1em removing the unit, then multiply by 16px and subtract one when needed.

Position images one-per-line

Another thing you can do if you know the exact line-height is to make background-size match it, at least on the vertical axis. Then you can make it repeat vertically and it will line up one-image-per-line.

.text
 background-image: url(image.svg);
 background-size: $lh $lh;
 background-repeat: repeat-y;
 padding-left: $lh*2;
}

Demos

See the Pen One line of Text Dif Color by Chris Coyier (@chriscoyier) on CodePen.

Psst! Create a DigitalOcean account and get 200ドル in free credit for cloud-based hosting and services.

Comments

  1. ElijahFowler
    Permalink to comment#

    I used a similar technique building style for a code block. Neat stuff.

  2. Lea Verou
    Permalink to comment#

    On targeting the last few lines: Why mess with calc() instead of just changing the direction of the gradient to “to top” instead of “to bottom”?

    On the lines example: This is really messy, without needing to be so. You can just use plain linear gradients with color stops at 1px, then use background-size in ems. NEVER EVER specify line heights in absolute lengths. It’s bound to break horribly when some careless developer changes the font size without bothering to change the line-height as well.

    • Chris Coyier
      Permalink to comment#

      Yeah Charlotte caught me on that one too, WAY easier to just start the gradient at the bottom.

    • Yeswanth
      Permalink to comment#

      Thanks for the article Chris!

      NEVER EVER specify line heights in absolute lengths. It’s bound to break horribly when some careless developer changes the font size without bothering to change the line-height as well.

      -> Could I get any work-around example for this issue. I know relation wit line-height & font-size. But, I need some clearance with first statement.

  3. Šime Vidas
    Permalink to comment#

    If anyone is wondering why the 1. example (multi-color) doesn’t work in Safari, but the 2. example (fade-to-gray) does, it’s because the CSS of 1. example relies on @supports (which Safari does not support).

  4. Antonio Sánchez
    Permalink to comment#

    I love this emphasizing efect, I never thouht of it. I am definitely using it in some of my next projects :)

  5. Chris
    Permalink to comment#

    The gradient at the bottom is not just how to reverse the gradient, it’s solving a different problem, namely what it states in the article: “trying to target the last few lines of an arbitrary amount of text.”

  6. Paul d'Aoust
    Permalink to comment#

    Neat tricks, and they feel ‘good’ — as in, not abusing CSS (although it would be great to have an ::nth-line() to make our intent more explicit). One thing I should point out, though, is that with that ‘lined paper’ effect, Blink has a nasty bug that makes the gradients render at the wrong scale. There are multiple bug reports about it, but no traction as far as I can see. If people could vote on a few of them, that might spur some action.

  7. Sam S.
    Permalink to comment#

    Vox really <3s their gradients on all the things, but I noticed some of their pull quotes getting the treatment in a few stories recently:

    http://charleston.eater.com/2015/1/27/7921157/after-28-years-in-business-crowds-still-line-up-for-hymans-seafood

  8. Ashley Sheridan
    Permalink to comment#

    Some of these fail in pretty weird ways if the zoom level is changed on the browser for any reason (which I actually know a lot of people do, as it’s easier to set the zoom on a browser than get their font sizes fixed correctly system-wide on high-res screens)

  9. Robert
    Permalink to comment#

    Neat and interesting effects. Fade out bottom effect is amazing, and I did not think that it was possible via CSS. Before that we had to use PNG half transparent images and absolute positions with Z-Index to create that effect over text box. Above in examples, it’s a very good practice to create that effect. Thanks for sharing.

  10. Samuel deHuszar Allen
    Permalink to comment#

    These are really cool tricks, but browsing these on my Nexus 5 makes a couple of them fall apart, particularly in portrait.

    Not knowing how the lines will wrap at various media queries can pretty dramatically change the impact. So don’t forget to test these on smaller devices.

This comment thread is closed. If you have important information to share, please contact us.

AltStyle によって変換されたページ (->オリジナル) /