Troubleshooting CSS

Sometimes CSS can be frustrating. Learn about some tricky properties, the common issues they can cause and how to solve them.

Troubleshooting CSS

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.

css-is-awesome-700x375

Photo credits

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

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.

css-floats-y-u-no-clear-yourself

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 effect
  • relative: offsets move the visual layer but not the element itself
  • absolute: 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.

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):

  1. the background and borders of the element forming the stacking context
  2. the child stacking contexts with negative stack levels (most negative first)
  3. the in-flow, non-inline-level, non-positioned descendants
  4. the non-positioned floats
  5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks
  6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0
  7. 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, like z-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…

wat

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 than auto)
  • 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:

Anyway, I hope this helped you understanding quite a few things and save you some future problems. Thanks for reading, see you! πŸ˜‰

Tagged with:

Kitty Giraudel

Non-binary accessibility & diversity advocate, frontend developer, author. Real life cat. They/she.

Stay up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 98

Comments are closed.
  1. Great article! Your explanation about absolute position really opened my eyes.

  2. Great post! As i was reading it, my own problems came right before me. Faced most of them in my long career as a CSS designer. I am now more than convinced being a good graphics designer is all about being proficient with css.

  3. Thanks for all the information but I am still a little confused with the positioning system. Can you please put more examples in there.

  4. You can also use “overflow: auto” to fix the problem with the floated elements.

  5. What a great article!! I feel SOOOOO much better about my skills now. Just about every problem that is described here, I have had to deal with. I have wanted to scream and have worked hours trying to fix some of these issues. Thank you for showing me that these issues are very common and that maybe I am not the world’s worst web designer.

  6. Amazing Article Hugo. I am one of those beginners who struggle with these types of issues everyday at work. And sometimes (well, okay most of the time, I say to myself “WTF!” am I doing, will I ever learn CSS?”) So, it is good to hear and see others struggling with this besides Myself

    I love the related articles at the bottom of this post as well. Keep up the amazing work you all do at Codrops. You all are my “Go To” site for inspiration, help, and just everyday work related issues that I run into everyday. And I love the tutorials as well.

    Codrops for LIFE

  7. In this fix for margin collapsing you make a mistake:

    overflow: hidden (or anything else than auto)

    work with hidden, auto and scroll
    don’t work with visible

  8. That method of clearing floats is still too much. You don’t need a clearing class at all (which still gets into the markup); setting the overflow property on the parent (containing) element is all that’s necessary.

  9. Thanks for the article Hugo! I was surprised i’d never tried out the box-model method. I love how it works. I love how this works like we all want it to work. There’s only one big concern for me, and that’s the IE support. Personally I’m not a big fan of the .htc files so to go with the CSS solutions provided we still end up defining widths minus paddings for the .no-boxsizing class. […] Between writing this last dot and the square brackets I came to the conclusion I might be willing to go with the .htc file for this… darn.

  10. Good read. Really the best rule of thumb is to avoid issues like this before you ever take a design into full development. Really take time to look at the design and spot potential future issues. You wont catch them all, but you can at least limit damage this way. Sucks these exist, but they have been there forever! You really should avoid them; because these seem to be the first thing that will break when a new version of IE comes out ect.

  11. That was complete package of solutions on the css. Great article, i had to read it for 3-4 times to save it in my head. I have book marked it now.. thanks for the article.

  12. You should have pointed out at z-index that setting a negative value (like -1) will screw the whole thing up. Setting a negative value anywhere in the CSS for z-index will make it stop working for the entire site, so be careful with that. No negative values!

  13. Excellent article. Over the years I’ve encountered and solved all of these issues many times (and wasted who knows how much time). Seeing the solutions alongside their root cause has been a great help.

  14. I fucking love you! In three seconds, you were able to help me fix a major bug affecting the quality of my CSS Code: achieve 100% width with display: inline-block elements.