Flexbox

By default, every HTML element stacks vertically — one on top of the next, all the way down the page. That's fine for paragraphs, but terrible when you want an image sitting beside a block of text, or three cards sitting side by side in a row.

Before 2015, getting elements to sit next to each other required complicated hacks. Then Flexbox arrived and reduced it to two words.

How Flexbox Works

Flexbox has one golden rule: the container controls the layout of its children.

You apply display: flex to the parent element, and its direct children automatically line up in a row.

.row {
  display: flex;
}

That's it. Every direct child of .row now sits side by side horizontally.

Core Flexbox Properties

All of these go on the container (the parent), not the children.

justify-content — alignment along the main axis

justify-content: flex-start;    /* pack to the left (default) */
justify-content: center;        /* centre everything */
justify-content: flex-end;      /* pack to the right */
justify-content: space-between; /* equal gaps between items, none at edges */
justify-content: space-around;  /* equal gaps around each item */

align-items — alignment along the cross axis

align-items: stretch;    /* children stretch to fill height (default) */
align-items: center;     /* vertically centre the children */
align-items: flex-start; /* align to the top */
align-items: flex-end;   /* align to the bottom */

gap — space between children

gap: 20px; /* adds space between flex children — never on the outer edges */

Use gap instead of adding margins to individual children. It's cleaner and easier to change.

flex-direction — rows or columns

By default, display: flex arranges items in a row (left to right). To stack items vertically inside a flex container, use:

flex-direction: column;

Once you change direction, justify-content and align-items switch axes too — justify-content now controls vertical alignment and align-items controls horizontal.

flex-wrap — let children wrap to the next line

flex-wrap: wrap; /* children drop to the next line if they don't all fit */

Without this, children squish together rather than wrapping.

The flex Property (on children)

The one property you usually set on children is flex. It controls how much space each child takes.

.left  { flex: 1; }  /* takes 1 share of the available space */
.right { flex: 2; }  /* takes 2 shares — twice as wide as the left */

Think of flex: 1 as a ratio. If both children have flex: 1, they split 50/50. If one has flex: 2 and the other flex: 1, it's a 66/33 split.

A Worked Example: Image + Text Side-by-Side

Here's a common pattern — an image on the left, text on the right:

HTML:

<div class="hero">
  <div class="hero-image">
    <img src="cause.jpg" alt="A photo of the subject">
  </div>
  <div class="hero-text">
    <h2>Why It Matters</h2>
    <p>A short paragraph explaining the topic.</p>
  </div>
</div>

CSS:

.hero {
  display: flex;
  align-items: center;
  gap: 40px;
}

.hero-image { flex: 1; }
.hero-text  { flex: 2; }

.hero img {
  max-width: 100%;
  border-radius: 12px;
}

The image column takes 1 share of the space; the text takes 2. The image is automatically vertically centred next to the (probably taller) text block.

Common Mistakes

  • "Flex isn't doing anything." → The parent doesn't have display: flex.
  • "justify-content / align-items is being ignored." → You added it to a child instead of the parent. Both properties only work on the container.
  • "My items all collapse to the left." → Children need flex: 1 (or a width) to expand and fill the row.
  • "My cards are stacking vertically when I want a row." → The container is flex-direction: column, or the cards have width: 100%.

When in doubt, right-click → Inspect and check which element actually has display: flex on it.