Perfect CSS Positioning
Why Arranging Boxes is Harder Than it Seems
Styling applications with basic CSS can feel fairly simple and straight-forward at first. You can easily change colors, sizes, fonts, etc. The concept of our elements residing in boxes made up of margins, borders, and padding is easy to grasp… But what about defining where these boxes live on the page? This where things can get a little tricky.
We don’t have access to typical de-bugging tools that we do when using other languages such as console logging or error messages. So when our styling is not performing as expected, it’s hard to pinpoint the problem. However, getting familiar with some default behaviors and how to manipulate them can save us a ton of time and frustration.
The Position Property
The default position of all elements is static. Static elements follow the natural flow of an HTML document (one element comes after another, vertically). If we give an element’s position attribute any other value such as relative, absolute, fixed or sticky, it is considered a positioned element.
Only once we have changed this default value are we able to specify the distance from the top, bottom, left and right that we would like to move the element to. These directional instructions take on different meanings depending on the position attribute’s value. For instance, if an element has a position set to relative with a top value of 10px, it will move the element 10px from the top of it’s previous position. Relative positioning adjusts the placement relatively to where it was residing its previous static position.
Absolute and fixed position values take the element completely out of the default flow of the document. This could lead to some strange behavior like elements stacking on top of each other, because elements that live in the regular document flow are oblivious to absolute and fixed elements. The key difference in these two values are that an absolute element’s placement is relative to its closest parent and a fixed element’s placement is relative to its containing block. For fixed elements, there is an exception to this rule. If one of it’s ancestors has a transform, perspective or filter property, that ancestor acts as it’s containing block and the fixed element is placed relative to it instead.
Last but not least, let’s talk about sticky positioning. Be warned that is is not supported by all browsers, but it’s still pretty awesome when appropriately used. When we defined an element’s position as sticky, it stays within the normal HTML document flow. However, its position is set relative to the nearest ancestor that has a scrolling mechanism such as overflow:hidden, scroll, auto, or overlay. Also note, that it will still stay within its containing block. This is often the behavior seen in navigation bars, so it continues to stay in the viewport as we scroll down the page.
Z-Index and the Stacking Index
Just like with geo-location coordinates, the values of x and y indices determine the placement of our elements on a 2-deminsional plane. The z-index determines the 3rd deminsional placement of our element. This gives us the ability to place something above or below other points of reference. By default the z-index of all elements is set to 0. So, all the elements are on the same plane and are rendered one after another vertically. When we assign a different value to the z-index, whichever element has the highest z-index will appear on top.
Here’s the catch… we can’t just add a z-index to statically positioned elements. Well, you could but it would have no effect. So first, be sure to define the element’s position as relative, absolute, fixed or sticky. Changing the position attribute creates what is called a stacking context.
As described in MDN Web Docs, “The stacking context is a three-dimensional conceptualization of HTML elements along an imaginary z-axis relative to the user”.
One of the best anaologies for this is Photoshop layers and groups. If you’re familiar with Photoshop, you’ll understand that you can group together photos, drawings, logos, etc. and all of those things will be flattened out into one “layer”. You can then add other layers and define what you would like on top and bottom. It may help to think of each stacking context as a group of children elements encased by their parent element. They do not mix with other stacking contexts. The group can be placed on top of or underneath other layers, but it will always stay glued together.
The child of an element that contains a specified z-index will follow the location of its parent. It will not be compared to elements outside of its parent. The only way around this is to remove the z-index of the parent and thus removing it from the stacking context altogether. Don’t confuse the stacking context with the placement of elements in the DOM tree. It’s not the same, because stacking contexts are only concerned with non-static elements that have a z-index!
It’s also important to note that the stacking context can be affected by other properties such as opacity, transform, isolation and a few others. Be aware of this if you’re seeing some unexpected behavior.
The isolation property is especially helpful, because it actually allows us to break the rules a little bit and add the specific elements we want into a stacking context — including statically positioned elements!
Whew! That was a lot to take in. It’s understandable if these concepts take a while to grasp. Open up a recent project and play around with the position property. Try adding a z-index here and there. Do you see a change? If not, can you figure out why? Remember, the best way to really feel confident in your CSS positioning is to practice. Best of luck!
The Importance of Learning CSS
I know so many super-talented developers who share the same achilles heel: CSS. Instead of trying to "outrun" CSS, this…
The stacking context
The stacking context is a three-dimensional conceptualization of HTML elements along an imaginary z-axis relative to…