From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
CSS is a mess. First introduced in ~1995, it was meant to style basic text documents. Not websites. Not applications. Text documents. It has come a long way since then. Probably a bit too long.
A lot of things were not intended in the first place like multi-column layouts, responsive web design and more; this is why it has become a language full of hacks and glitches, like some kind of odd steam machine with a bunch of extensions.
On the bright side, this is what makes CSS fun (or kinda)! And this is also why we have jobs. Because I’m personally convinced that generating efficient, cross-browser and future-proof CSS is not possible and probably won’t be anytime soon.
Anyway, I’m not here to talk about my convictions but about CSS. Today, we will do a round-up of common CSS problems in order to see how to troubleshoot CSS.
Among others, I picked some really common yet annoying issues:
- Float clearing, an old battle
- How to fight inline-block spacing?
- Understanding absolute positioning
- When to set width / height to 100%?
- How not to screw up z-index?
- What is margin collapsing?
Float clearing, an old battle
I think this has to be the most common wat? moment of CSS. It is as old as the hills, thus I’m one hundred percents sure every single person who has ever coded CSS fell into the trap.
Basically, when an element only contains floated elements, it collapses on itself. This is due to the fact that floated elements are kind of pulled out of the flow so the wrapper behaves as if he has no child at all.
There are a number of ways to fix this. Back in the days, we used to add an empty div
with clear: both
at the bottom of the container. Then, we replaced the div
by a hr
tag. Not much better.
Then Nicolas Gallagher came with a new way to clear floats from the parent without touching the markup at all. After a lot of discussions and tests to bring it down to the minimum amount of characters required to make it work, here is the latest version:
.clearfix:after { content: ""; display: table; clear: both; }
Actually I lied, this isn’t exactly the latest version, but it’s definitely the shortest. If you have to support IE 6/7, you might need to add this:
.clearfix { *zoom: 1; } /* The star is here to prevent other browsers from reading and applying this rule. */
What you probably should do is creating a .clearfix
class in your project that you can apply to your elements in the markup by simply adding it as a class. This is the simplest and cleanest way to deal with floats.
How to fight inline-block spacing?
Let’s continue with positioning elements on the same line, this time not by floating them but setting them as inline-blocks. display: inline-block
has long been underused, yet we finally figured out how it works and why it’s cool. Nowadays, more and more front-end developers get rid of floats for inline-blocks when they have the option to.
I think the main point of inline-blocks is we don’t have to deal with float clearing and other issues we can face when floating elements. Basically, setting an element’s display to inline-block turns it into a hybrid animal: half-inline, half-block. They can be sized, they can have margins but their default width depends on the content instead of being full parent width (among other specifications). Thus, they don’t stack vertically but horizontally.
There you say “what the hell is the problem then?”. Problem is since they are half-inline, they are spaced from each other by the width of a blank character. With a default 16px baseline with a regular font, that is 4px. In most cases, it is about 25% of the font-size. Anyway, this can be annoying when layouting elements. Let’s say you have a 600px wide parent with three 200px wide inline-block children. If you don’t fight this 4px space, they won’t fit into one line (200 * 3 + 4 * 2 = 608).
Thankfully, there are a couple of ways to get rid of these annoying spaces, each of them with their strengths and weaknesses. To be totally honest, there is no perfect solution yet. Let’s look at them, one by one!
Markup-side: removing the spaces
Please consider the following markup structure, corresponding to the test case I described a couple of lines before.
<div class="parent"> <!-- 600px --> <div class="child">I'm a child!</div> <!-- inline-block 200px --> <div class="child">I'm a child!</div> <!-- inline-block 200px --> <div class="child">I'm a child!</div> <!-- inline-block 200px --> </div>
As I said earlier, this won’t fit into a single line because there is one or more space-characters between each element (in our case a line-break and 2 spaces). The first way to fix the problem is to simply remove the spaces.
<div class="parent"> <div class="child">I'm a child!</div><div class="child">I'm a child!</div><div class="child">I'm a child!</div> </div>
This definitely works but it makes the markup very hard to read. Perhaps we can reorganize our tags instead of putting them all on the same line so they remain readable:
<div class="parent"> <div class="child"> I'm a child!</div><div class="child"> I'm a child!</div><div class="child"> I'm a child!</div> </div>
Or if you like being funky, you can also go like
<div class="parent"> <div class="child">I'm a child!</div ><div class="child">I'm a child!</div ><div class="child">I'm a child!</div> </div>
That’s right, it totally works! However I wouldn’t recommend this since it’s kind of counter-intuitive. We’ve been taught to avoid useless spaces inside our HTML tags and even if it isn’t a problem in itself, it makes code ugly. Let’s try something else.
Markup-side: commenting the spaces
What about commenting the spaces instead of removing them?
<div class="parent"> <!-- 600px --> <div class="child">I'm a child!</div><!-- --><div class="child">I'm a child!</div><!-- --><div class="child">I'm a child!</div> </div>
Hey, it’s better! The code is readable and it works fine. Even if it seems odd at first, you would probably get used to something like this. I personally use this method when I have to remove the gap between inline-block elements.
However, some would say this isn’t perfect since it’s a markup-side solution and this kind of layout issue should be a matter of CSS and CSS only. True. This leads us to CSS-side solutions.
CSS-side: letter-spacing
The letter-spacing
property is used to define the space between letters. The idea behind this trick is to reduce the space between letters to “override the blanks”, then resetting the letter-spacing on children so the text looks normal.
.parent { letter-spacing: -0.3em; } .child { letter-spacing: normal; }
This technique is used by Griddle, a Sass-based grid system from Nicolas Gallagher so you can tell this is a pretty serious fix. However I don’t really like the fact that we’re relying on a magic number. Plus with some fonts, you might have to go slightly lower than -0.3em
like -0.31em
or -0.32em
. Adjust to your case.
CSS-side: negative margin
One other way which is pretty similar to the previous one is the use of a negative margin. The main problem is that this fails in Internet Explorer 6 and Internet Explorer 7 which do not like negative margins. Plus, we have to remove the left margin of the first element to make our children perfectly fit into the container.
.child { margin-left: -0.25em; } .child:first-of-type { margin-left: 0; }
If you don’t have to support Internet Explorer 6 and 7 or have a conditional style sheet for these browsers, I think this is a pretty safe solution. I’d probably go with this.
CSS-side: font-size
Last but not least, you can try by setting the font-size of the parent to 0 to make the blank characters 0px wide, then restore it on the children.
.parent { font-size: 0; } .child { font-size: 16px; }
It seems to work great but it actually has a couple of drawbacks like:
- you can’t set back the font-size with
em
since the parent has a 0px font-size, - spaces are not removed in pre-Jellybean Android Stock Browser (as shown by Matt Stow),
- used along
@font-face
, text can lose antialising in Safari 5 (as shown by Doug Stewart), - some browsers don’t allow a 0px font-size, like Chinese Chrome which automatically sets it back to 12px.
So, definitely not the best solution. As I said earlier, I would probably go with the comment way. If you feel like this is all too complicated, you may get back to floating your elements, or better using flexbox.
Understanding absolute positioning
Positioning is tricky and has always been. Most beginners struggle when positioning elements on a page. They often (mis)use the position
property. This property defines how an element is able to be moved with offsets (top
, right
, bottom
and left
). It accepts four values:
static
: default value, offsets have no effectrelative
: offsets move the visual layer but not the element itselfabsolute
: offsets move the element in its context (first, not static ancestor)fixed
: offsets position the element in the viewport, no matter what their position in the DOM is
The real problem occurs when using position: absolute
. You’ve probably already encountered it: you define an element in absolute mode because you want it to be in the top right corner of its parent (like a little close button for a modal or something).
.element { position: absolute; top: 0; left: 0; }
… and it’s in the top left corner of the window. And you’re like “what the hell?”. Actually, this is the intended behavior (not by you, but definitely by the browser). The keyword here is context.
The above code basically tells “I want my element top to be positioned in the top left corner according to its context”. So what is the context? It is the first not static ancestor. It can be the direct parent. Or the parent of the parent. Or the parent of the parent of the parent. As long as it is the first which is not static.
This is a tricky concept to comprehend especially for a beginner, but once you get this you can do pretty much whatever you want with absolute positioning without crying out loud because everything is a mess.
Here is a quick demo to illustrate what we just saw. Two parents, each one with a child absolutely positioned with top: 0
and right: 0
. On the left side, the parent has position: relative
(correct behavior). On the right side the parent is static (fail).
When to set width / height to 100%?
Height: 100%
Let’s deal with the less tricky one first. When to use height: 100%
? Actually the question many of us asked at least once is “how the *bleep* am I supposed to make my page at least the height of the screen?”. Right? Right?
To answer this question, it is important to understand what height: 100%
means: the full height of the parent element. It doesn’t magically mean “the height of the window”. So if you want your main container to have the height of the window, setting height: 100%
isn’t enough.
Why? Because the parent of your container (body
) has a default height of auto
, which means it is sized according to its content. Then, you can try adding height: 100%
to the body element to see… it is still not enough.
Why? Because the parent of body (html
) has a default height of auto
, which means it is sized according to its content (you get the idea). Now what if you try to add height: 100%
to the html element? It works!
Why? Actually, the root element (still html
) is not really the uppermost containing block of your page; there is the “viewport”. To put it simple, it is the browser window. So if you set height: 100%
to the html element, you ask for it to be as tall as the browser window. As simple as that.
To sum up our story with a tiny bit of code:
html, body, .container { height: 100%; }
Done. In case you’re interested in having a more in-depth explanation about the viewport, I highly recommend you this article from PPK.
What if the parent has min-height
and no height
?
Roger Johansson recently discovered that there was an issue with height: 100%
when the parent element doesn’t have a height assigned but a min-height. I won’t go too deep into the article but summarized, you may need to add a height of 1px to the parent element so the child can expand all the way to the min-height.
.parent { min-height: 300px; height: 1px; /* Required to make the child 100% of the min-height */ } .child { height: 100%; }
More on the topic can be found in Roger Johansson’s article.
Width: 100%
Now, let’s deal with width: 100%
. First of all, a quick reminder: as for the height property, setting width: 100%
is the same as asking your element to be as wide as its parent. No surprise here.
Now let me tell you a little secret. width is a very inadequate name for this. The width
property doesn’t specify the total width of an element but its content width, which is completely different.
When adding padding and borders to your width: 100%
element, it no longer fills the width of its parent. No, it overflows. Because of padding and borders. And because width
should have been called content-width. Please consider the following demo to see what I mean.
The parent has a width of 25em
. The child element has a width of 100%
(of its parent’s width) but it also has a padding of 1em (1em left, 1em right so 2em horizontally) and a border of 0.5em (0.5em left, 0.5em right so 1em horizontally) which pushes its width to 25em (100%) + 2em + 1em = 28em. Eeerrr, Houston we have a problem.
There are four ways to fix this. The first one and definitely the best one is to avoid setting width: 100%
especially since it is useless in such a case. The child element is a block level element which automatically expands to the width of its parent (without the issue seen above). Unfortunately, if we are dealing with an inline-block child, we can’t simply do that which leads us to the next fix.
We could avoid using width: 100%
and use required width instead. In our case, 25 – (2 + 1) = 22em. Needless to say this solution sucks since we have to compute the width manually. We need a better way!
The third one would be to use calc()
to do the calculation automagically: width: calc(100% - 3em)
. It still sucks. First, we still have to compute the result of horizontal padding + vertical borders. Secondly, calc()
doesn’t have the best support in the world (no IE 8, no Safari 5, no Opera 12, no Android Stock browser).
The fourth idea is to use box-sizing: border-box
. Basically, it changes the box model so the width property is actually set to the total width of the element, borders and padding included. The best news is the browser support is very good (everything except IE 7- and Opera 9-). And for unsupported browser, we can still use a polyfill.
Long story short: don’t use width: 100%
unless you’re using a border-box
box-model.
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
How not to screw up z-index?
All boxes in a page are positioned in a 3 dimensional space: in addition to their vertical and horizontal positioning, elements lie along a Z axis. At first, this seems very simple: elements with a higher z-index appear on top of elements with a lower z-index.
Unfortunately, things are more complicated than that. ‘Cause you see, z-index is totally screwed-up. I’m pretty sure this is the most tricky CSS property in the whole history of CSS. As I’m sure z-index issues are the most frequent and annoying issues one can face when doing some CSS. Hopefully, we’ll try to enlighten the path to solutions.
First things first. The z-index
property has no effect on a static element. In order to be able to move an element on the Z-axis, you have to define it either relative
, absolute
or fixed
. So the first thing to do is to make sure your element has a position assigned before even thinking of applying z-index
.
Now, the thing to know about z-index is that all elements in the DOM are not placed on the same layer. It means z-indexing an element to a very high value may not be enough to make it appear on the foreground. This is called stacking contexts.
To put it very simple, a stacking context is a kind of group based on a single HTML element within which all child elements share the same stacking order (thus the same Z axis). Changing the Z value of those elements may make them overlap each other in the way you want. In the same stacking context, here is how elements are displayed back-to-front (from the CSS specifications):
- the background and borders of the element forming the stacking context
- the child stacking contexts with negative stack levels (most negative first)
- the in-flow, non-inline-level, non-positioned descendants
- the non-positioned floats
- the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks
- the child stacking contexts with stack level 0 and the positioned descendants with stack level 0
- the child stacking contexts with positive stack levels (least positive first)
When things get ugly
Okay, these were the very basics for the z-index property. Knowing this stuff will save you a lot of trouble, that’s for sure. Unfortunately, this isn’t enough. This would have been far too easy!
The thing is, every stacking context has its own Z scale. Basically, an element A in a stacking context 1 and an element B in a stacking context 2 cannot interact through Z-indexes. That means if element A is part of a stacking context at the bottom of the stacking order, there is no way to get it to appear in front of element B in a different stacking context which is higher in the stacking order, even with a very high z-index value.
I feel like we could make a keyword for
z-index: 99999999
, likez-index: face-punch
.— myself after seeing a lot of ridiculous z-index values.
But wait, there is even worse. The html
element forms the root stacking context. Then, every positioned (not-static) box with a z-index value other than auto
creates a new stacking context. Nothing new. Now this is where things get ugly: some completely unrelated CSS properties create new stacking contexts. Like opacity
. And you’re like…
That’s right, the opacity
property creates a new stacking context. So do the transform
and the perspective
properties. This makes absolutely no sense, right? Basically, if you have a totally random element with either a lower than 1 opacity or a different from none
transform, you are on your way to potential issues.
Unfortunately, every z-index issue has its own context (no pun intended) making it very hard to provide a bulletproof advice for all these case. In the end, we can draw a few conclusions:
- Always make sure your element has a position defined before applying z-index
- Stop the 5+ digits z-index, it is absolutely pointless; in most cases something like
z-index: 10
is more than enough - Make sure the elements you want to reorder are part of the same stacking context
- If you still have an issue, make sure you don’t have a transformed or opacified element along the way
On topic, I highly recommend you to read What No One Told You About Z-index by Philip Walton and the official CSS specifications from the W3C. A lot to learn.
Fighting margin collapsing?
I think this is one of the CSS glitches which took me the most time to wrap my head around. I guess you can tell this is as twisted as z-index in a sense. Anyway, margin-collapsing is when top and bottom margins of two elements collapse into the greatest margin of both. For those of you who have a mathematical brain, vertical margin between two blocks is margin = max(block1.marginBottom, block2.marginTop)
.
Fortunately, this is usually the desired behavior. This is probably why it works like that (as intended and described in the CSS specifications). However, sometimes you don’t want a vertical margin to collapse. To understand how to fix this, we first have to see why this happens. Margin collapse can happen in three different cases.
Adjacent siblings
When two adjacent siblings have vertical margins, they collapse into the greatest margin between the bottom one of the first element and the top one of the second element. This may be prevented in a couple of ways:
clear: left; float: left;
on siblings (right
works too)display: inline-block
on siblings (inline-table
works too)
The following JSFiddle shows these fixes in action.
Parent and first/last child
Usually, the parent’s top-margin and the first child’s top-margin collapse into the greatest margin of both. Similarly, the parent’s bottom-margin collapse with the last child’s bottom margin. This phenomenon is also known as “ancestor’s collapsing”. There are multiple solutions to fight this behavior. Most of them consist of adding one of the following properties to the parent element:
overflow: hidden
(or anything else thanauto
)padding: 1px
(or any value greater than 0); some browsers even support sub-pixel values)border: 1px solid transparent
(or any border)display: inline-block
(inline-table
works too)float: left
(right
works too)
The following JSFiddle shows these fixes in action:
Empty blocks
When an empty block has no border, nor padding, nor height, its top and bottom margins collapse into a single one. Then, it can also match one of the two cases above, collapsing once again with the margin of a parent/sibling. However, empty elements are a bad idea in general, so I guess this isn’t a case you’ll encounter very often.
Final words
Waw, that was pretty dense, wasn’t it? And unfortunately this is only the top of the iceberg in matter of bugs, hacks and glitches. Those are the most common problems one can face when doing some CSS, but there are so many more than this, like browser inconsistencies, vendor prefixes mess, selector specificity, cascade and inheritance, and many more.
I recommend you these websites and articles on the topic:
- What No One Told You About Z-index by Philip Walton
- Height in percent when parent has min-height and no height by Roger Johansson’s
- Official CSS Specifications from the W3C
- Micro clear-fix hack by Nicolas Gallagher
- Display inline-block, why it rocks and why it sucks by Robert Nyman
- Browserhacks by Tim Pietrusky and me
Anyway, I hope this helped you understanding quite a few things and save you some future problems. Thanks for reading, see you! 😉
What a freaking long list of awesome stuff!
Dam that was a rather good and well written article.
The “commenting spaces” trick was the only one I didn’t know and imho it seems like a far superior fix than the others, so thank you a lot for that.
Yep, that’s the tip of the iceberg, CSS has a quite simple syntaxe but is not that easy to master. Thanks a lot for this little reminders Hugo 😉
This actually helped me understand some of the stupid issues i sometimes run into. Thanks 😀
Very good article. I would love to see more 🙂
Another thing I don’t get is having a child with a “margin-top” as the first element inside of the “” tag causes the body to have a margin top.
It’s called collapsing margins and is a normal behavior as in ‘it’s supposed to do so’, but either way it can be indeed annoying sometimes.
Here’s some reference… It’s a pretty old article, but still stands:
http://reference.sitepoint.com/css/collapsingmargins
Duh!?! *confused* It’s mentioned in this article too!
Really nice sum up, thanks a lot!!
Really? I have to say that u save my life with this post man… It’s always good learn anything new 😀 – Great post!!!
loved the part when you say “CSS floats, Y u no clear yourself?”
.element {
position: absolute;
top: 0;
right: 0;
}
You then mention underneath it goes in the top left of the screen, others might get confused as the CSS states top right!
Awesome post, thanks for writing it.
It was a nice reading, thanks a lot for the good work. I have battled with each one of these CSS issues (and many others unfortunately) anf from time to time I end up googling for the same old question again and again (“Why it don’t go 100% height?”, “Clearfix”, etc). Having it all summarized in here was a good reading and helpful as a sheet for future 🙂
Hey!
Interesting post!
I design all my horizontal lists in Inline-block. They is an other way to fight the extra space. U can simply omit the closing tag. Some closing tags are optionnal in HTML5 (html, head, body, p, li …). Then you can style your like and it’s semantically better 🙂 Eg :
<ul class="parent"> <li class="child">I'm a child ! <li class="child">I'm a child ! <li class="child">I'm a child ! </ul>
Works with li’s but not div’s. That’s why I didn’t talk about it.
Very good article! Thank you very much.
Great Stuff !!
I still want to stab height:100% in the face. But this was overall really great and well written.
This was so awesome! Thank you!
For the clear i just use overflow:auto when i can. It works, there is some problem in that?
At first I wanted to talk about this one, then I guess I forgot.
There is no problem with this; in fact it’s probably better since it’s way shorter. The only issue I can think of is if you have some box-shadow or offset inside content. They will be cut off.
Overflow is often desired without the scrollbars.
This is generally my solution as well.
A great article i definitly owe you a beer.
Well, this is getting interesting.
As per the first
inline-block
solution, why not just use tabs for spacing in the IDE? In fact, why not use tabs altogether?Doesn’t make any difference. The problem isn’t retricted to spaces but to all blank characters, tab included.
What an awesome coffee mug! Where can I find one?
Logan, found them here:
http://www.zazzle.com/css_is_awesome_coffee_mug-168716435071981928
Great article, I’m a developer, and occasionally get pulled into the presentment layer, and occasionally run into some layout/CSS issue that drives me _insane_.
I think I’ll buy one. It’s a hot mug.
Hugo, awesome article. This is the stuff that nightmares are made of for front end devs starting out (and seasoned ones, come to think of it). Nice work 😉
Great Article!
I didn’t know all display: inline-block solutions.
Thanks!
There is “a perfect solution” for inline-block spacing via Css.
For Opera that not support negative values for word-spacing
For Safary that not support font-size: 0
and old IE´s too
It is independent of the font family (the values for negative margins and letter-spacing are depending of each typeface).
Awesome. Very useful and claryfing article on those mystical css mysteries.
Great article… It took me a loooong time to realize that actualy knowing what did what on css instead of just ‘making it work (or not)’, was essencial to do a good job.
Thank you so much; amazing idea to do such this; organized article ))
Quite an interesting, pretty dense and very good explained article, regarding some of the most common “WHAT” problems with CSS out there 🙂 Good job, Hugo!
You are a freakin genius!!! People should write more posts like this. It all could have been sooo much easier in the past^^ Please make a series of such post this would help our community a lot!!!
Amazing article!
It would a great help if I read it a couple of years ago. Now it’s all tried and true by myself.)
You impress me Hugo. I keep notes on my phone for potential blog posts, of which you covered at least 3 in one gigantic post! 🙂
You didn’t mention not fighting inline-block spacing though. Forgive me for blatantly advertising my own website but if anyone is curious – click.
Another trick to emulate the 100% width with padding/borders that can be useful is to position the element with no width and set left: 0 AND right 0 to cover the width of the parent.
.child { background: tomato; position: relative; left: 0; right: 0; padding: 1em; border: .5em solid; }
Works with position: absolute and fixed. Not with position relative. 🙂
You are right, the above doesnt work with pos relative, cheers
Amazing article!
hey, this is a huge help! and the resource you recommend is very helpful ,too, thanks a lot, keep going!
with display: inline-block
try white-space: nowrap
(on parent container)
Bookmarked this one 🙂 Nice to have all these tips in one place, really well written too
I hate CSS. That’s it, no more. wont do it. I refuse. na na na na can’t hear you.
Great article, thanks alot!
Re: How to fight inline-block spacing?
What the world needs is { white-space: remove; }
MakeItOnlyApplyToBlockElements.YesThereWillProbablyBePerformanceIssuesIfAppliedToAllParagraphsOnAPageButThat’sNotWhatItIsFor.InTheContextOfFixingInlineBlockForUseWithPageLayoutThereWon’tBeNearlyAsManyTextNodesToRemoveComparedToUsingItOnParagraphs.
Great post. Even better pics!
Never use html, body, whatever{
height: 100%;
}
if you have enough content so you have to scroll your html, body, whatever has the height of the browser without the scrollable content
It doesn’t make much difference in the end. I guess you could use `min-height` instead of `height`.
min-height will not expand body/html and it does make a different as soon as you use backgrounds
Great stuff – thank you!
You have no idea how often I yelled, screamed, cried or otherwise wanted to throw the stupid screen out of the window because of one of those situations you mention here. I think I got this close to get thrown out into one of those “self hugging” white suite, inside a padded room.
As a junior front-end developper, your article is a real life and state of mind saving.
You’ve just gained the SuperHero status! If you were a girl, I’d kiss you. 😉
“La vache, t’es trop top mec!”
Excellent ! et en plus très bien écrit, “à l’anglo-saxonne” ! Je me suis régalé.
putaint
Sorry, just because I don’t understand what you said 😛
Yes, it is well-written. God bless Hugo because he doesn’t split infinitive just for the fun of it. I’m referring to “How not to screw up z-index?” by the way. It’s a pet peeve of mine.
Alex, Jean-Sam wrote that the article was not only excellent but also well-written in the anglo-saxon tradition. He loved it.
Great article! I was aware of most of these quirks but now I know exactly how to fix them. Sure beats playing CSS roulette!
Cheers
Great article, Hugo and very much appreciated. Thanks for sharing.
Nice article! Thanks for the other options on fixing these quirks. Good to add to the long options list whenever something goes wrong in CSS again.
Nice article. Thanks
I love you. That’s all.
Well, I love you too. 🙂
Nice artical. You really rock…