margins & paddings pt3
You can talk about margins and paddings all you like, but there comes a time when talking just doesn't cut it anymore. A time to experiment away. This article will present a first solution based on the problem and the guidelines explained in previous articles. This solution will be the first in a row of three, hopefully bringing one favorable solution to the table.
example code & test cases
<div class="parent"> <div>...</div> <p>...</p> <ul>...</ul> </div>
The code above will be used to build the three cases above. It's a very simple setup with one parent div containing three elements. For practical purposes, I will assume that the nested elements are "cleaned" of any preset css (like defined margins and paddings by browsers).
The first case is the most elementary, where all children are equally distanced from each other and their parent. The second case has the first child expanded to the total width of its parent and positioned against the top, while the third case has double top and bottom spacing for the middle child. These three cases should represent the most common occurrences of parent-child behavior seen in web pages.
the first solution: pad the parent
The first solution is a simple one and has some very obvious benefits. The idea is to apply a 4-way padding to the parent element, then add a top margin to the children (except on the first child) to push them away from each other. The advantage of putting the padding on the parent is that no matter what element gets thrown into the parent element, it will fall in line without any additional css code. A big plus when it comes to flexibility.
case 1
1/ .parent {padding:1em;} 2/ .parent>*:first-child~* {margin-top:1em;} -/ /* ------------IE6 fix ------------ */ 2.-/ .parent p, .parent li {margin-top:1em;} -/ /* ------ rule 2 alternative ------ */ 2.1/ .parent>* {margin-top:1em;} 2.2/ .parent>*:first-child {margin-top:0;}
The css above will render the first case as wanted. The first rule will add consistent padding on every side of the parent element, the second rule will give all direct children following the first child a top margin. Just what we needed. This will work for all browsers, except IE6, which doesn't know half of the combinators used in the second rule.
To fix IE6 I've replaced the second rule. This will work for our example, sadly this solution is not very useful in a real-world situation as it is impossible to predict the elements and order of elements within the parent. The alternative is to use an extra first class on every first child in the parent. Then again, implementing this in a cms is almost impossible. No matter how you look at it, fixing this in IE6 requires tweaks based on the code at hand.
There's one final remark I'd like to make about the second rule. While it works wonders, it might be a bit overwhelming for some people not used to css. And when working on projects, css files often fall in the hands of people that just need to fix little things. If you want to lower the difficulty level a bit, you could write two alternative rules which are just as easy to change. It's your call.
case 2
1/ .parent {padding:1em;} 2/ .parent>*:first-child~* {margin-top:1em;} 3/ .parent>*:first-child {margin:-1em; margin-bottom:0em;} -/ /* ------------IE6 fix ------------ */ 2.-/ .parent p, .parent li {margin-top:1em;} 3.-/ .parent div {margin:-1em; margin-bottom:0em;}
To build the second case, we start from the same code as the first case. The trick is to get the first element back across the padding of its parent. To accomplish this, we add a third rule. This rule applies a negative margin as large as the padding applied to the parent element. This will work fine in all browsers, although IE6 will act up when you try to stretch the children outside their parent element.
As for the lack of combinator support in IE6, the same remark applies to this case. The css above will fix our example, but again, its real-life value will be minimal.
case 3
1/ .parent {padding:1em;} 2.1/ .parent>* {margin-top:1em;} 2.2/ .parent>*:first-child {margin-top:0;} 3/ .parent>p {margin:2em 0em;} -/ /* ------------IE6 fix ------------ */ 2.-/ .parent li {margin-top:1em;} 3.-/ .parent p {margin:2em 0em;}
The third case is actually quite easy to accomplish as we can make use of the collapsing margins effect. We simply add a third rule that defines extra top and bottom margin on the p element. And again we add the fixes for IE6.
One very important thing to mind here is that I've chosen to use the alternative version of the second rule. The reason for this is because I lack classes for the third rule to overrule the second one (css specificity). Usually, this isn't a big problem but in this simplified case there's little else I can do.
The verdict
Obviously this solution has a couple of serious benefits. The relation between parent and children can be adequately described as long as IE6 is out of the picture. Sadly, there's no generic way to make IE6 behave with this solution, so case by case tweaking is necessary. Applying the padding on the parent element makes for a robust solution as an element can simply be thrown into the parent and it will align nicely by default.
The only real problem with this solution is seen in the second case. When you need to override the padding of the parent you create dependencies in your css. If you increase the padding on the parent element, you need to increase the negative margin on the child. Which is asking for trouble.
All in all it's a decent solution, but one that requires a little too much attention in IE6, especially when considering the margin reset on the first child. But it's a good start anyway, so stay tuned for the second solution.