CSS percentage unit, the evil parts
Taming the wild side of CSS
CSS has dozens of length units, but the percentage unit is one of my favorites. However, it can be very evil and troublesome sometimes.
It should be one of the easiest CSS concepts to understand, the percentage CSS data type represents a percentage value, a percentage is a ratio expressed as a fraction of 100, so it has to do some calculations to get the final computed(absolute) value.
Until now it seems straightforward, the most tricky part is that every percentage value has to be relative to some absolute value of an element. It is often relative to an element’s parent node, but sometimes it will be relative to the element itself (as I will explain later). In addition, we have to know exactly which property value it will refer to (with, height or font-size).
TL;DR
When you use it with some CSS properties it’ll be relative to the parent element’s width. Some of these properties include: left, right, margin, padding, (even top and bottom margin/padding will be relative to the parent’s width).
And sometimes it’ll be relative to the parent element’s height. Like when it’s used for top or bottom properties, as expected.
When you use it with the transform property it’ll be relative to the element itself, either its width or its height.
We will go through several steps of creating this carousel(slider) to get familiar with the CSS percentage unit.
Note that we will not be going over a detailed explanation on how to exactly create this carousel in terms of styling, or how to make it interactive. We will only focus on how to work with percentages in align controllers and set slides’ width and height.
Creating the carousel in detail would require lots of maths and digging into JavaScript, which would require an article of its own; so for the sake of brevity, I will not be digging into either of these and will start from this basic-styled tutorial pen.
General guides
I use BEM convention in naming classes; so every element has the same specificity weight and can over-write its properties easily.
We are working with border-box box-model because it’s more intuitive, as
the visible width of an element’s box = width + padding + border, and
the visible height of an element’s box = height + padding + border.
I use class names with a bold italic font, like .slider, to point to the element that I’m currently working with.
You can see there are vertical and horizontal guidelines in the pen, perfectly center-aligned. To hide guides, simply remove .slider — guides class from .slider container.
At the bottom of the pen in the CSS section, you will find each tutorial step code commented. Un-comment it to see the results step by step as we go along with this tutorial.
Step 1: Set slides with and height
We gave a .slider__slide a width of 25%, this will make the element’s content area exactly equal to the quarter width of its parent (.slider__track), but only if its parent has an explicit width.
We gave a .slider__slide a height of 100%, this will make the element’s height exactly equal to the height of its parent (.slider__track).
You will notice that the slides get off the guides, I’m sure that front-end developers of any level have faced a similar issue to what I’ve just described. When you think working with CSS percentage is very simple (and it should be), you face this strange behavior that drives you crazy.
If you investigate this problem more, you will find that .slider__slide height is equal to its parent as we expected, but because it has margin top and bottom of 20px it gets off the grid (I put that margin on purpose 😈😈).
We have to be very careful when setting the element width/height to 100% while it has a margin because this will move it outside the parent.
note here: if you used the default browser box model (content-box), adding padding or border will move the element outside the parent too.
There are many ways to fix this, and I believe you’ve already thought of some, one of them is to remove that little evil margin, or we can simply subtract the top and bottom margins form the height.
🎉🎉 You have done it!
Step 2: Align slider indicators
We will start by aligning .slider__nav to the center-bottom of the slider. We will give it an absolute position, relative to .slider with left 50% of the slider width, and see the output.
As we can see, we’ve aligned the left of .slider__nav to the center of .slider, but we actually want to align the center of .slider__nav to the center of .slider.
Unfortunately, there’s no center property in CSS so, we have to make a trick to force .slider__nav to shift back to the center.
We need to tell .slider__nav to move to the right half of its width (50% from width), we can use a margin with a negative value for this.
If we added margin-left: -50% to .slider__nav itself, we would find it will go back to its starting place, because the percentage value will be relative to .slider width not .slider__nav width.
The trick here is to select the first .slider__nav child, add margin-left: -50% to it then the percentage will be relative to .slider__nav itself.
👌👌 And now it’s perfectly centered
Don’t rush it though, we still have two arrows to align…
Step 3: Align slider controller next button
We will align .slider__next arrow to the right middle of the .slider.
Let’s try the trick that we’ve just learned, but now we will use it to center align the element vertically.
Oops! It didn’t work this time, can you guess why?
It seems like this technique worked perfectly in horizontal alignment but not as much for vertical alignment. That’s why CSS can sometimes be so evil.
To know why, we will check every percentage value and determine which element and property it’s relative to.
top property is relative to .slider’s height which is exactly what we want, while margin-top is relative to .slider__next’s element and again it’s what we want, but it’s relative to its width, not its height.
This would work perfectly if the .slider__next height was same as the width, but how we can fix this?
I introduce to you the ultimate solution, say welcome to the superhero transform.
The transform property is very powerful as it can be relative to the width or height of the element itself, so there’s no need for the trick where we select the first-child element. It can work perfectly for both vertical and horizontal alignment, without mention, it’s better in performance too.
In fact, there are sometimes when there’s no child element in the parent container, so again, the first child trick won’t work at all.
This why I called it the ultimate solution. Because it will work in every situation.
Replacing the previous code with this snippet.
There’s still a little problem here. When hovering over the next arrow, it will be misplaced.
So we have to add translateX to .slider__next:hover state too.
Step 4: Align slider controller prev button
The final step is to align .slider__prev, it will be exactly as .slider__next except it will be on the left.
🎉🎉 Congratulations! You have become a true CSS spider.
Conclusion
I hope this example helps you get a better understanding of how CSS percentage unit works and when it will be relative to the element itself and which property it will be relative to.
I also hinted at very important topics like CSS Specificity, Box-modal, and BEM naming conventions.
The percentage unit is very powerful if you know exactly how to manipulate it in your favor.
Thank you for reading, Happy coding!