CSS Flexbox: Interactive Guide

Flexbox is easier to internalize by dragging it than by reading about it. This guide covers the model, then hands you a live container and six boxes: change the container controls or drag a box onto another to swap their positions, and the CSS driving what you're looking at regenerates underneath in real time.

1

The Flexbox Model

Setting display: flex on a container turns its direct children into flex items laid out along a main axis, horizontal by default. Everything flexbox does is described in terms of that main axis and the cross axis running perpendicular to it; the five properties below all just position items along one or the other.

flex-direction is what decides which axis is which: in row the main axis runs left to right and the cross axis runs top to bottom; switch to column and that flips, the main axis becomes vertical. Every other property below keeps the same meaning either way, since they're defined relative to the main/cross axes, not literally to "horizontal" and "vertical."

2

Live Demo

Try it: Change the dropdowns below to see how each container property repositions every box at once. Then click and drag (or touch and drag) any box onto another to swap their positions; the CSS panel updates the moment you drop it.
1
2
3
4
5
6
flex-demo.css Reset order

                
3

Container Properties

These five properties (plus display: flex itself) all live on the container, not the items, and correspond directly to the dropdowns in the demo above:

PropertyWhat it controls
flex-direction Which way the main axis runs: row, row-reverse, column, or column-reverse. The -reverse variants flip the visual order without touching the underlying source order or the order property.
justify-content Spacing of items along the main axis: bunched at the start or end, centered, or spread out with space-between (no space at the outer edges), space-around (equal space around each item, so edge gaps are half-size), or space-evenly (every gap, including the edges, is the same size).
align-items Positioning of items along the cross axis. stretch (the default) makes every item fill the cross-axis size unless it has an explicit height/width set on that axis, which is exactly why the boxes in the demo only fully stretch vertically when flex-direction is a row.
flex-wrap nowrap (the default) forces every item onto one line, shrinking them if they don't fit. wrap lets items overflow onto additional lines instead.
gap Fixed spacing between items, on both axes. Modern and the preferred approach; the older alternative was margin tricks on individual items, which got messy fast once wrapping was involved.

Try switching flex-direction to column in the demo, then changing align-items: instead of moving boxes up and down, it now moves them left and right, because the cross axis rotated along with the main axis.

4

Item Properties: order, flex, and align-self

The container properties in Section 3 apply to every item at once. A handful of properties go the other way: set on an individual item, they override how that one item behaves within the container's layout. order is the one driving the drag-and-drop demo above.

.box-3 {
    order: -1; /* visually first, regardless of where it sits in the HTML */
}

Every flex item has a default order of 0. Items are laid out by sorting on order first, and falling back to source (HTML) order for any items that tie, which is why two untouched boxes (both still at the default 0) stay in their original relative order even after a third box elsewhere gets pulled to the front with a negative value. Critically, order changes only the visual position; it doesn't move anything in the DOM, doesn't change tab order for keyboard navigation, and doesn't change how a screen reader announces the content, which makes it purely a visual layout tool and a poor substitute for actually reordering meaningful content in the markup.

flex-grow, flex-shrink, flex-basis

Often written together as the flex shorthand, these three control how an item's size responds to the container having extra or insufficient space. flex-basis is the item's starting size before any growing/shrinking happens; flex-grow is a ratio describing how much of any leftover space the item should claim relative to its siblings; flex-shrink is the same idea in reverse, how much of a size deficit the item should absorb when there isn't enough room for every item at its basis size. flex: 1, by far the most common use, is shorthand for flex: 1 1 0%: ignore each item's natural content size and divide the container's space evenly between them.

align-self overrides the container's align-items for one specific item on the cross axis only, useful for pulling a single item to the opposite end of the cross axis from all its siblings without having to restructure the container.

5

How the Drag-and-Drop Demo Works

The demo doesn't use the HTML5 native drag-and-drop API (draggable="true", dragstart/drop), which has no built-in touch support. It's built on the Pointer Events API instead (pointerdown/pointermove/pointerup), the same approach used in this site's Pong guide, so dragging works identically with a mouse, a finger, or a pen.

// On press, lift the box visually and remember where the pointer
// grabbed it relative to the box's own top-left corner.
box.addEventListener("pointerdown", function (e) {
    dragging = box;
    box.setPointerCapture(e.pointerId);
    var rect = box.getBoundingClientRect();
    grabOffsetX = e.clientX - rect.left;
    grabOffsetY = e.clientY - rect.top;
    box.classList.add("is-dragging");
});

While the pointer moves, the dragged box follows it with a CSS transform: translate(...), computed from the pointer's current position minus that initial grab offset, so the box doesn't visually "jump" to re-center on the cursor the instant the drag starts.

Finding what's underneath the dragged box is the part that needs a small trick: the box being dragged is itself the topmost element at the pointer's position, so a naive hit test would just find itself. Temporarily setting pointer-events: none on the dragged box makes the browser see straight through it to whatever box is actually underneath:

box.style.pointerEvents = "none";
var under = document.elementFromPoint(e.clientX, e.clientY);
box.style.pointerEvents = "";

var target = under ? under.closest(".flex-box") : null;

On release, if the pointer is over a different box, the two boxes' order values are swapped and the dragged box's transform is reset to none; flexbox itself handles animating both boxes into their new slots, since order is included in the box's transition-eligible layout. Drop it back where it started, or off the container entirely, and nothing changes; only a drop onto another valid box commits a swap.

6

Reference

PropertyApplies toDefault
display: flexContainer
flex-directionContainerrow
justify-contentContainerflex-start
align-itemsContainerstretch
flex-wrapContainernowrap
gapContainer0
orderItem0
flex-growItem0
flex-shrinkItem1
flex-basisItemauto
align-selfItemauto (inherits align-items)