Web Platform Team Blog

Adobe

decor

Making the web awesome

Improving CSS text decorations

The possibilities to style, decorate and lay out text in HTML are just awesome! CSS has done an incredible job to make this possible. But if you are familiar with SVG you may still miss two very exciting features.

Stroke and fill text

SVG allows authors to stroke and fill a text. The stroke is the path along a glyph shape that can be styled differently from the shape fill. In the following example the text is stroked with orange.

Stroke on text

The following snippet show the SVG code:

<svg>
  <text style="fill: #FFDB80; stroke: #E09F00; stroke-width: 2px;">
    Stroke on text
  </text>
</svg>
  • The fill property is responsible for filling the shape of a glyph.
  • The stroke property is responsible for stroking the shape of a glyph.
  • The stroke-width property controls the width of the stroke path.

All major browsers support filling and stroking of SVG text. There is still no CSS module that allows the same for other markup languages.

WebKit implemented the CSS properties -webkit-text-fill-color, -webkit-text-stroke-color, -webkit-text-stroke-width and -webkit-text-stroke (shorthand for the two previous properties) to fill this gap. The following snippet styles the text and gives you the same result as in the initial example.

h1 {
  -webkit-text-fill-color: #FFDB80;
  -webkit-text-stroke: #E09F00 2px;
}

Style text with gradients and patterns

Even WebKit’s CSS properties just allow coloring the stroke and the fill of a text. SVG allows to apply gradients and patterns on a text as well. The first example applies a gradient on the fill and another gradient on the stroke of the text:

Gradient on text

<svg>
  <linearGradient id="grad1">
    <stop stop-color="#FFDB80" offset="0%"/>
    <stop stop-color="#FF791F" offset="50%"/>
    <stop stop-color="#E09F00" offset="100%"/>
  </linearGradient>
  <linearGradient id="grad2">
    ...
  </linearGradient>
  <text style="fill: url(#grad1); stroke: url(#grad2); stroke-width: 2px;">
    Gradient on text
  </text>
</svg>

The same with patterns and the following image:

Pattern image

Pattern on text

<svg>
  <pattern id="pattern1" patternUnits="userSpaceOnUse"
   viewBox="0 0 216 78" width="216" height="78">
    <image xlink:href="textpattern.png" width="216" height="78"/>
  </pattern>
  <text style="fill: url(#pattern1);">
    Pattern on text
  </text>
</svg>

To apply the gradient or the pattern, the fill and stroke properties use references to the paint resources instead of colors. fill: url(#pattern1) references the pattern by it’s id.

WebKit implemented another property that allows authors to archive similar effects on HTML text: -webkit-background-clip: text;. The value text tells the rendering engine that it should take the containing text of the element and mask it with the background of the same element.

First of all, you need to specify the background of the image. This will either represent a pattern with background-repeat or a gradient with CSS gradients.

h1 {
  background: url(bgimage.png) repeat;

The repeat statement ensures that your image gets repeated if it does not fill the whole background area.

The next step is setting -webkit-background-clip. Make sure that you use the prefixed version of this property.

  -webkit-background-clip: text;

The containing text is used as a mask for the background. But the text will still be painted on top of the masked background. To avoid that, the text must be set to transparent.

  opacity: 0;
}

The transparent text is an accessibility problem for browsers which do not support -webkit-background-clip: text;. The text won’t be readable there. Instead you just see the unmasked background, filling the size of the whole element. For more details and possible workarounds read Divya Manian’s blog post “Using background clip text with CSS fallback”.

Other tutorials suggest -webkit-mask instead, which is not less problematic.

A standards conform, interoperable solution – or “yet another hack”

Since all major browsers support SVG, it might make sense to use SVG for this styling purpose. Indeed there is a standards conformant way with full accessibility support as described in the blog post “Text masking — The standards way”.

An SVG element can be used inside your HTML text element, and replaces your text for this element. The text is still there, but encapsulated in the SVG element. The benefit: screen readers and search engines can ignore the SVG code and crawl the text as they did before. In the following snippet, the old code

<h2>Stroke on text</h2>

gets replaced by

<h2>
  <svg>
    <pattern id="pattern1">
      ...
    </pattern>
    <pattern id="pattern2">
      ...
    </pattern>
    <text style="fill: url(#pattern1); stroke: url(#pattern2); stroke-width: 2px;">
      Stroke on Text
    </text>
  </svg>
</h2>

This is fully functional in Safari, Chrome, Opera and Internet Explorer 9 and up.

Sadly there are several problems with this solution:

  • First of all, this only works for text without line breaks. SVG text elements do not support line breaks. Your inline SVG code would get more complicated at the cost of maintainability.
  • The SVG element needs a with and height. Everything inside the SVG element gets clipped to it’s dimension. You need to measure each text and set the dimension for each SVG element individually.
  • Styling h2 may not affect the text inside the SVG element. You need to make sure that the SVG element and all of it’s siblings inherit all necessary property values.
  • SVG elements contribute to the layout. You need to adjust the layout manually.

A solution for the future

Together with Elika (known as fantasai) from Mozilla and Tab Atkins from Google I tried to find an interoperable solution. Both Elika and Tab run into the same problems.

The proposal we came up with is to allow the fill, stroke and stroke-width properties on text in general. Tab Atkins reached out to the SVG WG to discuss this proposal. The interaction of the color property to the fill and stroke properties on text needs to be clarified. This may require changes to fill and we need to make sure that existing content is not affected. The next step is an official suggestion of the proposal to the CSS WG and ask for approval. Last but not least, the proposal needs to get specified.

There is still a long way to go. But the result will hopefully make decorations of text more powerful than they are already today. Stay tuned!

10 Comments

  1. February 20, 2013 at 6:59 pm, Šime Vidas said:

    Shouldn’t the element have (at least) a “y” attribute? I’ve copy-pasted the first code block into jsFiddle, and the text was barely visible (see http://jsfiddle.net/nZjuK/). After setting “y”, it was fine: http://jsfiddle.net/nZjuK/1/

    Btw, if you could accompany your articles with live demos (e.g. jsFiddles), that would be cool :-)

    • February 21, 2013 at 8:51 am, dschulze said:

      Indeed, the attributes x and y are missing to reduce the example size to the necessary minimum. The examples in the blog post are live examples. Try to mark the text in other browsers than Firefox and you’ll see the selection behind the glyphs.

  2. February 23, 2013 at 6:39 am, tim peterson said:

    Thank you for posting this but two comments:

    1) the examples you show are quite asthetically ugly. I wouldn’t consider these “improvements” in “decorating” text.. Might there be some more attractive examples?
    2) the font color used in this textbox is way too light colored to be legible.

    Both of these hurt the point you are trying to make.

    • February 25, 2013 at 11:04 am, dschulze said:

      Hehe, I agree :) This was more for demonstration of the feature rather than giving use cases. Maybe it should be both. I am working on it. Thanks!

  3. February 23, 2013 at 10:14 am, CSS Viking said:

    What is the purpose of text-fill-color? It doesn’t do anything differently from color in this example. A quick search revealed a compatibility argument, color can be a fallback color in case of unreadable text when text-stroke isn’t applied. @supports would imho be a much better and general solution for that problem although that too is just “promising” at the moment.

    • February 25, 2013 at 10:59 am, dschulze said:

      text-fill as text-fill-color are proprietary properties at the moment. You will be able to check for support in WebKit once it implements @support. This doesn’t make it interoperable though.

  4. February 23, 2013 at 10:38 am, Zach said:

    It’s like 1997 all over again, but with CSS instead of images :(

    • February 25, 2013 at 10:57 am, dschulze said:

      Stroking text is very helpful as applying patterns and gradients can be. There is no need for restrictions. It is up to the designer and author to use it probably. As with every technology, there might be some esthetic fails. This is normal and no reason to blame the technique.

  5. February 23, 2013 at 3:33 pm, Dan Owen said:

    Fascinating, but until you can feed SVG attributes indirectly from a stylesheet, it can’t be practical. Can this be done already?

    • February 25, 2013 at 10:54 am, dschulze said:

      The fill and stroke attributes are so called presentation attributes and can be set with CSS Style sheets as well. Furthermore, these properties will take CSS Image values in the future, which allows applying CSS gradients as well.