Centering With Sass

Centering in CSS is well known for being a tedious task. It is kind of the running gag from the language, leading to jokes such as “we managed to send men on the moon, but we can’t vertically align in CSS”.

While CSS is indeed a bit tricky when dealing with centering, especially vertical centering, I feel like those jokes are a bit unfair. Actually, there are plenty ways of centering content in CSS, you just have to know how to do it.

This article is not intended to explain how these methods work, but how we can wrap them in a Sass mixin for friendly and easy usage. So if you feel a bit uncomfortable with CSS centering, may I recommend a couple of resources to read beforehand:

What is it all about?

First, we will focus on centering an element within its parent as it is the most common use case for absolute centering (modals, content in section, etc.). When you ask someone about CSS centering, the usual question you get as a reply is: do you know the element’s dimensions? The reason behind this question is that if you don’t know them, the best solution is to rely on CSS transforms. It lowers the browser support a bit, but it is highly flexible. If you can’t use CSS transforms or do know the element’s width and height, then it is easy to rely on negative margins.

So our mixin is going to basically do this: position the top left corner of the element absolutely in the middle of the container, then shift if back of half its width and half its height with either CSS transforms or negative margins depending on whether or not dimensions are passed to the mixin. No dimensions: go for transforms; dimensions: use margins.

You would then use it like this:

/**
* Enable position context for the child
*/
.parent {
position: relative;
}
/**
* Absolutely center the element in its parent
* No dimensions are passed to the mixin, so it relies on CSS transforms
*/
.child-with-unknown-dimensions {
@include center;
}
/**
* Absolutely center the element in its parent
* Width is passed to the mixin, so we rely on a negative margin for the
* horizontal axis and CSS transforms for the vertical axis
*/
.child-with-known-width {
@include center(400px);
}
/**
* Absolutely center the element in its parent
* Height is passed to the mixin, so we rely on a negative margin for the
* vertical axis and CSS transforms for the horizontal axis
*/
.child-with-known-height {
@include center($height: 400px);
}
/**
* Absolutely center the element in its parent
* Width is passed to the mixin, so we rely on a negative margins for both
* horizontal axis and vertical axis
*/
.child-with-known-dimensions {
@include center(400px, 400px);
}

Note: the #{0 0} trick is a dirty hack to prevent a slightly to agressive minification from Sass that would lead to margin: mt 0 ml instead of margin: mt 0 0 ml.

So far, so good.

Going further

There are several things we could do to push our mixin further, such as including a @supports rule inside the mixin to check for CSS transforms support or assume there is (or allow) Modernizr and output condition styles depending on whether or not CSS transforms are supported. We could also do some more agressive checking on the arguments to make sure they are valid values for width and height.

Although you have to ask yourself whether it’s a good thing to go that far. The mixin, as is, already has a cyclomatic complexity of 6, which is getting quite a lot for a Sass helper. It’s still okay, but adding more code to it likely means bumping the cyclomatic complexity further.

What about Flexbox?

I’m pretty sure some of you folks are jumping on your seat, thinking about how we can use Flexbox to center an element within its parent. Indeed, it’s possible and it turns out to be the easiest solution of all if you can afford it.

The main difference between the solution we have just set up and the Flexbox one is that the latter is built on top of the parent while the former is mainly focusing on the child (provided any of its ancestors have a position different from static).

To make an element have its child(ren) centered, you only have to print a triplet of properties. You can make a mixin, placeholder, class or whatever you fancy for this.

Final thoughts

We wanted a short mixin to easily center an element within its parent; this one does the job, and it does it well. Not only is it clever enough to work no matter whether or not the element has specific dimensions, but it also provides a friendly and obvious API which is extremely important.

By looking at the code, anybody understands right away that the @include center line is the inclusion of a helper that does some logic in order to make the element centered within its parent. However remember, the latter (or any parent in the DOM tree) has to have a position different than static for this to work! ;)