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:
All good? Let’s get started then.
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);
}
When compiled, it should output this:
§.parent {
position: relative;
}
.child-with-unknown-dimensions {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.child-with-known-width {
position: absolute;
top: 50%;
left: 50%;
margin-left: -200px;
width: 400px;
transform: translateY(-50%);
}
.child-with-known-height {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%);
margin-top: -200px;
height: 400px;
}
.child-with-known-dimensions {
position: absolute;
top: 50%;
left: 50%;
margin-left: -200px;
width: 400px;
margin-top: -200px;
height: 400px;
}
Okay, that looks a bit verbose but keep in mind that this output is for demonstration purpose. It is rather unlikely you find yourself using them all in a given situation.
Building the mixin
Okay, let’s dig. From the previous code snippets, we already know our mixin’s signature: it has two optional parameters, $width
and $height
.
/// Horizontal, vertical or absolute centering of element within its parent
/// If specified, this mixin will use negative margins based on element's
/// dimensions. Else, it will rely on CSS transforms which have a lesser
/// browser support but are more flexible as they are dimension-agnostic.
///
/// @author Kitty Giraudel
///
/// @param {Length | null} $width [null] - Element width
/// @param {Length | null} $height [null] - Element height
///
@mixin center($width: null, $height: null) { .. }
Moving on. In all circumstances, the mixin needs to make the element absolutely positioned, so we can start with that.
@mixin center($width: null, $height: null) {
position: absolute;
top: 50%;
left: 50%;
// Moar magic here...
}
We will have to be clever with our code. Let’s pause here for a second and analyse the different options we have:
Width | Height | Solution |
---|---|---|
Undefined | Undefined | translate |
Defined | Defined | margin |
Defined | Undefined | margin-left + translateY |
Undefined | Defined | translateX + margin-top |
Let’s go with this.
@mixin center($width: null, $height: null) {
position: absolute;
top: 50%;
left: 50%;
@if not $width and not $height {
// Go with `translate`
} @else if $width and $height {
// Go width `margin`
} @else if not $height {
// Go with `margin-left` and `translateY`
} @else {
// Go with `margin-top` and `translateX`
}
}
Now that we have set up the skeleton for our mixin, we only have to fill the gaps with actual CSS declarations.
@mixin center($width: null, $height: null) {
position: absolute;
top: 50%;
left: 50%;
@if not $width and not $height {
transform: translate(-50%, -50%);
} @else if $width and $height {
width: $width;
height: $height;
margin: -($width / 2) #{0 0} -($height / 2);
} @else if not $height {
width: $width;
margin-left: -($width / 2);
transform: translateY(-50%);
} @else {
height: $height;
margin-top: -($height / 2);
transform: translateX(-50%);
}
}
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.
@mixin center-children {
display: flex;
justify-content: center;
align-items: center;
}
Provided you add relevant vendor prefixes (through the mixin or Autoprefixer), this solution should Just Work in many browsers.
.parent {
@include center-children;
}
Yielding, as you can surely guess:
.parent {
display: flex;
justify-content: center;
align-items: center;
}
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! ;)
You can play with the code on SassMeister: http://sassmeister.com/gist/550809f5aa00b73d932c.
Frequently Asked Questions (FAQs) about Centering with SASS
How can I center an element vertically and horizontally using SASS?
To center an element both vertically and horizontally using SASS, you can use a combination of CSS properties and SASS mixins. First, you need to set the position of the parent element to relative. Then, for the child element, you can create a mixin that includes the properties top: 50%, left: 50%, and transform: translate(-50%, -50%). This will center the child element within the parent.
What is the purpose of the ‘include’ directive in SASS?
The ‘include’ directive in SASS is used to include mixins. Mixins are a way of reusing a block of CSS declarations. By using the ‘include’ directive, you can include these declarations in multiple places in your stylesheet, which can help to keep your code DRY (Don’t Repeat Yourself).
Can I use SASS to center text within an element?
Yes, you can use SASS to center text within an element. You can do this by creating a mixin that includes the properties text-align: center; and line-height: normal; Then, you can include this mixin wherever you want to center text.
How can I use SASS to center an element with a fixed width and height?
To center an element with a fixed width and height using SASS, you can use a combination of CSS properties and SASS mixins. First, you need to set the position of the parent element to relative. Then, for the child element, you can create a mixin that includes the properties top: 50%, left: 50%, width: [your desired width], height: [your desired height], and transform: translate(-50%, -50%). This will center the child element within the parent.
What is the difference between ‘extend’ and ‘include’ in SASS?
The ‘extend’ and ‘include’ directives in SASS serve different purposes. ‘Extend’ is used to share a set of CSS properties from one selector to another, while ‘include’ is used to include mixins. The main difference is that ‘extend’ creates a relationship between the selector that is being extended and the selector that is doing the extending, while ‘include’ simply copies the CSS properties from the mixin to the place where it is included.
How can I center an image using SASS?
To center an image using SASS, you can use a combination of CSS properties and SASS mixins. First, you need to set the position of the parent element to relative. Then, for the image, you can create a mixin that includes the properties top: 50%, left: 50%, and transform: translate(-50%, -50%). This will center the image within the parent.
Can I use SASS to center a block-level element?
Yes, you can use SASS to center a block-level element. You can do this by creating a mixin that includes the properties margin-left: auto; and margin-right: auto; Then, you can include this mixin wherever you want to center a block-level element.
How can I use SASS to center an element vertically?
To center an element vertically using SASS, you can use a combination of CSS properties and SASS mixins. First, you need to set the position of the parent element to relative. Then, for the child element, you can create a mixin that includes the properties top: 50% and transform: translateY(-50%). This will center the child element vertically within the parent.
Can I use SASS to center an element horizontally?
Yes, you can use SASS to center an element horizontally. You can do this by creating a mixin that includes the properties left: 50% and transform: translateX(-50%). Then, you can include this mixin wherever you want to center an element horizontally.
How can I use SASS to center an element with a dynamic width and height?
To center an element with a dynamic width and height using SASS, you can use a combination of CSS properties and SASS mixins. First, you need to set the position of the parent element to relative. Then, for the child element, you can create a mixin that includes the properties top: 50%, left: 50%, and transform: translate(-50%, -50%). This will center the child element within the parent, regardless of its width and height.
Non-binary trans accessibility & diversity advocate, frontend developer, author. Real life cat. She/her.