CSS Layout: Get around frameworks wrapping your components
Tags: css
Published: 2019-06-26 08:40:55

CSS is hard, so they say. But have you tried CSS while using a framework that messes up your DOM? Because that's where the real fun lies.

## The problem

So I am working on a simple mobile interface: header, content, footer. The header and the footer have a fixed size, and I want the content to be placed at the center of the remaining space.

<blockquote>"Ah, flexbox, my dear old friend, we meet again!"
<cite>― Myself (not really, it's just for the storytelling)</cite></blockquote>


So of course I come up with this simple implementation:

```html
<body>
  <header></header>
  <main></main>
  <footer></footer>
</body>
```

```css
body {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
```

![](https://thepracticaldev.s3.amazonaws.com/i/e29aq9rtgh3ddxv4qzvr.png)
<figcaption>Each direct descendent of the body has a blue border. So far so good.</figcaption>

So I'm happy, as any front-end developer is when things align nicely.

But as the application grows, I realize that I'm gonna need to wrap the `main` and the `footer` elements inside a single Angular component, for structural reasons inherent to the application.

That's when my beautiful page breaks, and you can see why by looking at the generated markup taking in account my new component:

```html
<body>
  <header></header>
  <my-component>
    <main></main>
    <footer></footer>
  </my-component>
</body>
```

Alas! Flexbox doesn't care for anyone but its own direct children, which are now `header` and `my-component`.

![](https://thepracticaldev.s3.amazonaws.com/i/5i24mnc7iot8fmlzgfx1.png)
<figcaption>Look at the blue borders: the body now has only two direct  children.</figcaption>

My first thought was to make my-component a flex container itself, but I couldn't get very far with this idea.

```css
my-component {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
```

![](https://thepracticaldev.s3.amazonaws.com/i/cfai6f431uj6hv2fmscj.png)
<figcaption>my-component is a flex container itself, with "justify-content: space-between". But of course that doesn't make the `main` bloc vertically centered.</figcaption>

Now, take a moment: how would you do it?

## A dirty way

I know you didn't take a moment and read this right away. It's okay.

One way I didn't think of before writing this article is to add a pseudo element to `my-container`:

```css
my-component::before {
  content: '';
}
```

`my-container` has now three children: the `before` pseudo element, the `main` bloc and the `footer` bloc.

You can see it in action [here](https://codepen.io/bcalou/pen/GaJyXZ)!

![](https://thepracticaldev.s3.amazonaws.com/i/ifkhjrc7p5kve913m15u.png)
<figcaption>Just set the pseudo element's content to an empty string instead, and voilà!</figcaption>

That's it! Sometimes a dirty fix like this one is so simple that it becomes quite attractive…

## A cleaner way

But adding a pseudo element just to trick the flexbox system does not feel very clean.

A few weeks ago I read this great article by Rachel Andrews: [Digging Into The Display Property: Box Generation](https://www.smashingmagazine.com/2019/05/display-box-generation/).

While reading about the `display: contents` property, I had two conflicting thoughts:
* "Well now, that's very interesting"
* "Yep, never gonna use that though"

But working on my layout problem, I thought about it again. From the article:

<blockquote>"The value display: contents will remove the box it is applied to from the box tree in the same way that display: none does, but leave the children in place."</blockquote>

In other words, it will result in a situation similar (in our case*) to what would happen if we commented out the element (but not its children):

```html
<body>
  <header></header>
  <!-- <my-component> -->
    <main></main>
    <footer></footer>
  <!-- </my-component> -->
</body>
```

And this is how you actually do it:

```
my-component {
  display: contents;
}
```

*In reality, CSS properties applied on `my-component` still impact its children (`font-size`, `color`...). So not exactly like commenting out the element, but you get the idea.

So… Does it mean that flexbox-wise, `main` and `footer` become direct children of body again? Yes! `my-component` doesn't have to be a flex container itself anymore. The `justify-content: space-between` applied to the `body` now works again.

This is the shortest solution presented in this article: one selector and one property. [Almost magical](https://codepen.io/bcalou/pen/xNJVWG).

![](https://thepracticaldev.s3.amazonaws.com/i/1ozaauzxfmq8zdxjioay.png)
<figcaption>The blue border wrapping my-component disappeared: indeed, the box is not rendered anymore, so box properties (margin, padding, border…) are not rendered either.</figcaption>

While I love the beautiful simplicity of this solution, `display: contents` has its drawbacks.

There can be **accessibility** problems with `display: contents`, because it hides the element from the accessibility tree. If you use it on a `ul` element in order to achieve some layout, the information that this is a list will be lost in many browsers.

This is not a problem in our case though, since `my-component` doesn't carry a specific semantic meaning. But one could be concerned about **browser support**, which is in that grey area that I would call the "meh" area.

## A cleaner cleaner way?

Enter the `margin: auto` technique.

It's only while writing this article that I remembered about another article, one of the greatest I read on this subject: [Flexbox's Best Kept Secret](https://hackernoon.com/flexbox-s-best-kept-secret-bd3d892826b6), by Sam Provenza. It's from 2015 and I used this "secret" countless times since then.

4 years later, I'm not sure this is a well-known fact. But you can use `margin-left: auto` , for exemple, in order to "push" a flexbox item to the right as much as possible.

{% codepen https://codepen.io/samserif/pen/GpwyOg %}
<figcaption>A pen that saved me many headaches. The price element has its margin-left set to auto.</figcaption>

That's super useful ! With this technique, you can push a flex item in any direction, horizontal or vertical.

But there was a part of the article that I had forgotten:
<blockquote>"If you don't specify a direction, simply applying margin: auto, a flex item would evenly distribute any extra space on either side of the itself equally"</blockquote>

So the "secret" also works with multiple `auto` margins.

And there it lies, a wonderful solution to our problem: make `my-component` a flex container again, and use `margin: auto 0` on the `main` element.

```css
my-component {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

main {
  margin: auto 0; 
}
```

![](https://thepracticaldev.s3.amazonaws.com/i/vy32ob6se2idyw64ewn4.png)
<figcaption>The empty space is evenly distributed between the margin-top and the margin-bottom of the main element.</figcaption>

See it [here](https://codepen.io/bcalou/pen/ZNjWRN)!

No magical pseudo element. Less accessibility and compatibility risks. Just sweet and glorious flexpower™.

The journey of vertical centering never stops.