CSS Positioning


Why does your div end up in the wrong place? Why won't two elements sit side by side? CSS positioning is one of the most confusing topics for beginners — and it matters a lot for dataviz layout.

This lesson covers block, inline, flex, absolute, fixed, and more — everything you need to place elements exactly where you want them.

Members only
7 minutes read

How elements flow: block vs inline

block

The code below renders 3 divs. Each has some CSS style that controls its color, width, and height.

Before revealing the result, think about it: how will the divs position themselves? Can you picture what this webpage will look like?

<div style="background-color: blue; width: 70px; height: 70px;">Box 1</div>
<div style="background-color: red; width: 70px; height: 70px;">Box 2</div>
<div style="background-color: yellow; width: 70px; height: 70px;">Box 3</div>

As you can see, the boxes stack on top of each other.

This is because by default, a div has its display property set to block. A block element takes the full available width and stacks vertically.

The only trustworthy way to learn about a CSS property is to check the MDN documentation. Get used to it — anything else will end up being a waste of time in the long run.

inline

Now, let's run the same experiment with 3 different elements: a code, an a, and a span. Once again, try to picture what the page will look like before clicking reveal!

<code>code element</code>
<a href="#">link element</a>
<span>span element</span>

This time, the elements sit beside each other! 😳

This is because those elements have, by default, their display property set to inline. Inline elements adjust their width to their content and flow horizontally, just like words in a sentence.

So going back to our 3 boxes example, we can simply add display: inline to make them sit beside each other!

<div style="display: inline; background-color: blue; width: 70px; height: 70px;">Box 1</div>
<div style="display: inline; background-color: red; width: 70px; height: 70px;">Box 2</div>
<div style="display: inline; background-color: yellow; width: 70px; height: 70px;">Box 3</div>

It worked — the boxes are beside each other!

But what happened to our width and height? They are completely ignored!

This is a very common CSS caveat: when an element has its display property set to inline, it cannot have a width or height — those properties are simply ignored.

There is a solution, though:

inline-block

inline-block is another value for the display property. It's a mix between block and inline: elements can have dimensions, but flow beside each other like inline elements.

<div style="display: inline-block; background-color: blue; width: 70px; height: 70px;">Box 1</div>
<div style="display: inline-block; background-color: red; width: 70px; height: 70px;">Box 2</div>
<div style="display: inline-block; background-color: yellow; width: 70px; height: 70px;">Box 3</div>

The handy flex option

Controlling element positions with block and inline works, but it's limited. As soon as you need proper alignment or spacing, it quickly becomes a nightmare.

Fortunately, there is another option called flex that turns out to be extremely handy. You'll use it all the time.

There is a small mental shift to make: setting display: flex on a parent element controls the position of its children. In the sandbox below, the parent div has display: flex, and the 3 children automatically line up in a row:

<div style="display: flex;">
  <div style="background-color: blue; width: 70px; height: 70px;">Box 1</div>
  <div style="background-color: red; width: 70px; height: 70px;">Box 2</div>
  <div style="background-color: yellow; width: 70px; height: 70px;">Box 3</div>
</div>

Once display: flex is set on the parent, you can customize the direction in which children are laid out. By default the direction is row (horizontal), but you can switch it to column (vertical) using the flex-direction property:

<div style="display: flex; flex-direction: column;">
  <div style="background-color: blue; width: 70px; height: 70px;">Box 1</div>
  <div style="background-color: red; width: 70px; height: 70px;">Box 2</div>
  <div style="background-color: yellow; width: 70px; height: 70px;">Box 3</div>
</div>

justify-content

Now that our boxes are in a flex container, we can control how they are spread along the parent using the justify-content property.

With flex-start, all elements are packed toward the start. With flex-end, they are packed toward the end. Options like space-between and space-evenly distribute the remaining space between or around the elements. Click the buttons below to see each option in action:

<div style="display: flex; justify-content: flex-start;">
  <div style="background-color: blue; width: 70px; height: 70px;">Box 1</div>
  <div style="background-color: red; width: 70px; height: 70px;">Box 2</div>
  <div style="background-color: yellow; width: 70px; height: 70px;">Box 3</div>
</div>

→ Main axis vs cross axis

You may have noticed that justify-content distributes items horizontally in our examples. That's because our flex direction is row (the default).

In flexbox, justify-content always works along the main axis — the direction in which items are laid out. When the direction is row, the main axis is horizontal. When it's column, the main axis is vertical.

Here is the exact same widget, but with flex-direction: column. Notice how justify-content now distributes items vertically:

<div style="display: flex; flex-direction: column; justify-content: flex-start; height: 450px;">
  <div style="background-color: blue; width: 70px; height: 70px;">Box 1</div>
  <div style="background-color: red; width: 70px; height: 70px;">Box 2</div>
  <div style="background-color: yellow; width: 70px; height: 70px;">Box 3</div>
</div>

align-items

We just saw that justify-content controls how items are distributed along the main axis. But what about the other direction?

That's what align-items is for. It controls how items are positioned along the cross axis — the axis perpendicular to the main one.

In a row layout, the cross axis is vertical. So align-items controls the vertical alignment of items. In the widget below, the container has a fixed height so you can see the effect clearly:

<div style="display: flex; align-items: flex-start; height: 200px; border: 2px dashed #d1d5db;">
  <div style="background-color: blue; width: 70px; height: 40px;">Box 1</div>
  <div style="background-color: red; width: 70px; height: 70px;">Box 2</div>
  <div style="background-color: yellow; width: 70px; height: 55px;">Box 3</div>
</div>

→ All together

With just flex-direction, justify-content, align-items, and gap, you can handle the vast majority of layout needs. In practice, flexbox is what you'll reach for almost every time you need to position elements.

Play with all the options at once below. Notice how gap adds consistent spacing between items — no more fiddling with margins!

flex-direction:
justify-content:
align-items:
gap: 0px

📌 The position property

So far, every element we've placed has followed the normal document flow — each one appears after the previous one, top to bottom, left to right. The CSS position property lets you break out of that flow and place elements exactly where you want.

static

position: static is the default value. It simply means "follow the normal flow" — which is exactly what every element in the examples above has been doing. You almost never need to write it explicitly, but it's good to know the name.

relative

position: relative keeps the element in the normal flow, but lets you nudge it from its original position using top, left, right, or bottom.

The key insight: the element still occupies its original space in the layout. Other elements don't move — only the visual rendering shifts. In the sandbox below, the dashed border shows where the box would normally sit:

<p>Some text above.</p>

<!-- Dashed border = where the box would normally be -->
<div style="border: 2px dashed #ccc; width: 70px; height: 70px;">
  <div style="
    position: relative;
    top: 20px;
    left: 30px;
    background-color: blue;
    width: 70px;
    height: 70px;
  ">
  </div>
</div>

<p>Some text below — notice it hasn't moved.</p>

absolute

position: absolute removes the element from the document flow entirely. It is then positioned relative to its nearest ancestor that has position: relative (or any non-static position).

In the example below, the grey parent has position: relative, so the blue label positions itself relative to that parent. Try changing the top and right values to move it around:

<div style="
  position: relative;
  background-color: #f3f4f6;
  width: 300px;
  height: 200px;
  border: 2px solid #d1d5db;
  padding: 16px;
">
  <p style="margin: 0;">This is the parent container.</p>
  <p style="margin: 8px 0 0;">It has <code>position: relative</code>.</p>

  <div style="
    position: absolute;
    top: 8px;
    right: 8px;
    background-color: #3b82f6;
    color: white;
    padding: 4px 10px;
    border-radius: 4px;
    font-size: 14px;
  ">
    Label
  </div>

  <div style="
    position: absolute;
    bottom: 8px;
    left: 8px;
    background-color: #f59e0b;
    color: white;
    padding: 4px 10px;
    border-radius: 4px;
    font-size: 14px;
  ">
    Annotation
  </div>
</div>

position: absolute is extremely handy for dataviz. Whenever you need to place a tooltip near a data point, add an annotation on top of a chart, or overlay a legend — absolute positioning inside a relative container is the go-to pattern. You'll see it a lot in the upcoming D3 lessons.

💪 Let's practice!

If all of this feels extremely confusing, don't worry — that's completely expected! CSS positioning is one of those things that only clicks through repetition. The more you practice, the more natural it becomes.

Below are a few exercises in growing complexity. I strongly advise you to run through them several times — but wait a good while between passes! Spacing out your practice over days or weeks is far more effective than repeating everything in one sitting.

Oh no! 😱

This lesson isn’t available just yet.

Take a look at the status badges next to each lesson in the sidebar to see when it’s coming out.

Thanks for your patience! 🙏