Fake it til you make it: A recipe for slanted text

In Jen Simmons’ recent presentation on modern layouts for the web, she takes down current web design conventions, and asks us to consider looking at the print world for inspiration. One such inspiration she points to is text wrapping around complex images.

Why hasn’t there been more of that? Well, we haven’t been able to do it in a flexible and responsive way. And, while that’s changing, we need to have some alternatives ready for browsers that don’t support CSS Shapes. The following is a recipe for a cross-browser solution to text sloping down at an angle, one example of a layout that breaks the constrictive box of web convention. 

At Advomatic, we recently received a design comp that required something that looked a little like this:

 White text paragraph on a gray background with the left edge moving in further as you go down.

It’s a block of text with the left side angling in, as if it were abutting the hypotenuse of a triangle, like this:

White text paragraph on a gray background with the left edge moving in further as you go down with a blue triangle pushing it over

This is a design embellishment that is so easy to do in Illustrator, yet oh-so-hard to do with CSS.

Immediately I googled for some workarounds, especially that of CSS Shapes. I found a really cool CSS property, shape-outside. It controls how text flows around a floated element — including a CSS shape.

If you are in Chrome or Safari, you can see it in action here:

As you can see, it works well in those browsers. However, it just isn’t ready for Firefox and IE. Instead we’ll need to come up with a Javascript solution.

As ugly as this may look, it seems to be a surprisingly good solution. We’ll use a bunch of spans to push each line over a little more than the last, as illustrated here:

White text paragraph on a gray background with the left edge moving in further as you go down with red blocks pushing each row over

 

To accomplish this, we’ll just use jQuery to do the following:

  • Add a bunch of spans (here you will need to make a decision about a maximum number needed; I chose 20.)*

    slantedText.prepend('<span class="push"></span>'.repeat(20));

     

  • Float the spans to the left with CSS, and clear.

    .push { 
      clear: left;
      float: left;
    }
    

     

  • Iterate through the “push” spans, and give each an increasing width:

    push.each(function(i) { 
      // include number is how wide each step is var incline = 12; 
      // iterate through each "push" span, multiplying the position by the incline. 
      $(this).css('width', (++i * incline) + 'px'); 
    }); 

     

  • Make a function to determine the height of the text (after the “push” span widths have been set.)

    // Set the height of the div to be the height of its text. 
    // Pairing this with overflow: hidden in the CSS will keep any extra "push" spans from showing. 
    function setSlantedTextHeight(slantedText) { 
      var slantedTextInner = $('.slanted_inner', slantedText); 
      // Get the height of the inner text 
      var height = parseInt(slantedTextInner.height()); 
      slantedText.css('height', height); 
    }
     
    // Call height 
    function setSlantedTextHeight(slantedText); 
    

     

  • Make sure the line-height of the text matches the height of the “push” spans; also make sure the text has any overflow hidden, to not show any extra “push” spans.

    p { 
      font-size: 21px; 
      line-height: 32px; 
      overflow: hidden; 
    } 
    .push { 
      height: 32px; 
    } 

     

Here it is, all together:

This is surprisingly easy to make responsive. To re-check the height of the paragraph, you can add a line like this at the end:

$(window).resize(function() { 
  setSlantedTextHeight(slantedText); 
}); 

And if you need smaller text at smaller sizes, just adjust the “push” span height and text line-height so they match at the different breakpoints.

While you may go forever without needing to know how to do this, at least now you are ready should the case arise!

*Note: There is one more small piece of JS you will need to make String.repeat work in all browsers:

// String.repeat is only available in ECMA6, so most browsers don't have it.
  if (!String.prototype.repeat) {
    String.prototype.repeat = function(num) {
      return new Array(num + 1).join(this);
    }
  }