As responsive, tile and grid-driven design continue to dominate the modern web, evenly spacing elements in a fluid layout with maximum cross-browser support is a must. JavaScript plug-ins are often used to implement this functionality, but they bring unnecessary overhead to the site, and cross-browser support and customization often pose implementation and maintenance challenges. In this post, I will show how this can be achieved using nothing but HTML and CSS, using a technique that is supported by IE6+ and all major browsers!
Markup
Let's start by evenly spacing four div
elements. Here is our markup:
HTML
<div class="column-outer-row demo-outer-row">
<div class="column">Some content...Column 1</div>
<div class="column">Some content...Column 2</div>
<div class="column">Some content...Column 3</div>
<div class="column">Some content...Column 4</div>
</div>
Notice how I have assigned the classes .column-outer-row
and .column
to differentiate between the different elements. We will continue to use these going forward.
Styles
Now that we have our markup, it's time to have a look at our styles. As we go, I will show both the CSS and the LESS versions of the styles being discussed.
If you are following the LESS, note that I named the mixin .edc-outer-row
, where edc
stands for "Evenly Distributed Columns." Also note that we will be adding to the mixin as we go. To make it easier to read, I replace the existing rules with an ellipses (...) and only show the new rules that are being added. Don't worry, we will see the whole thing put together at the end.
Now that we have that covered, let's dive into our styles!
The Container
The following are the styles for the .column-outer-row
element:
CSS
.column-outer-row {
text-align: justify; /* REQUIRED for all */
-ms-text-justify: distribute-all-lines; /* IE ONLY (other browsers use :after) */
text-justify: distribute-all-lines; /* IE ONLY (other browsers use :after) */
font-size: 0; /* White-space fix */
}
/* Restore font-size for children */
.column-outer-row > * {
font-size: 16px;
}
LESS (Alternative)
/* Global variable for standard font-size */
@baseFontSize: 16px;
/* Mixin */
.edc-outer-row(@fontSize: @baseFontSize) {
text-align: justify; /* REQUIRED for all */
-ms-text-justify: distribute-all-lines; /* IE ONLY (other browsers use :after) */
text-justify: distribute-all-lines; /* IE ONLY (other browsers use :after) */
font-size: 0; /* White-space fix */
/* Restore font-size for children */
> * {
font-size: @fontSize;
}
}
/* Rules */
.column-outer-row {
.edc-outer-row();
}
Details
Note: This element has been named the .column-outer-row
as all of the columns contained within it must be constrained to fit on a single line (or row). It is the "outer" row because this technique creates a column-based grid, not a row-based one. In other words, each row is independent from one another and could have any number of columns. In other words, it is not like a table
, in which your columns line up and are the same size but might be merged. However, if that is the functionality that you are looking for then what you want to do is create your columns and then create "inner rows" of a consistent height that have the same vertical alignment in all of your columns. This gets a little off topic, but feel free to reach out to me through my contact page if you have any questions.
Starting from the top, notice how the text-align
property is used and set to justify
. The justify
value is used to tell the browser to evenly space the content over the entire width of the container. This property was designed to work with text, which is inline
, and thus any inline
element, including any inline-block
element, will be effected. This is important, as we are going to make set our .column
elements to display: inline-block;
, as we will see later.
The one caveat to text-align: justify
is that it only works when the content is long enough to normally cause a line-break. We work around this by using text-justify: distribute-all-lines
and by using an invisible element with width: 100%
, as we will see later. The distribute-all-lines
value of the text-justify
property tells the browser that the last line should be justified, regardless of length, though the default behavior is to leave it left-aligned.
Next, note that the setting font-size: 0;
with the note that it is a white-space fix. Because the .column
elements are to be displayed inline-block
, the browser may interpret and render additional, undesirable white-space between the markup for our .column
elements as well as within the container's tags. Note that this applies to any inline
elements, and that in this case this additional white-space may result in extra margins after the last element. By setting the font-size
to 0
, we are effectively eliminating this white-space from the flow.
Lastly, we restore the font-size
of the children. We can do this with individual styles rules for children or descendants of the .column-outer-row
element, or we can restore them globally. I prefer to restore them globally, since we overrode them from this level, and to use a wildcard (*
) selector to make the rule easy to override by more specific rules. Note that I assumed that the base size of the text would be 16px
, but any value can be used here.
Note, I chose to include the style rule for restoring the font-size
because, from a reusability standpoint, I do not want to include a rule that may cause changes in styles for the content within the columns. If my use-case requires that the font-size
for the columns remain 0
then I would either create an additional, more specific style to override the "restoration rule," or I would pass 0
in as my value for the @fontSize
parameter of my LESS mixin. This is just personal preference. The choice is yours.
The Columns
The following are the styles for the .column
elements:
CSS
.column {
display: inline-block; /* All major modern browsers */
*display: inline; /* Browser hack for IE6/7 */
zoom: 1; /* For IE 6/7 to display inline-block */
vertical-align: top; /* Prevent staggering */
}
LESS
/* Mixin */
.edc-outer-row(@columnClass, @valign: top, @fontSize: @baseFontSize) {
...
>.@{columnClass} { /* Make column class a parameter for greater reusability */
display: inline-block; /* All major modern browsers */
*display: inline; /* Browser hack for IE6/7 */
zoom: 1; /* For IE 6/7 to display inline-block */
vertical-align: @valign; /* Prevent staggering */
}
}
/* Rules */
.column-outer-row {
.edc-row(column);
}
Details
The styles for the .column
elements are fairly straight forward. First off, we plan to put more than one element on each line and want them to can be justified, thus we want them to be inline
. We also want block
-styling so that we can set dimensions. As such, we set the .column
elements to display: inline-block;
.
Unfortunately, IE6 and 7 do not allow elements that are not naturally inline
to display as inline-block
elements. A div
is an example of an element that is not naturally displayed inline
, whereas a span
element is naturally displayed inline
. In order to work around this, the next two property settings are used.
The *display: inline;
setting is the first of the two used to get around IE6/7's inline-block
support issue, and is fairly straight-forward. It sets our element to be inline
, but has a prepended asterisk, *display...
that ensures that the property will only be read by IE browsers that are IE 7 or earlier. This is known as a CSS hack. CSS hacks are safe and do are widely supported across browsers, however they are avoided so as to prevent overuse - one should not be using them unless it is a minor work-around for a legacy browser, as is the case here (read more about CSS hacks in the detailed reference, found at browserhacks.com).
The second property setting used to work around the IE6/7 inline-block
support issue is the zoom: 1;
setting. The zoom
property is a Microsoft proprietary CSS property, and setting it to 1
has no visible affect on the element, except that it adds hasLayout to it (another Microsoft internal), that gives it block
-styling. In IE 7 and earlier, in order to have block
-styling, an element needed to have hasLayout, which was default on some elements, and could be received by others via the use of certain properties, like the zoom
property (read more about hasLayout in the comphrenensive article found at satzansatz.de/cssd/onhavinglayout.html).
Now that we have all of our elements displaying as inline-block
elements, we need to make sure that they don't stagger, that is we want to ensure that the elements are all aligned vertically with one another, and that none push up above the others. To do this, we use apply the vertical-align: top;
property setting, which can can be modified to the desired alignment. For this reason, I added the @valign
parameter to the LESS mixin, to be used to set the value of the vertical-align
property.
After the Container
Our last style is for after the container, and is as follows:
.column-outer-row:after {
content: ''; /* Ensure it displays */
display: inline-block; /* Display on same line as columns */
width: 100%; /* Ensure row's content is long enough to be justified */
font-size: 0; /* Ensure (with line-height) does not take up visible space */
line-height: 0; /* See font-size */
}
LESS (Alternative)
/* Mixin */
.edc-outer-row(@columnClass, @valign: top, @fontSize: @baseFontSize) {
...
&:after {
content: ''; /* Ensure it displays */
display: inline-block; /* Display on same line as columns */
width: 100%; /* Ensure row's content is long enough to be justified */
font-size: 0; /* Ensure (with line-height) does not take up visible space */
line-height: 0; /* See font-size */
}
}
/* Rules */
.column-outer-row {
.edc-outer-row(column);
}
Details
Recall that used the IE text-justify: distribute-all-lines
properties to work around the text-align: justify;
setting requiring that the content be long enough to naturally cause a line-break. However, we now need to add support for this in all of the other browsers. As we are no longer worried about IE, we need only ensure that the above supports all browsers other than IE, which it does. What we are doing here is essentially creating a "fake" element that appears after the last .column
element that is long enough to guarantee that there is enough content to make the line wrap, thus ensuring that text-align: justify
will be applied correctly.
We start with the content: '';
setting, which we use to ensure that the browser "thinks" that there is content to be rendered. We want this content it to be blank, but white-space is okay, since we set the font-size
and line-height
to 0
. If we do not do this, some browsers will consider our "fake" element to be empty, and will set it to display: none;
.
The next setting is display: inline-block;
. Setting the element to display: inline-block;
places the "fake" element on the same line as the .column
elements, sets it to be treated as an inline
element, and gives it block
-styling so that the width
property is not ignored.
The width: 100%;
setting ensures that the line is long enough to force a line-break. As this is not a real element and there will be no actual, visible content, it does not matter that this element is "shown" on the next line, as it will be invisible.
Lastly, the font-size:0;
and line-height: 0;
settings are used to ensure that our "fake" element does not take up any visible space.
Completed Styles
CSS
.column-outer-row {
text-align: justify; /* REQUIRED for all */
-ms-text-justify: distribute-all-lines; /* IE ONLY (other browsers use :after) */
text-justify: distribute-all-lines; /* IE ONLY (other browsers use :after) */
font-size: 0; /* White-space fix */
}
.column-outer-row:after {
content: ''; /* Ensure it displays */
display: inline-block; /* Display on same line as columns */
width: 100%; /* Ensure row's content is long enough to be justified */
font-size: 0; /* Ensure (with line-height) does not take up visible space */
line-height: 0; /* See font-size */
}
.column {
display: inline-block; /* All major modern browsers */
*display: inline; /* Browser hack for IE6/7 */
zoom: 1; /* For IE 6/7 to display inline-block */
vertical-align: top; /* Prevent staggering */
}
LESS (Alternative)
/* Mixin */
.edc-outer-row(@columnClass, @valign: top, @fontSize: @baseFontSize) {
text-align: justify; /* REQUIRED for all */
-ms-text-justify: distribute-all-lines; /* IE ONLY (other browsers use :after) */
text-justify: distribute-all-lines; /* IE ONLY (other browsers use :after) */
font-size: 0; /* White-space fix */
/* Restore font-size for children */
> * {
font-size: @fontSize;
}
>.@{columnClass} {
display: inline-block; /* All major modern browsers */
*display: inline; /* Browser hack for IE6/7 */
zoom: 1; /* For IE 6/7 to display inline-block */
vertical-align: @valign; /* Prevent staggering */
}
&:after {
content: ''; /* Ensure it displays */
display: inline-block; /* Display on same line as columns */
width: 100%; /* Ensure row's content is long enough to be justified */
font-size: 0; /* Ensure (with line-height) does not take up visible space */
line-height: 0; /* See font-size */
}
}
/* Rules */
.column-outer-row {
.edc-outer-row(column);
}
Demo Styles
Now that we have finished creating our reusable styles, let's add in some styles for a demo that uses our markup.
Whether I am using LESS or plain CSS, I am a big fan of making sure that things are reusable. For that reason, we will are not going to modify the reusable styles that we defined. Instead, we will create new style rules to add to or override them.
Because LESS provides the ability to use techniques not available in plain CSS that vastly improve reusability, we will write the CSS for the demo separately from the LESS. As before, let's start by looking at the CSS.
CSS Demo Styles
The CSS style rules for our demo are as follows:
CSS
.demo-outer-row {
background: rgb(200, 200, 200);
border: 2px solid black;
min-width: 630px;
width: 100%;
height: 200px;
}
/* Override? */
/*.demo-outer-row > * {
font-size: 18px;
}*/
.demo-outer-row > .column {
width: 136px;
height: 186px;
padding: 5px;
border: 2px dashed rgb(140, 240, 255);
background: rgb(0, 200, 255);
/* Overrides? */
/*vertical-align: bottom;*/
/*font-size: 12px;*/
}
Details
The CSS styles are pretty straightforward, so we won't get too in-depth. However, I would add that if you are looking to override the font-size
property that we set on the children of the .column-outer-row
, or the vertical-align
property on the .column
elements then now would be the time to do it. However, note that if you want to use a different class for the column
or column-outer-row
elements, then you need to add that selector to the reusable rules.
LESS (Alternative)
The LESS styles are much more flexible, as LESS allows us to create functions (mixins) and to pass them parameters, including property values and CSS selectors.
LESS
/* Rules */
.column-outer-row {
.edc-outer-row(column);
/* Overrides? */
/*.edc-outer-row(column, bottom);*/
/*.edc-outer-row(column, bottom, 12px);*/
/*.edc-outer-row(column, @fontSize: 12px);*/
/*.edc-outer-row(other-column-class);*/
/*.edc-outer-row(other-column-class, text-top, 0);*/
background: rgb(200, 200, 200);
border: 2px solid black;
min-width: 630px;
width: 100%;
height: 200px;
}
/* Alternatives */
/*.demo-outer-row {
.edc-outer-row(column);
...
}*/
Since we added support for passing the class name of the column
elements, the font-size
and the vertical-align
properties as parameters to our mixin, overriding them properties is as easy as passing an optional parameter. Even if you decide to use a different class for the column
elements then you simply change wthe value that you pass for the @columnClass
parameter.
Unlike in the CSS demo, here there is one thing that we might go back and change. Because LESS is provides so much more in the way of reusability, it might be better if we remove our rule to call the mixin and instead call it here. This is because the column-outer-row
class on element isn't required anymore. Thanks to LESS, we can now call the mixin on any element that we select. In the commented example above, notice that we used the demo-outer-row
class instead of the column-outer-row
class to demonstrate this point. If we used this rule instead then the style rules would be listed for the demo-outer-row
class and not for the column-outer-row
.
Demo
Check out the attached demo, at the top of this page. Download the zip and extract it. Inside, you will find two demo files: demo-css.html and demo-less.html. The associated CSS and LESS (with compiled and minified CSS) files are included, as well. Have a look and feel free to reach out to me if you have any questions.
Alternatively, if you decide that you want to see a live demo, then check out my portfolio page. The tiles on that page are spaced using this technique. Note, however, that this site was not built with IE6/7 support, so the attached demo files (which use the same styles and classes we talked about) may be better examples to work from.