DEV Community

loading...
Cover image for Horizontal centering in CSS

Horizontal centering in CSS

Kevin Pennekamp
Originally published at crinkles.io ・4 min read

In a previous article I wrote about modern CSS layout solutions. As horizontal centering is a common layout pattern, the grid-based solution was a prime candidate to convert into a generic class when creating Feo CSS. But I encountered an issue. When combining this solution with other CSS layout patterns (e.g. the flow pattern) my layout would break. Both patterns are targeting the display property, but with different values. To allow both patterns to work together, I had to find a different solution.

The center layout pattern

The center layout pattern allows you to horizontally center elements on the screen. But more importantly, it allows child elements to have a different width as the parent. These can be images that span the entire width of the screen in articles, regardless of the screen size. Or when we want selected elements to break away from the paddings on the side. On small screens, we want small padding around the text, and images span the entire width.

The required layout

CSS grid-based solution

This solution creates a three-column grid. The outer columns act as the padding and overflow of the layout. By giving them a minimum width via minmax(1rem, 1fr), you ensure small padding exists on small screens. The center column takes the space required for the content but is capped at the maximum width. You can achieve this with minmax(auto, 60ch). This gives the combined implementation as displayed below.

:::
The used examples make use of CSS custom properties. This allows for adjustable layouts based on utility classes. Examples can be found in Feo CSS.
:::

.center {
  --gap: 1rem;
  --mw: 50rem;

    display: grid;
    grid-template-columns:
        minmax(var(--gap), 1fr)
        minmax(auto, var(--mw))
        minmax(var(--gap), 1fr);
}

/* Center all children */
.center > * {
    grid-column: 2;
}
Enter fullscreen mode Exit fullscreen mode

By overwriting the default grid-column: 2 to grid-column: 1 / -1, you can achieve the effect described in the previous section. This effectively makes the element the same width as the entire grid, not only the center column.

.center > .exception {
    grid-column: 1 / -1;
    width: 100%;
}
Enter fullscreen mode Exit fullscreen mode

But this has a downside: does not allow to set a different display value on the parent element. This makes .center less useful for non-article-based elements.

Back to the old school solution

In finding a solution for my problem, I looked at the solution that was used years before CSS grid became a thing. On Stack Overflow, you will still find enough answers pointing to this solution. Of course, I am talking about the margin: 0 auto solution. Let's modernize it a bit though.

.center {
  --gap: 1rem;
  --mw: 50rem;

  width: 100%;
  max-width: var(--mw);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--gap);
  padding-right: var(--gap);
}
Enter fullscreen mode Exit fullscreen mode

This is always a solid solution for horizontally centering solutions. But it has one issue though, and the reason why I did not use it in the first place. It does not easily allow child elements to 'overflow' and has an increased width. This issue can be avoided by using the method below. However, I found that this gave me issues with the responsiveness of the exception elements. So my search for a better solution continued.

.exception {
    width: 100vw;
    position: relative;
    left: 50%;
    transform: translateX(-50%);
}
Enter fullscreen mode Exit fullscreen mode

Out with the old, in with new

By slightly adjusting the old school solution, we can create a class that allows for the same behavior as the shown grid-based solution. Instead of setting the properties on the parent, you should set the same properties on the children by using the child combinator (parent > child). Similar to the grid-based solution, we can create a .exception class. When children are given this class, they are allowed to have a different width. They can flow outside of the parent's boundaries.

.center > * { ... }

.center > .exception-1 {
  max-width: none;
}

/* a solution with a different max-width */
.center > .exception-2 {
  max-width: min(100%, 50rem);
  padding-left: 0;
  padding-right: 0;
}
Enter fullscreen mode Exit fullscreen mode

Setting the max-width to none allows a child element to have no restrictions on its width. It can become 100% while all the other elements are capped at a maximum of 50rem. Similar to the grid-based solution, you can now create more dynamic layouts. The use of CSS custom properties even allows you to define utility classes that gave you more control over the layout.

.center-g-sm > * {
  --gap: 0.5rem;
}
Enter fullscreen mode Exit fullscreen mode

Wrapping up

The grid-based solution works well for articles. But when converting the class to a generic layout pattern, I encountered some issues. The main issue was that the display property on the parent was taken. By altering the solution used many years before CSS grids became a thing, I was able to come with a new solution. This creates the same layout with the same responsiveness advantages but gives me more flexibility. I am sure I will encounter another case where my new solution just does not work well. But for now, I settled for it.

Discussion (4)

Collapse
alvaromontoro profile image
Alvaro Montoro

What do you mean by "It does not easily allow child elements to 'overflow' and has an increased width."?

Collapse
crinklesio profile image
Kevin Pennekamp Author

Assume you have a blog post, like here on dev.to where all the text is centered in an article. The article has given a max-width and the margin: 0 auto properties. We want to spice up the layout, by allow images to have a bigger width (e.g. 50px more on both sides). Because the parent is constrained to a maximum width, the children are too. To allow for the effect, you have to make the child elements forcefully ignore the parents width and still keep them centered in the layout (by default if the child ignores the parents width, it will overflow in the reading direction, so on the right side).

Hope this makes sense.

Collapse
alvaromontoro profile image
Alvaro Montoro

You can go around that issue by setting a relative position to 50%, then translating the content 50% (which in that case will be 50% of the size of the element and not the parent):

.exception {
  position: relative;
  left: 50%;
  transform: translate(-50%, 0);
}
Enter fullscreen mode Exit fullscreen mode

Here is an example: codepen.io/alvaromontoro/pen/mdWgKdx

Thread Thread
crinklesio profile image
Kevin Pennekamp Author • Edited

Aah ofcourse, you are right. That is another solution for the same problem. Thanks for it.

I did found one downside though. This method feels to me to make it more difficult to have it scale better for responsiveness in cases where the child elements are less than 100vw and more than the --gap. But it is a personal preference I think.