Saturday, June 08, 2013

Text-align: Justify and RWD

Text-align: Justify and RWD

Did you know that one of the most powerful tools for fluid and responsive layout has been a native feature for every browser since HTML 4?


While working on a project early last year, I quickly became frustrated having to re-calculate the margins between menu items in my full-width footer nav each time the client requested a new link in the menu.

Thinking about how justified text in Microsoft Word fills the entire width of the line by creating even word spacing, I wondered if the same principle could be applied to a menu of text links in HTML. Knowing that text-align: justify was a long established CSS property (the descendent of HTML 4 attribute align=justify), I did some Googling for answers.

Sure enough, I found some discussions on StackOverflow about justified horizontal nav menus.

Text-align: Justify and RWD
Fig. 1: An example of a full-width “justified” navigation menu.

How it works

In its natural state, text-align: justify will not work unless the contents of the line are long enough to cause a line-break. Otherwise, the text remains left aligned. Incase you're wondering why this is, it's because in a typesetting situation this is the desired behavior - the final line of a block of justified text is always left aligned. Perhaps this was an oversight when the CSS spec was designed, but even if we only have one line of text, that one line still behaves like the final line.

It took me a few tries, but we can solve this problem by giving 100% width to an invisible inline element at the end of the line (shown in purple for demonstration purposes). As this "break" element is too wide to share a line with anything else but itself, it causes a line-break, thus becoming the final line and forces the words before it to justify (fig. 3).

Text-align: Justify and RWD
Fig. 2: Without a “break” element. As there are not enough elements to break the line, all elements will align left.

Text-align: Justify and RWD
Fig. 3: With a “break” element. The “break” element, shown as a purple dash, forces a line break due to its 100% width. All preceding inline elements are now justified.

Because text-align: justify is designed to work on individual inline words, it works on any inline element and more importantly any inline-block element.

As this particular project was a fixed grid, non-responsive site (remember when we used to build those?), the potential significance of this technique for RWD didn't occur to me until later, when I started to combine it with percentage based inline-block elements.

Text-align: Justify and RWD
Fig. 4 Justified "inline-block" elements. Note the "break" element is again used to force the last row of elements to justify.

Implications for Responsive Web Design

Almost all of our responsive designs at Barrel, feature some sort of boxed content in a grid. Traditionally, the way I handled this was to give each element in the grid a percentage width and then divide up the available leftover space into equal, percentage-based margins.

When I wanted my grid to go right to the edges, I would target the first and last element of each row using nth-of-type and remove their left and right margins respectively. This already seemed like way too much calculation and if a browser decides to round your percentages up instead of down when converting to pixels, that one extra pixel is enough to cause elements to jump on to the next row at random (even though all widths and margins add up to 100%!).

I'm sure we've all grappled with some of these issues. Enter text-align: justify to save the day!

You'll never have to deal with horizontal margins ever again!

By simply applying text-align: justify to the container and giving the child elements display: inline-block and a percentage width, you'll never have to deal with horizontal margins ever again! (Oh and did I mention, when using this trick, you'll also never need to use float on your elements ever again, so you can wave goodbye to those ignominious clearfixes and clear divs too!)

NB: We should be aware that when using display: inline-block, our elements will be at the mercy of various typographic CSS properties, including font-size, line-height, vertical-align and word-spacing. These properties will have a visible affect on your layout's whitespace, but can be easily adjusted or removed as needed. In 99% of cases, setting font-size: 0.1px; to the container and vertical-align: top to the child elements should do the trick.

"Break" Elements

As mentioned earlier, the first caveat of this technique is that it is necessary to force each row to "break" before it will justify. If you have a multi-row grid, this won't be necessary until you get to the last row, but it is crucial to the technique and there are two ways we can solve the problem.

1. Adding a "break" element as the last child in the container.

<ul id="Grid">
    <li class="break"></li>

With the following styling:

    text-align: justify;
    font-size: 0.1px; /*hide whitespace between elements*/

#Grid li{
    display: inline-block;
    width: 23%;

#Grid .break{
    width: 100%;
    height: 0;

2. Applying an ::after pseudo-element to the container

    content: '';
    display: inline-block;
    width: 100%;

I recommend the second method for cleaner, semantic markup.

"Placeholder" Elements

In this section I use the term "column" as a visual metaphor only, as we should all be aware that there is arguably no such thing as a vertical column (or a grid for that matter) in HTML/CSS. One of the keys to understanding this technique is understanding that by default, HTML flows inline and horizontally across the page, left to right, line by line. By using text-align: justify, we are harnessing this characteristic instead of fighting against it with floats and absolute positioning.

The second caveat occurs in cases where we don't have control over the number of elements in our grid.

Text-align: Justify and RWD
Fig 5. Coming up short. As the last row of our grid is not complete, the justified margins are calculated based on two elements, and not four.

Say you get to the last row of your four-column grid and you only have two elements instead of the normal four - and you want those last two to align neatly with everything else - you will need to add invisible "placeholder" elements to make up the difference. This forces the browser to calculate the margins as if there were four items and not two (fig. 5).

If you had only one element on that last row, it wouldn't be an issue - as it will always align left naturally. However, if you have any number of elements greater than one and less than the total columns per row, you will have to "fill in the gaps" manually with "placeholder" elements (fig. 6).

Text-align: Justify and RWD
Fig. 6. "Placeholder" elements. This four-column grid requires two placeholders (shown in solid purple), to ensure margins are calculated correctly.

To account for any and all possible numbers of elements on the last row, the number of "placeholder" elements you will need to add is equal to the maximum number of elements per row, minus 2. These elements should be inserted at the end of your grid (before the "break" element if you are not using a pseudo-element to break the row) and then left alone. Since they do not occupy any vertical space, the "placeholder" elements won’t affect the layout if the last row is full or if your site is responsive and the number of elements per row changes. As long as you have enough placeholders for the widest view, you’ll be fine.

No. of placeholders = max elements per row – 2

Obviously, this has some semantic implications - as there is no way to create any of these placeholders using pseudo-elements. On a grid where the last row will always have the maximum number of elements, we don't need to use placeholders at all (just a break), but in most CMS situations they are necessary and should be hard-coded into your HTML.

Media Queries

Media queries can be used to dictate the number of columns in the grid based on viewport size. For each new column layout, we need only redefine our column width and justify will take care of the rest. Check out the demo to see it in action across four different layouts (NB: Our demo also uses fluid, dynamic element height via the old padding-top trick).

Wider Uses

After having great success with this technique, I've started using it as a powerful layout tool for all sorts of applications. I rarely, if ever, need to use floats for layout anymore and combined with media-queries, it becomes an incredibly powerful and time-efficient technique for tackling percentage-based fluid layouts.

It's also just as useful on non fluid layouts, if you don't want to spend time calculating your margins - why not let the browser do that work for you?

Perhaps most impressive, is the cross-browser compatibility of this technique. Because justify and inline-block have both been around for a very long time, this technique can be used confidently on IE7 and up (with a few small IE7 hacks!).

As a footnote, the main stimulus behind the creation of the MixItUp plugin was preserving the elegance of this technique on a grid that required dynamic client-side animated filtering. I knew that slapping masonry or isotope on it would force absolute positioning on my elements, convert their percentage-based widths and heights into static pixels, and require a disproportionate amount of custom JavaScript to maintain the responsive behavior I had so easily achieved with CSS alone. So MixItUp was born and I'm very excited with how the two techniques are working together so far.

If you're interested in exploring this technique further, check out the following links:

Basic Examples:

Advanced Examples:

View Demo