Grow your CSS skills. Land your dream job.

Spacing The Bottom of Modules

Published by Chris Coyier

Ah the humble module! A good many designs these days make use of modules in the content-y and app-y sites alike. A chunk of information, an advertisement, a grouped set of functionality... could be anything. The fact that they likely have visual similarity yet can contain anything leads to an interesting CSS challenge: how do you pad the inside consistently?

Here's a simple example where the module is set apart from the background through color differences:

html {
  background: #333;
}
.module {
  width: 20rem;
  padding: 1.5rem;
  margin: 1.5rem auto;
  background: white;
}

Easy cheesy. Nothing to it. Evenly padded.

But as soon as that module contains other elements, the padding can get "off".

<div class="module">
  <h3>Module</h3>
  <p>Pellentesque habitant morbi tristique senectus...</p>
</div>

The paragraph will very likely have some global typography rules applied to it which very likely have some bottom margin. Even if you go with opt-in typography, you'd probably opt-in here since there is type present.


Paragraph in a module.

See the heavy bottom padding? Perhaps that's OK to you, but I'd suspect it's not. That heaviness comes from the consistent set of padding you've set on the module itself plus the bottom margin on the paragraph.


Red = bottom paragraph margin

So what do we do here? A wet band-aid would be to remove margins on those paragraphs.

.module p {
  margin: 0;
}

But I'm sure we agree that's pretty gross. What if a module contains two paragraphs? Now they butt up against each other awkwardly.

Perhaps just the last one?

.module p:last-child {
  margin: 0;
}

That might work. But this fix is still feeling a little hacky. What if the problem element isn't a paragraph at all someday? It could be an <ul> or <ol>. Or <dl>. Or any other HTML element... Are we going to list every single HTML element out?

.module p:last-child,
.module ul:last-child,
.module ol:last-child,
.module dl:last-child {
  /* losing battle */
  margin: 0;
}

That's a losing battle for sure.

But we could match any element...

.module *:last-child {
  margin: 0;
}

You could even omit the "*" there since it's implied and has no specificity, but I think it makes it more clear. I think that's closer to a good fix, but it's a little heavy. Lists always have child elements that this would effect. Anything with decendants would be a problem. To fix that, we'll just ensure only the top level element under the module gets this treatment with the child selector.

.module > *:last-child {
  margin: 0;
}

That's decent. I still wouldn't call it perfect since descendants might actually be the cause of the final extra bottom padding. Like if the list items had paragraphs in them. You might want to chain them up a few times to match up to a reasonable level of DOM depth.

.module > *:last-child,
.module > *:last-child > *:last-child,
.module > *:last-child > *:last-child > *:last-child {
  margin: 0;
}

That might just do it.

Here's a visual demo:

See the Pen JFynD by Chris Coyier (@chriscoyier) on CodePen


A totally different approach might be to leave the bottom padding off the module and let the child elements provide it, but that feels a bit dangerous to me as there is no guarantee the child element would come with its own (e.g. some generic <div>).

Have you run into this? Do you have your own solution you like?

Comments

  1. Permalink to comment#

    What I did was exactly up to the second to last block of code. Didn’t really think about chaining up the DOM a few times just to be safe. Either way, feels pretty good to see we have the same solution to this whole trailing margin malarkey.

    • Luca R.
      Permalink to comment#

      I always hear about the poor performance of the * selector. So, isn’t chaining it a threat to performance?

      Does someone know how much impact does it have on a real web page?

    • Permalink to comment#

      @Luca R.: I always use this method and I’ve never experienced any performance issues. But I guess it depends on the complexity of the project.

    • Luca – I think that the performance of the * selector is more talking when you apply it to EVERY element on a website, * {} like. And even then I’m pretty sure that it’s been covered here that it’s not an entirely too bad of a hit on performance.

    • Luca R.
      Permalink to comment#

      Well, that’s good news. :)

  2. I have definitely run into it, but used something like your first iterations of the fix only to find out later I was losing the battle. And that’s where I gave up.

    Thanks a lot for shedding some light!

  3. Hey Chris, what about the following?

    http://codepen.io/catalinred/pen/yHsop

    .module *:last-of-type {
    margin: 0;
    }

  4. I would do this

    .module *:last-child {
    margin-bottom: 0;
    }

    • Permalink to comment#

      Yes! I was just using this code to solve the bottom spacing issue and ran into other spacing issues with the removal of all margins. margin-bottom: 0 is the way to go!

    • Doug
      Permalink to comment#

      The article doesn’t go this way because “it’s a little heavy”. Any way to optimize without chaining child selectors?

  5. Adam
    Permalink to comment#

    This is exactly what I have done in the past. I didn’t think of decendants, but then again the issue had never came up. It is a nice solution :)

  6. I normally solve this in one of two ways:
    1. Make the module have less padding at the top and bottom, which gets compensated by the content’s margins.
    2. Set a 1px padding on the bottom of the container (in case the margin of the contents is the same as the padding of the container).

    Number 2 is a bit ‘hacky’ and should only be used if you’re in full control of what goes into the container (and you’re sure the lay-out won’t change).

    :last-child doesn’t go well with IE8 (although you might not be interested in fully supporting IE8).

    Alternatively you can set all contents to have only top-margin, that way you can target :first-child, or use an OOCSS class structure (but that’s more mark-up).

    Only top-margin isn’t completely nice either, normally you’d want either both top and bottom margins or at least the bottom one, but I guess it’s an option.

  7. philtune
    Permalink to comment#

    I’ve always done the wet band-aid approach, .module p:last-child { margin: 0; }, and then just pay attention to if I add any nested elements later on and manage them then. Quite messy and inefficient. I’m interested in a better way too!

  8. What I usually do is add the margin on top of adjacent sibling elements, instead of on bottom of every element. I use something like:

    p + p { margin-top: 1em; }

    I adapt this depending on the content module. It works well, most of the time.

    • Permalink to comment#

      That’s a killer idea, I’ll have to remember that.

    • Luca R.
      Permalink to comment#

      I used this method too. Works fine with paragraphs, but it’s hard (and verbose) to make it bulletproof for all the possible adjacent elements combinations.

    • Rasmus Fløe
      Permalink to comment#

      I was going to suggest the exact same thing.

      We use this technique at work. :)

    • Paulo José
      Permalink to comment#

      The best approach, until now. :-)

    • Dean
      Permalink to comment#

      Beat me to it!

  9. Hey Chris,
    Just curious about your use of the universal selector – does it do anything in the example? Does it help or hurt performance? What about removing it: http://codepen.io/mikevoermans/pen/ILida

    Just curious your thoughts.

  10. Another solution is to use additional classes:

    // removing margins for first/last elements
    .first { margin-top: 0 !important; margin-left: 0 !important; }
    .last { margin-bottom: 0 !important; margin-right: 0 !important; }

    I use them when I know that added :last-child is overkill, e.g. because a case is special and the rule would match one element on the entire site.

    • Ryan Boone
      Permalink to comment#

      Yeah, this is exactly what I was thinking. It’s better to rely on classes than it is on advanced selectors, even if it’s clunkier, although, in this case, I would argue this is a bit more elegant.

    • Ryan Boone
      Permalink to comment#

      Well, I say “it’s better.” I mean I FEEL it’s better to rely on classes vs. advanced selectors. I think advanced selectors work better in an atmosphere where you are in control over the content, i.e. a personal website.

    • Kyrodes
      Permalink to comment#

      Not really. If the module content comes from a CMS how will you add the first and last classes?
      You will need to insert it via backend or javascript.
      The advanced selectors will work fine if the content comes from a CMS.

      I hate when I get a redesign project and I need to keep the previous html but it is full of extra classes, like “clearfix” everywhere.
      The HTML have to be simple enough to CSS be replaced anytime. Using too much extra classes is not a good idea.

      At least, in that case, the first and last classes will do nothing if i replaced the CSS.

  11. Why not use a negative margin on the bottom of .module?

    • Kyrodes
      Permalink to comment#

      You tried this?
      The problem is not with the module margin, but with the module padding.

  12. Art Lawry
    Permalink to comment#

    I think this should work at the expense of an :after pseudo-element (which may or may not already be accounted for in your real world markup).

    .module > *:last-child:after {
      content: ' ';
      margin: 1em 0 -1em;
      padding: 0;
      display: block;
      height: 0;
    }
    

    here’s a link to your modified codepen with the solution in place. Haven’t tested across all browsers yet. The top margin needs to be greater than any bottom margin on a contained element and will force a column-collapse with the last element so that you have a consistent amount of extra space at the bottom of each module. The bottom negative margin just compensates for that collapsed margin by swallowing it up. I usually look to negative margins for tricks like this.

    • Paul d'Aoust
      Permalink to comment#

      I really really like this solution. As fsr as I can understand it without testing it (I’m on a phone), it accommodates the bottom margin of any last element (even grandchildren) then swallows that margin up again with its own negative margin, right? Brilliant; I’m looking forward to trying it. Box formatting contexts are a thorn in my side, and I always felt uncomfortable relying on styling the last direct descendant — o4 a chain of last d3scendants of last descendants of last descendants. It always seemed too brittle and reliant on predictable markup. This one Just Works — unless one of the descendants creates its own block formatting context and prevents margin collaps3

    • Jacob Ross
      Permalink to comment#

      I really like this solution solely because of the use of :after pseudo selector. I suppose this would work with IE 8 and up.

  13. Why not add top padding to every second element? Something like

    .module > * ~ * {padding-top:1em} // every child after the first element

    That way, you separate stuff, and no bottom padding?

  14. Shane Murphy
    Permalink to comment#

    I use .module > :last-child { margin-bottom: 0; } + a generic helper class .vertical-rhythm--none { margin-bottom: 0; } for nested or oddball cases. I only apply vertical margins in one-direction (down).

    • Luca R.
      Permalink to comment#

      I think this is the best solution so far.

    • Scott
      Permalink to comment#

      I’m a huge fan of single direction margins, it makes everything amazingly predictable! I heard it first from CSS Wizardry.

  15. Johnslegers
    Permalink to comment#

    I’ve been using the following code in Cascade Framework to deal with the extra padding across the site’s grid structure :

    .cell>:last-child {
    margin-bottom: 0;
    }

    I chose to use margin-bottom : 0 instead of margin: 0 because the selector is too generic to throw in a reset for top, left and right margins as well.

    I went only one level down for the same reason. I may want a bottom margin for the element’s children or its children. With such a generic use case, there’s no way of knowing. And if you really do need a margin reset for children’s children and so forth, it seems better to me to just use class based selectors to adjust them.

  16. I tend to do it once for all block elements at the base typography level rather than at the module level, eg (Sass)

    p
    margin-bottom: 2em
    &:last-child
    margin-bottom: 0

    Cases where I actually want a p, ul or ol that is a :last-child to have that bottom margin are few & far between, so it’s easy & sensible to override those edge cases at the module level.

  17. scarbom
    Permalink to comment#

    true vertical centering often “looks” wrong. that’s why in print you’ll often see if weighted to the top somewhat. to my eyes, your “heavy bottom padding” looks better than no padding at all.

  18. Permalink to comment#

    Wow, much tidier approach than my methods… will be using this from now on. Thanks!

  19. Ryan Boone
    Permalink to comment#

    If only this could be checked with logic in the CSS itself, like a media query.

  20. I usually add top margin/padding instead:

    .module p {
    margin: 20px 0 0;
    }

    Here is example.

  21. Al F.E.D
    Permalink to comment#

    Or some simple math:

    $mod-bottom-padding: 1.5rem;
    $mod-element-bottom-margin: 1rem;

    .module {
    padding-bottom: $mod-bottom-padding - $mod-element-bottom-margin;
    }

    Ok if your module elements all have the same bottom margin.

  22. David Smith
    Permalink to comment#

    What about using collapsing margins and pseudo elements. See

    http://codepen.io/getdave/details/gfEiq

    We create a psuedo element, apply top margin which then “collapses” into the bottom margin of the last element. Works best if the module padding is based on the base line height/margin.

    • Permalink to comment#

      Wow this is smart! Seems pretty fool proof as it’s relying on the logic of margin collapses rather than double guessing with CSS selectors. Are there any cases where it doesn’t work?

    • John Ferris
      Permalink to comment#

      This is brilliant! Do you need the height 1px? Won’t the margin still appear if it’s display: block even if there is no height?

  23. IMHO, the only predictable, sustainable solution is for the markup to be more descriptive. By specializing the module classname, you can address your design need in a straightforward, idiomatic (and performant) way. Demo pen.

    (There are other ways to enrich the markup. You can leave the module generic, but wrap the contents in an element that has a specializing classname. E.g. essentially <module><module-titled-list><h3/><ul/></module-titled-list></module> or <module><module-list><ul/></module-list></module>.)

    The argument that modules should be mindless generic zombies is an interesting but artificial hypothetical. Whatever CMS or app is rendering this module can be augmented to specify type at the time of content entry. If you cannot modify the app or whatever is rendering the structure, then you have a much bigger problem confronting your design.

    Thanks for the interesting post.

  24. Sam
    Permalink to comment#

    Gotta admit I usually go the losing battle route. However, I’ve decided after reading this that my css needs to be a bit more generic. Thanks for the interesting read.

  25. Permalink to comment#

    I suggest just using top margin for general spacing, as when planning a specific page you most of the time know what content you’ll have at the beginning of the page. You usually don’t know how a page will end – will there be a paging component, action buttons, author information or whatever (depending on the dynamics of specific template). This way you can arrange the whole page as a train set – locomotive is the first one with no damper and each following coach has its own spacer designed just for this specific item. First item of the page is 99% time the same, a heading or breadcrumb so you basically just have to deal with this. If you have to focus just on one side spacer then it’s more easier to follow general design grid/spacing.

    I’ve been using just top margin for spacing for years and have never had any problems with this approach. So just to throw a special in – IE7 supports first-child: (so you can use it) and doesn’t support last-child:

  26. jeff
    Permalink to comment#

    I set all my top margins to 0 and only use bottom margin ala vertical rythum. I would set the bottom padding of the module to 0 as any contained element will have bottom margin.

    Used this technique for years and never had a problem with heavy padding.

  27. I have always just left the bottom padding off the module.

    Most items of content need bottom padding/margin and if you use layout divs with this in mind you just give them some bottom love too. Also you are almost never going to end a module with an H3 etc..

  28. Permalink to comment#

    I deal with this using sass by keeping it abstract so I can reuse it for every module that needs it:

    %last-child { & > :last-child { margin-bottom: 0; } }

    So when a module needs to remove the margin-bottom of his last child element:

    .module { @extend %last-child; }

  29. If the .module will always contain an element that has a margin-bottom, and its margin is equal to or greater than the internal padding, then instead of resetting the margin-bottom on all of the “last” elements, set the .module‘s padding-bottom to 1px.

  30. Permalink to comment#

    Throwing semantics out of the window, I’ll often use some helpers:

    .remove-margin-bottom {
    margin-bottom: 0 !important;
    }

    And in other cases:

    .give-margin-bottom {
    margin-bottom: $spacing-variable !important;
    }

    Might sound horrid, but there are too many times when I find myself giving an element a class just to take or give a bottom margin away. Feel free to hate. ;)

  31. Scotty
    Permalink to comment#

    What happens when a designer comes along and wants a header with a background color or image to start on the edge of the module? Now you have to use a negative offset, which I suppose is OK for just one element…

  32. Leaving this here:

    Clever in that it uses margin collapsing to fix the issue. Would work great if the margin/padding was equal in most cases and the children weren’t floated/inline-block.

  33. Samir
    Permalink to comment#

    I would go for an extra class since it’s clearly states my intention of removing the padding for this element. Using something like _spaces.sass and removing all marigns and paddings from any core modules styles. Then you would have a spacing component with reusable margin/padding classes like .l-pbn (padding bottom none) or .l-mls (margin left small), etc..

  34. { margin-top: 10px; }

    Would give every child element a margin on the top except the first element, which also solves the issue. Another solution could be to use an element with a margin in stead of a padding and to utilise collapsing margins.

  35. Karl Merkli
    Permalink to comment#

    Chris dont use

    .module > *:last-child{
    //
    }

    use

    .module > :last-child{
    //
    }

    its faster and cleaner

  36. Permalink to comment#

    Such a dumb little issue that I find myself thinking about a lot. Thanks for the round-up Chris! I’m usually building for IE8 support so I tend to use margin-top on most things and first-child to zero it out when I need.

  37. Permalink to comment#

    I’ve definitely run in to this. Great solutions!

  38. Jason
    Permalink to comment#

    .module *:nth-last-child(1) { margin-bottom: 0; }

  39. It would be better to use !important rule.
    See http://jsfiddle.net/guimihanui/9hCdC/

  40. Mike S.
    Permalink to comment#

    This type of code may look very basic but it’s a great code to use and has lots of applications. Thanks for posting this.

  41. Permalink to comment#

    Why not leave it at .module :last-child ?

    specified rules will most likely override this one.. example: .module ul li { padding: 10px}

    also I tend to use the method Sugareina mentioned, figuring that the adjacent sibling selector is far more backward compatible than :last-child.

    MHO

  42. Permalink to comment#

    Sometimes I use the :not selector like this :
    .my-spacing-element:not(:last-child) { margin-bottom: 15px; }

  43. Michael S.
    Permalink to comment#

    You could also do something like this:

    .container { padding: 5px 20px 20px; }

    .container > h1 { margin-top: 15px; }

    .container > p { margin-top: 10px; }

    .container > [any other element you might need] {margin-top: [something]px; }

    Assuming the container always starts with a h1 this gives us a nice spacing of 20px on every side inside the container while having a space of 10px before each p (15px before each h1 except of the first one which makes sense because of the consistent container spacing) and you don’t have to style the first or last element in the container any different from the others.

    It’s sufficient for most situations I find myself in. Then again, I don’t usually develop websites where a single container must be adaptable to every single effin type of element the client wants to shove into at the start of the container. :D

  44. .module > * {margin:1em;}

    Provides a nicely spaced grid for all children. Margin can be retracted from individual elements if needed. Been using this for the past 5 years or so, the only tricky part is when .module doesn’t have any borders and margins may start collapsing. A clearfix or overflow:hidden can fix that, but that’s still a bit hacky.

  45. Using some pseudo elements, you could create a reliable solution without modifying any children elements. Take a look.

    module { 
      padding: 0 20px; 
      &::before, &::after { 
        content: ""; 
        display: block; 
        height: 1px; 
      } 
      &::before {
        margin-bottom: 19px; 
      } 
      &:after { 
        margin-top: 19px; 
      }
    } 
    

    Above: Self-contained sexiness

    Note: If you have a border around the module then the pseudo elements’ height and slightly off margin’s are not necessary (eg. here we would leave them at padding: 20px, and there wouldn’t need height: 1px) This approach has excellent browser support and doesn’t enforce having margin-bottom only elements. For instances where the margin of an element is larger than the modules padding I’m going to go ahead and guess that it’s probably better to leave the elements margin on since it’s probably designed that way. I try and structure my page to allow for naturally occurring margin collapsing as much as possible. I find that not fighting the flow turns out much better than otherwise.

    Here’s that codepen again: http://codepen.io/thejameskyle/pen/pthGd (be sure to checkout the mixin I created in there too!)

  46. I’d just omit the bottom spacing of the container. That’s the most straight-forward way for me, since I would be very uncomfortable to use something containing the * selector. Moreover :last-child isn’t supported in IE8 – which becomes more and more unimportant these days, but it’s a disadvantage you shouldn’t forget about.

  47. Permalink to comment#

    Hi Chris, nice technique !

    I have modified it a bit to include first-child, like this :

    .module > *:first-child,
    .module > *:first-child > *:first-child,
    .module > *:first-child > *:first-child > *:first-child {
    margin-top: 0;
    }

    .module > *:last-child,
    .module > *:last-child > *:last-child,
    .module > *:last-child > *:last-child > *:last-child {
    margin-bottom: 0;
    }

    The only issue I see with this is the fact that it’s not fully IE8 compatible.

  48. Permalink to comment#

    I have a different solution that does not require the removal of excess glue.

    Excess glue??

    Let me explain: When you use bottom margins, the last element is always left with a margin that does not separate it from anything. In the solution offered this margin always gets put on and then has to be taken away again, like excess glue.

    The code I use for “modules” uses top margin instead, but only on elements that follow other elements.

    .module * + * { margin-top: 1.5em }

    This reads as “any element preceded by any element should be separated with a top margin”. Glue is only applied when it’s needed.

    This works at an infinite number of nesting levels because, although each nested element receives the top margin, no first child does: No doubled up margins to worry about or have to anticipate.

  49. Andrew Tibbetts
    Permalink to comment#

    Since :first-child has deeper legacy IE support, I got in the habit a long time ago to only apply margins and padding to the tops of elements and remove them from first children. http://stackoverflow.com/questions/7938521/browser-support-for-css-first-child-and-last-child

  50. Paul d'Aoust
    Permalink to comment#

    Here’s another semi- to sub-optimal solution to add to the mix. It adds no new elements:

    .module::before, .module::after {
        content: " ";
        line-height: 0;
        display: block;
    }
    
    .module::before {
        margin-bottom: -1em;
    }
    
    .module::after {
        margin-top: -1em;
    }
    

    The upside is that this tackles any level of nesting, even if descendants prevent their own descendants’ margin collapsing (floated children, children with overflow: hidden, children with padding or border, etc).

    The downside is that it expects all your children to have the same size of margins (in this case, 1em).

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".