<div> (& Friends) Shouldn't Affect Layout by Default

3 minutes read · 2021-04-13

It’s hard to overstate how successful HTML and CSS really are. Having evolved dramatically since their humble origins in the 90s to allow for a wide range of interoperable, accessible and aesthetically rich documents and experiences, these two technologies are now the bread-and-butter that underpins the critical path of a double digit portion1 of the world’s economy.

With that said, it’s only natural that after nearly 30 years of standards development some non-optimal decisions have been made, particularly when it comes to default behaviors.

One example of such a decision is box sizing. Over time, web developers and designers have (somewhat reluctantly) come to agree that IE was right: Elements should be sized by their external bounds (including padding and border) and not by their internal content bounds. Nearly every modern CSS framework ships with something like:

* {
    box-sizing: border-box;

Another example is text layout. Mark Dalgleish has called it2 that the way line height works by default will likely be regarded in the same way once the leading-trim CSS property is implemented and widely supported. I think he’s likely going to be right.

After giving it some thought, I believe I found a third case in which the default behavior we get out of the box is unfortunate: <div> elements are displayed as blocks by default. With the long-standing Chromium accessibility bug around display: contents 3 finally fixed, I think it’s now time to consider adding the following rule to your next CSS file:

div {
    display: contents;

Or, if you’re feeling particularly adventurous:

div, main, aside, header, footer, nav {
    display: contents;

Why should you do this?

Here’s why I think this is a good idea:

  1. Having wildcard elements that you can use as a parent anywhere without affecting layout is extremely useful, particularly when developing reusable styles and behaviors. You can now think of <div> elements as Document Fragments that are persistent and also support styles and event handlers;
  2. From a semantic standpoint, display: contents is a much better fit for <div> than display: block. The HTML spec states: β€œThe div element has no special meaning at all. It represents its children.” 4 If that is the case, why should it affect layout at all?
  3. Elements that actually represent blocks of text (e.g. <article>, <p>, <h1>, <section>, <ul>, <ol>) gain renewed prominence. If you have to add a class to your <div> to get it to display as a block anyway, you might as well reconsider and use something more fitting;
  4. The resulting layout hierarchy is much flatter, making the order property much more useful, and responsive layouts with CSS grids much more flexible. Want to move a specific item from your header to after the main page content depending on the screen size? Yes, you can do that;
  5. If you’re concerned about compatibility with existing libraries or components, you can still opt in to the old behavior for entire document subtrees with a descendant selector (e.g. .legacy-ui div { display: block; }), with minimal specificity implication;
  6. A significant portion of your <div>s will end up being styled with display: flex anyway. πŸ€·β€β™‚οΈ