Front-end education for the real world. Since 2018.





Another article about centering in CSS

Andy Bell

Topic: CSS

The tired old meme that centering in CSS is “impossible” has never been so irrelevant. In fact, I’d argue there’s almost too many options now.

Let’s have a look at some common contexts, what approaches we have available to us and what I would choose per context.

You want to center some textpermalink

The tried and tested old way will forever work.

Code language
css

p {
  text-align: center;
}

See the Pen Centering with text align by piccalilli (@piccalilli) on CodePen.

It works great, but what if you don’t want the actual text be be center-aligned and instead wanted the block to be aligned in the center, while maintaining its inherited text-align value?

Code language
css

.parent {
  display: flex;
  flex-direction: column;
  align-items: center;
}

p {
  max-width: 50ch;
}

See the Pen Centering text with layout parent by piccalilli (@piccalilli) on CodePen.

I’d opt for some flexbox here on a .parent element. Make sure you apply a max-width to the element — 50ch and a <p> in our case — to limit the text’s width. If not, it’ll blow out as wide as it possible can, losing that center-aligned look you’re going for.

You want to vertically and horizontally center an elementpermalink

This is where the deluge of options starts to show themselves. Let’s first focus on CSS layout options as I’d always recommend leaning into those, above anything else, rather than hacking positioning with position and transform. We’ll get into those two later in the article.

Grid and flexbox

With CSS grid, we can use place-items: center which will do exactly what it says on the tin.

Code language
css

.parent {
  display: grid;
  place-items: center;
}

See the Pen Centering with grid by piccalilli (@piccalilli) on CodePen.

This approach is really straightforward and puts all the power in the parent layout. It’s worth noting here that the boxes parent — the <body> — is filling the viewport height, providing the space to vertically align in the center.

I’m always a fan of this approach and advocate for it — along with a higher-level layout approach — with Every Layout and CUBE CSS’s layout compositions. It’s better to treat layout as a composable skeleton that doesn’t really care what child elements it has, as I see it.

Read more about that mentality in CUBE CSS.

You can also instruct your child element to center itself in a grid parent. Let’s use an example of a sidebar layout where you want a button to sit right in the center of that sidebar.

Code language
css

aside {
  display: grid;
}

.my-element {
  place-self: center;
}

Pretty handy. This would be a good approach if you had variable types of elements showing up in that sidebar and you want each type to have its own treatment.

For example, you might also have a sign up form that you want to fill the width of the sidebar and display at the top. That sign up form could itself, use position-self to do that.

If that context of variable items is not the case, and you want any child item to be centered, both vertically and horizontally, I, again, would advocate that you opt for the parent doing the work for you.

Code language
css

aside {
  display: grid;
  place-items: center;
}

See the Pen Centering in a sidebar with grid by piccalilli (@piccalilli) on CodePen.

My advice is to leverage grid whenever possible for either of these patterns. It’s mainly because we’re writing less code above anything else. I also hold the opinion that grid-based layout code is easier to understand than flexbox.

Speaking of flexbox, we can achieve both of the above approaches with that too.

Let’s start with the element, centering itself.

Code language
css

aside {
  display: flex;
  justify-content: center; /* We have to fragment here */
}

.my-element {
  align-self: center;
  /* justify-self: center; <- Won't work, grid only */
}

See the Pen Centering in a sidebar with flex by piccalilli (@piccalilli) on CodePen.

Well, partially centering itself. The justify-self: center rule is ignored with Flexbox, so we’re having to do the horizontal alignment on the child element.

I’d avoid this pattern completely. Sure, it’s doable, but a partial parent alignment and partial child alignment is a code smell, as I see it. That fragmentation is at such high risk of being missed by a colleague too, especially if documentation is lacking.

My advice is to go all-in on one approach — using the parent or using the child — and then make sure you document that principle for your team so your codebase remains consistent.

For example, flexbox can do all of what we need as the parent, just like grid can.

Code language
css

aside {
  display: flex;
  justify-content: center;
  align-items: center;
}

My tip after all of the options we’ve looked at: use grid. Keep it simple.

Code language
css

aside {
  display: grid;
  place-items: center;
}

What about horizontal centering when not in a layout?permalink

If you’re not in a flexbox or grid layout, you can leverage margin for this.

Code language
css

.my-element {
  margin: 0 auto;
  max-width: 20rem;
}

See the Pen Centering element with margin by piccalilli (@piccalilli) on CodePen.

You’ll only really see this approach working if your element has a width constraint, just like we’ve added with width: 20rem. Oh, make sure your element is a block element here too. Elements that are inline or inline-block won’t be horizontally centered with this approach.

Another problem with this approach is by setting the top and bottom margin to 0 — via the margin shorthand — we’re losing the opportunity for a parent to control flow.

For example, my favourite 3 lines of CSS might not work on this element in that layout if the .flow utility comes before .my-element in the source CSS.

This is a common risk with framework bundling!

Code language
css

/* Being bundled earlier, due to framework build process */
.flow > * + * {
  margin-block-start: var(--flow-space, 1em);
}

/* This margin top/bottom value now wins */
.my-element {
  margin: 0 auto;
}

You might be thinking “well, I’ll make sure I get the bundling order right”, which is a very optimistic viewpoint, especially in a framework environment. It’s also a completely avoidable point of long-term fragility.

A better option is to leverage well supported, modern CSS. Logical properties allow us to apply the auto margin, only on the horizontal axis.

Code language
css

.my-element {
  margin-inline: auto;
}

Handy stuff. That issue with the .flow utility is non-existent now, regardless of source order.

Using margin-inline: auto is definitely my chosen approach here.

What about positioning?permalink

Sure thing, positioning is very useful, but you are bedevilled with controls that need to be in place for it not to become a problem. My advice is to only use positioning for centering if you know for absolute sure that you have complete control of the child’s parent at all times.

For example, a play button on a video thumbnail link:

See the Pen Video thumbnail link by piccalilli (@piccalilli) on CodePen.

Code language
css

/* So the SVG has a positioning reference point */
.video-thumbnail {
  position: relative;
}

.video-thumbnail svg {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  transform: translateX(-50%) translateY(-50%);
}

I know it sounds unrealistic, but what happens if the video thumbnail is shallower than the play button? We’re at risk of blowout.

To prevent that outcome, we can do this exact pattern with CSS grid too.

Code language
css

.video-thumbnail {
  display: grid;
  aspect-ratio: 16/9;
  place-items: center;
  grid-template-areas: stacked; /* "stacked" is our named area */

  /* Just in case we get image leakage because we no longer define aspect on it */
  overflow: hidden;
}

.video-thumbnail > * {
  grid-area: stacked;
}

See the Pen Video thumbnail link - grid option by piccalilli (@piccalilli) on CodePen.

What I like here is the layout is sorted by grid at the highest level and one wildcard selector places everything in the “stack”. Because we’re also leveraging place-items: center, our positioning is solved too.

With that context aside, let’s say you have a notice that must be read. You could use fixed positioning for that.

Code language
css

.my-element {
  position: fixed;
  inset-inline-start: 50%;
  inset-block-start: 50%;
  transform: translateX(-50%) translateY(-50%);
}

What we’re doing here is first, setting the inline-start (AKA left) and block-start (AKA top) edges to 50% of the container’s width and height. Then using transform, we’re shifting .my-element, negative 50% of itself, which plonks it bang in the middle of our container.

I don’t really like doing this sort of thing though. For example, what happens if the content ends up being super long? I guess we can leverage overflow for that.

Code language
css

.my-element {
  position: fixed;
  inset-inline-start: 50%;
  inset-block-start: 50%;
  transform: translateX(-50%) translateY(-50%);

  /* The combination of overflow and max-height so the content doesn’t clip */
  overflow-y: auto;
  max-height: 80svh;
}

Sure, that works but because this is fixed, it’s going to dominate the viewport and cause us all sorts of problems. I’d save fixed positioning for non-intrusive elements that are always docked to an edge of the viewport, such as a chat helper.

Code language
css

.chat {
  position: fixed;
  inset-block-end: 1em;
  inset-inline-end: 1em;
  z-index: 10;
}

See the Pen Chat window trigger by piccalilli (@piccalilli) on CodePen.

With all that, I’m upgrading my advice to only use positioning for centering if you know for absolute sure that you have complete control of the child’s parent at all times and you’re not using position: fixed.

Wrapping uppermalink

Whew, that was quite long, sorry. The point I’m trying to make with this article is that there isn’t really a silver bullet when it comes to centering things. What’s more important is generating principles that work really well for you and your team.

With layout principles that are well established in your team, you’ll find you have to think about layout less and less. Using the layout options we have available to us in CSS effectively is how you get those really solid feeling websites.

It’s certainly how we do it.

Enjoyed this article? You can support us by leaving a tip via Open Collective


Newsletter