"The Jello Mold Piefecta" by Mike Purvis
Minimum Width
One of the keys to success in liquid layouts is a complete set of tools for precisely controlling a document's width, as viewed on the screen.
Non-support in IE has rendered the CSS2
min-width
property useless for now. However, with a few devious styles and some innocuous IE-fixes,
we can impose a natural minimum width on any element.
And as an added bonus, there's a new type of width governance-- a cross between a fixed width and a percentage width.
If you change the size of your browser, you'll see that the header and footer bars on this page are exhibiting this behaviour, at 700px. (IE6, IE5/Win, IE5/Mac, Safari, Opera [versions?])
The Secret Sauce: Negative Margins
Most CSS properties that control spacing are about making an element smaller than it would otherwise have been. Margins, padding, and borders all shrink an element inside its containing space. But negative margins are unique in that they make an element larger than its parent.
The narrowest an ordinary element can be is zero pixels wide. So if an element can be expanded 700px wider than its parent element, that nested child element's minimum possible width becomes 0px + 700px, or exactly 700px.
In other words, if the parent element is 0px wide and its child is always 700px wider than its parent, that child element cannot ever be narrower than 700px. When negative margins are applied to both sides of the widthless nested child, the effect is to widen that child element in both directions, causing it to appear wider than its parent!
Note that the outer element will typically be the body element in
most production pages, so for this code example we'll give the outer div a
id="bodydiv" as a substitute "body" element, and we'll call the
outer element #expander, since its negative margins will be expanding
its displayed width.
Here's some example markup upon which we'll apply the minimum-width effect, via negative margins:
<div id="bodydiv">
<div id="expander">
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</p>
</div>
</div>
Now we'll use those negative side margins to make #expander 700px
wider than #bodydiv. This can be done all on one side of
#expander, but to simplify centering we will split the total expansion
and place half on each side of #expander. And so that we don't let
those negative margins run the sides of #expander straight past the
edges of the window, we'll apply the same 700px total as as left and
right padding on #bodydiv. Here's the styles:
#bodydiv { padding: 0 350px 0 350px; }
#expander { margin: 0 -350px 0 -350px; }
Two divs, two styles. However, for IE to treat the negative margins properly, several fixes must be applied:
#expandermust beposition:relative,- all the
divs must be Holly Hacked, for maximum compatibility, and - there must be another
divin between the pair, with a width of 100%.
The first two problems are easily solved; the third is the more frustrating
and confusing. But it seems that IE will not properly set the width of
#expander to 700 pixels more than #bodydiv unless
there's an in-between element with an explicit full width of 100%. So the final
minimum-width code becomes:
<div id="bodydiv">
<div id="sizer"> <!-- new IE bugfixing div -->
<div id="expander">
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</p>
</div>
</div>
</div>
#bodydiv { padding: 0 350px 0 350px; }
#sizer { width: 100%; }
#expander { margin: 0 -350px 0 -350px; position: relative; }
/* Holly Hacks \*/
* HTML #bodydiv,
* HTML #sizer,
* HTML #expander { height: 0; }
/* */
Be aware that the Holly Hack above has a height value of zero, rather than the usual
1%. This is because any percentage width or height on #expander will collapse that
div down to the width of the longest word or image inside, when viewed in IE/Win.
Both forms of the Holly Hack work equally well and in the same manner, so there
is no new risk involved.
Safari will lose the negative side margins on #expander if the Jello Mold is loaded into a window that is narrower than the sum of those margins, so to fix this a simple min-width at that value is applied to #expander:
#expander { margin: 0 -350px 0 -350px; min-width: 700px; }
This just reinforces what Safari ought to be doing anyway, so it doesn't harm any other browser, not even IE, which ignores min-width anyway.
The Jello Mold
In the styles above, there's one very interesting number that just begs for
attention. It's the 100% width on #sizer. If purely
in the interest of science we should change that from 100% to 50%, what will happen?
That very change has been applied to this very page you are reading! Try RE-sizing your browser window by dragging it wider and narrower, and watch what happens to the white content as compared to the side spaces.
The content is showing a minimum width of 700 pixels (with a horizontal scrollbar
at narrower window widths), and with the old #sizer width of 100%,
the content would always fill the window at larger window sizes.
But what does the page do when #sizer is set to width:50%
and the browser window is wider than 700 pixels?
- Subtracts 700 from the width (padding),
- Takes 50% of what's left (width), and
- Adds 700 to it (negative margin).
May not sound like much, but check out the size of this at different widths:
| Browser Width | Jello Width | Percentage | |
|---|---|---|---|
| 800px | (800 - 700) * 0.5 + 700 = | 750px | 94% |
| 1024px | (1024 - 700) * 0.5 + 700 = | 862px | 84% |
| 1280px | (1280 - 700) * 0.5 + 700 = | 990px | 77% |
| 1600px | (1600 - 700) * 0.5 + 700 = | 1150px | 72% |
| 2560px | (2560 - 700) * 0.5 + 700 = | 1630px | 64% |
At the page top you can see grey sides that are the 350px side body paddings,
and between them is the green colored #sizer div, which is set to
a width of 50%, making it stay at exactly half the width of the available space
between the body paddings.
What's happening is that the percentage of the window occupied by the page
content is large when the window is narrow, and that percentage shrinks as the
window is widened. So not only is there a minimum width, but the page also expands
horizontally at a slower rate than the window as a whole. You get to decide how
quickly the percentage shrinks, simply by selecting different percentage widths
on #sizer.
Centering
Normally a flexible layout such as this will need to be centered in the browser.
This is done by applying side auto margins to #sizer.
Here's the code:
<div id="bodydiv">
<div id="sizer">
<div id="expander">
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</p>
</div>
</div>
</div>
#bodydiv {
padding: 0 350px 0 350px;
text-align: center; /* centering hack for IE5.x/Win */
}
#sizer {
width: 50%;
margin: 0 auto 0 auto; /* standard centering method */
text-align: left; /* resets centering hack to default */
}
#expander {
margin: 0 -350px 0 -350px;
position: relative;
text-align: left;
}
/* Holly hack for IE \*/
* HTML #bodydiv,
* HTML #sizer,
* HTML #expander { height: 0; }
/* */
The side auto margins on #sizer must always be equal, automatically
centering #sizer in standards compliant browsers, including IE6.
Previous IE/Win versions don't understand side auto margins, so we have to use
a text-align hack on the outer element, which wrongly centers all content in those
earlier IE's. Then that hack is reversed back to the default in #sizer
so that we don't center all of the page's text.
As mentioned before, the body element will usually be the outer
element, rather than a div like #bodydiv.
One More Pitfall
IE becomes confused with internal percentages. Often you
will want percentage sized floated columns in your layout, as is the case in this
page. Their widths are defined as a percentage of #expander, their
direct parent element.
Unfortunately, for IE to get this correct it needs to have a parent element
with an explicit width, not a weird one derived of negative margin
wizardry. So in this page and the bare-bones demo, there's a fourth div
nested inside #expander whose only purpose is to be width:100%.
This inner wrapper fills #expander, and is itself width defined,
so any internal columns will have a clean width reference to calculate against.
If no nested elements need percentage widths, this inner wrapper won't be necessary. However, if coders who come after you don't understand this bug, it could get nasty for them so you might want to have this div just in case. Also since it's a full wrapper, you can always use it as a graphical hook.
Maximum Width
Screens will keep on getting bigger and users will keep on maximizing their browsers.
At some point there does need to be a constraint put on content width. For most
modern browsers, a maximum width may be declared using max-width.
As with min-width, however, it is unsupported in IE.
However, IE does support a non-standard method by which any property's value may be expressed as a snippet of Javascript. IE5/Mac didn't incorporate this, so it gets left in the cold. But for everyone else, the following code imposes a maximum width of 1000px on the white content area.
#sizer {
width: 50%;
margin: 0 auto 0 auto;
max-width: 300px; /* 1000 - 700 = 300 */
width:expression(document.body.clientWidth > 1300 ? "300px" : "50%" );
}
In the interests of validation, the expression should be wrapped
in a conditional comment.
This hides it from better browsers, and from the validator. Unfortunately, IE5/Mac
never implemented conditional comments, so there's no known way to impose a maximum
width effect on IE5/mac without involving fully-fledged Javascript. Here's what it
looks like with the comment:
<style type="text/css">
#sizer {
width: 50%;
margin: 0 auto 0 auto;
max-width: 300px; /* 1000 - 700 = 300 */
}
</style>
<!--[if IE]>
<style type="text/css">
#sizer {
width:expression(document.body.clientWidth > 1300 ? "300px" : "50%" );
}
</style>
<![endif]-->
The expression gets the window width and checks it against the leading value of 1300px
to see if the window is wider than that. If true, the first value after the question
mark (300px) gets applied as the #sizer width, and if false, the
second value (50%) is applied instead. The 300px value is found by subtracting the
700px negative margin total from 1300px to get 600px, then multiplying by 50%,
arriving at 300px for the "true" value.
The maximum width in this case has been used simply as a safeguard to protect the readability of your site on very wide screens (in excess of 1300 pixels). Depending on your design, you'll need to play with the minimum and maximum width controls that the Jello technique offers. But that's the beauty of it-- you can!
A Tool and A Minimal Case
Two Column Fluid
A sample Jello Mold codeset, at its bare-bones minimum: here
Jello Generator
The exact 700px/50% combination on this page produces the widths shown in the table above. But if you'd like a different set of values, the Jello Generator can help you find the exact pair you want.
Last Words
The Jello Mold all about control. You control your maximum and minimum widths, you control exactly what width you'RE showing at the resolutions in between. This opens up the door to flexible designs of a sort that are simply not possible without such precision. No more worrying if your floats break up on those very wide and very narrow screens.
Of course, this is a pretty new technique. We've been careful about checking it out, but keep your eyes open for those scary bugs lurking where no one has seen them yet. And if you do find something odd, let us know!
Copyright © 2004-2006 Mike Purvis
Mike kindly gave his permission to feature his original article here on this blog
Posted ·2006-03-13
© 2006-2008 marios buttner
send this article to a friend
send articleCommenting is closed for this article.