It is the received wisdom among software developers, as anyone who has so much as had a conversation with one can testify, that the best way to make progress in learning to programme is to “learn by doing”. I am not here to contest that.
On the contrary, as a developer at the beginning of what is clearly going to be a long — possibly life-long — journey of study, I have already experienced first-hand the noticeable acceleration of learning that the project-based education on my Web Development course at Microverse allows.
But I have also witnessed how novice web developers, focused only on the end goal of a project, can pick up bad habits and awkward workarounds to problems, becoming over-reliant on familiar but unsuitable techniques while cleaner, stronger and more logical alternatives remain ignored and unused.
Simple though it may seem, positioning is one area in which learners of CSS are particularly likely to settle on just one or two preferred methods. As I did with Flexbox positioning, many students become too dependent on one way of placing and moving elements and fail to realise there are other techniques that could make their lives so much easier.
And though I will freely admit that it’s not the most glamorous aspect of CSS, I promise you this: if there’s one area of CSS where you want to feel supremely, unassailably confident, it’s positioning.
What the skeleton is to the body, CSS positioning properties are to a webpage. All the content, functionality and aesthetics you will ever want to see on your website all depends on that solid structure that your CSS positioning fundamentals provide.
So, if you’ve ever found yourself prodding sections of your webpage around pixel by pixel, or wondering if there’s an easier way to fill a responsive navigation bar with equally sized links, then you’re in the right place to fill in the gaps in your knowledge and graduate to true CSS positioning mastery. Let’s begin.
Question: How did your page look the last time you forgot to link your HTML and CSS files?
In addition to the disappearance of your tastefully selected fonts, backgrounds and borders, you might also have noticed that, in the absence of any CSS positioning properties, a lot of your elements suddenly wanted to queue up along the left-hand side of your screen. Why is that?
Well, what you were looking at is something called “normal flow”. It is to a webpage what gravity is to our lives on Earth: a weak force that can very easily be overridden, a force so universal that you would be forgiven for forgetting it’s even there, but in the absence of any interference, it is often the dominant factor in explaining why things move, or why they don’t.
An element is — very broadly speaking — only as big as it needs to be.
Contrary to my first impressions, elements don’t just take a random size. Generally, they behave like elastic. First, they wrap tight around their content. Then they conform to any box-model sizing properties they’ve been given, such as width/height and padding. Then they will stop and not ask for a pixel more.
Although this is a sound working principle, there are many common exceptions to this elasticity. Elements with defined widths and heights sacrifice their wrapping behaviour, and flexbox and grid items also come to mind as straying very far from this principle. But the first and most obvious exception to this principle of elasticity is block elements.
Block and Inline are two types of block formatting contexts, which is just some jargon that basically means that so-called “block” elements behave and arrange themselves on the page differently to so-called “inline” elements.
Simply put, block elements behave like paragraphs, while inline elements behave like words. In English, each word sits to the right of the previous, while each paragraph sits underneath the paragraph before.
That’s because words, like inline elements, can share a horizontal space, while paragraphs and block elements cannot.
That explains why, the first time you wrote <h1>Hello World!</h1> and wanted to jazz it up with an aquamarine background, that aquamarine background stretched all the way across the body of the webpage from left to right.
As well as demonstrating what a bad choice of colour aquamarine was, this also shows that block elements do not wrap around the width of their content but rather, stretch to the width of their parent container.
<h1>, like <p> and <div>, is a block element by default, so despite containing two words, it took the whole width of the container and any subsequent children will, therefore, be forced to sit beneath it. However, don’t forget that even greedy block elements wrap to the height of their contents unless instructed otherwise.
Other elements, such as <span> are inline elements by default, shrinking to both the height and width of their contents. In fact, inline elements are so “elastic” that their size cannot even be altered with explicit height and width properties!
And although they will grow to accommodate padding, inline elements also ignore top and bottom margins — more on this coming up.
Block elements can take width and height properties but without them, they will just wrap to the height of their content and stretch to the full width of their container.
However, while <span> is inline by default and <h1>, <p> and <div> are block by default, any element can have its display value defined as either to change its behaviour.
I’m going to say this once: Every element is a box.
Don’t be fooled by the smoke and mirrors of modern web design. If it’s not a box, it’s not an element. If it doesn’t look like a box, it is. This sentence is a box, this paragraph is a box, the images in this article are all boxes, and even the webpage itself is a box.
Every one of those boxes is composed of four parts, four sub-boxes, really, sitting one within the other like Russian dolls: the content-box, the padding-box, the border-box and the margin-box. While they are all important, it is margin and padding that we often use when positioning elements. But let’s start at the top. And by that, I mean the middle.
The content-box is the heart of the element. When we talk about elements “wrapping” around their content, this is the essential area of content we’re talking about. If it weren’t for padding, this content-area would (normally) be the whole area of the box. What’s padding, you say? Glad you asked.
Padding is very simple. It answers the question, “How close should this element wrap to its content?”. The element wants to take up as little space as it needs to, to wrap as closely to its content as it can. So when padding is 0, there is nothing to stop an element from wrapping right up to its content, and that’s exactly what it will do.
But increasing that padding means a distance must be maintained between the content and the element’s border. If an element is wrapped tight and then padding is applied, the total area of the element will be forced to increase, since the content is the same size but now“pushes” the border further from it. But what’s this border, you say?
Glad you asked. The border separates the “inside” of an element box from the “outside”. It lies on the limits of the element’s area. And, though it is often a very slim area, it’s important to ask: is the border itself inside or outside the box?
Glad you asked. The answer is, it can be both. By default, the border is considered outside the box, but the box-sizing property can be used to change that, and can make calculations of a box’s true size much easier depending on the situation.
Margin is the fourth piece of the box model puzzle, and it is possibly the most likely of the four to be used as a positioning tool. Why’s that?
In the normal flow context, margin does a similar job to padding, but instead of maintaining a distance between the content and the edges of the box, margin lies outside the box, keeping other boxes at a distance. In order to accommodate this distance that margin demands, elements on the page will shuffle left, right, or whatever direction they need to, to make sure those no-one’s-land distances are maintained.
Margins are not collapsable, which means that two margins cannot overlap. If one element’s margin demands that it stay 10px from any other element, while the next element’s margin demands that it stay 15px away from any other element, those two elements will refuse to sit any closer than 25px apart.
Now that we’re clear that elements are boxes and that they generally follow this force called “the normal flow”, let’s look at when they don’t.
The position property has five possible values, one of which we actually already know: static.
Static is the default value for the position property. It means “just follow the normal flow”. Statically positioned elements are the most obedient of them all.
The next value is relative. Relatively positioned elements are also very obedient, but they can be persuaded to break the rules using offset properties like top, right, bottom and left.
Relatively positioned elements first sit where they should according to normal flow, but from there they will then translate in other directions according to any offset properties it has been given.
The first of the truly mischievous position values is absolute.
These elements only obey offset properties and are outside the normal flow. Absolutely positioned elements can’t “see” normal-flow elements at all and their margins won’t push any other elements away (apart from their parent element).
But if relatively positioned elements take their “normal flow” position as the starting point for offset, what do absolutely positioned elements offset from?
Well, from their closest “non-static ancestor”. That could be their parent element, or it could just be the body. Whichever it is, they act as if they’re the first and only child element of that container and place themselves in its top-left corner and then offset from there as instructed.
The fourth position value is fixed. These elements, like their absolutely-positioned cousins, are also out of the normal flow. But while absolute elements position themselves relative to their immediate ancestor, fixed elements position themselves relative to the viewport. That’s right. Scrolling around a webpage means nothing to a fixed element. Tell it to sit on the left-hand side of the screen and it will always be there.
The final option for the position property is the youngster of the group. Only arriving on the scene with CSS3, sticky positioning combined the behaviour of relative and fixed positioning. It will remain in its relatively-positioned location, until the user’s scrolling would move it beyond its “permitted” offset (e.g. top: 0px), at which points it “sticks” to the viewport in order to obey that offset rule.
So far, our positioning options have been either “in-flow” with margins and padding or “out-of-flow” with non-static positioning and offsets.
And that basic information about the natural behaviour of elements is an absolute must-know. But websites built using only these somewhat awkward positioning methods tend to come out looking a bit…’90s. In case you've forgotten what that looks like, here's the US Geographical Survey's groundbreaking 1994 website:
Luckily for us, there are now a couple of other major, well-supported positioning techniques that are about as far from “normal flow” and ’90s websites as you can get.
Let’s start with grid.
Grids are pretty much the most powerful 2-dimensional positioning system available to a novice web developer. With a grid, you can divide a container into grid rows, grid columns and grid cells of whatever size you choose, and then just tell the child elements which areas to occupy!
The other strengths that grid layouts can boast of include their excellent capacity for responsiveness to screen sizes. They can use percentage distance units to define the breadth of rows and columns in terms of the width/height of the container. Or they can use the fr unit, unique to grids, which are “units of free space” and basically instruct the grid to divide untaken space proportionally between rows and columns that are in fr units.
The power of grids is in the macro-structure. Many smaller containers don’t need grid positioning when flexbox or normal-flow positioning are quite up to the job. But laying out an entire webpage with margins, offsets, floats, even flexbox, can become insanely complicated, like building a log cabin with matchsticks.
By allowing grids to control the large-scale and then fine-tuning positions with padding, margin, offsets, etc., you can play to the strengths of each method and keep your code clean and dependable.
While grid is a 2-dimensional layout system, flexbox positioning is best suited to one-dimensional contexts.
Flexbox containers have a main axis and a cross axis. In a horizontal navigation bar, for example, the main axis would be from left to right, while the cross axis would be from top to bottom.
Flex items automatically consume all of the space available to them in the cross axis. How the space on the main axis is distributed can be controlled quite precisely with properties like flex-basis and flex-grow, which instruct the flex item how much they should expand to fill empty space. Or the space can be left empty and distributed in gaps between the items, using the justify-content property. With flexbox, an item’s size is less important than its size relative to the space it’s in and to the other items it shares the space with.
Flexbox is brilliant. Combined with media queries, responsive units of measurement, and the flex-wrap property, flexbox layout can give navigation bars and other containers an ability to seamlessly adapt whenever they are stretched or shrunk. However, they really come into their own in one-dimensional layout contexts, between the large-scale structuring that grids excel at and the “finishing touches” shuffling that padding and margin provide.
I believe the contents of this article represent some of the most important concepts and tools when it comes to CSS positioning, even without going into floats, z-index, or the million little ways that different positioning rules interact — and I am currently discovering at least a handful of these every day.
But, even more important than understanding the concepts and tools at your disposal is the instinct for when different positioning methods are appropriate. There are no such things as “good” and “bad” positioning methods, just “good for this” and “bad for that”.
Newcomers to web development — and I speak here from first-hand experience — can so easily get carried away by the flashier aspects of CSS, its colours and backgrounds, its videos and images, its fonts and borders, that they neglect to really take the time to understand the positioning mechanics that are at work on every webpage they build. And I think they should.
Mastering positioning fundamentals means knowing what your tools are and when to use them. Some of the techniques covered in this article may be totally familiar to you, but I believe that avoiding over-reliance on your most confident areas means diversifying your toolkit, which can only improve the quality of the websites you create and the enjoyment you feel while creating them.
I started this piece by invoking that sacred phrase: “learn by doing” and I will conclude by once again saying that I have enormous faith in this idea. Get stuck in. Think of something that you wish existed and be the one to bring it to life. There really is no better way to learn.
But to that advice, I would add: do by learning. Instead of getting stuck in your ways, be curious about other ways. Instead of keeping to what’s comfortable, get comfortable in something else. Don’t negotiate with your weak areas, run right at them! Because in a field like development, there’s always a better way. So go find it.
This CSS-Tricks graphical explanation of flex-box rules deserves some kind of award for graphical explanations of flexbox rules. It’s so good.
Thought not “further reading” as such, Pesticide for Chrome has been an absolute game-changer for my positioning game. At the click of a button, this browser extension lets you see the outlines of every element on the page your viewing. Thank me later.
I can also strongly recommend sticking on some good music and playing around with different positioning methods concepts on Codepen.
And last but not least, some independent project-work on freeCodeCamp’s Responsive Web Design Certificate is a great way to put all that positioning to work.