From Tables to Grid
A brief history of how we stopped abusing HTML tables and learned to lay things out properly.
If you've been building websites since the early days, you know the pain: nested <table> elements just to get a sidebar next to content. Every layout was a wrestling match with colspan, spacer GIFs, and prayer.
Here's how we got from there to here:
The Table Layout Era
CSS1 is published, but browser support is spotty. Developers use <table> for everything: navigation bars, multi-column layouts, centering. Spacer GIFs are everywhere. It works, but semantics and accessibility suffer badly.
The Float Era
CSS2.1 gains traction. Developers discover float: left and clearfix hacks. Holy grail layouts emerge. It's clever but fragile — equal-height columns require JavaScript or display: table-cell workarounds. The "clearfix" becomes every developer's best friend.
Flexbox Arrives
After years of drafts and prefixed implementations, Flexbox becomes usable in browsers. One-dimensional layout that actually makes sense. Centering a div vertically becomes a one-liner. The crowd goes wild.
CSS Grid Lands
All major browsers ship CSS Grid in the same year. Two-dimensional layout with rows AND columns. Named grid areas, fractional units, minmax(). The first CSS layout system that was actually designed for layout.
Subgrid & Container Queries
Subgrid lets nested elements align to parent grids. Container queries let components adapt to their container, not just the viewport. Layout is no longer a hack — it's a first-class citizen of CSS.
The Display Property: Your Layout Superpower
Everything in CSS layout comes down to one property: display. It controls two things:
- Outer display type — how the element participates in its parent's flow (block or inline)
- Inner display type — how the element's children are laid out (flow, flex, grid, etc.)
block
Full width, stacks vertically. The workhorse.
inline
Flows in text. No width/height control.
inline-block
Inline flow + block sizing. The bridge.
flex
One-axis powerhouse. Alignment made easy.
grid
Two-axis layout. Rows + columns.
none
Gone. Removed from layout entirely.
Let's explore each one interactively. Click "Next" to dive in.
Block & Inline
The foundation everything else builds on.
display: block
Block elements take up the full available width and stack vertically. Think of them as bricks in a wall. <div>, <p>, <h1> — all block by default.
Key behaviors to notice:
- block: Each box takes full width and stacks vertically. Width and height are respected.
- inline: Boxes sit next to each other like words. Width and height are ignored.
- inline-block: Best of both — sit inline but respect width/height.
- none: Box 2 vanishes — it's removed from layout entirely, not just hidden.
display: none removes the element from layout. If you want it invisible but still taking up space, use visibility: hidden instead.
The Inline Gotcha
Inline elements have a famous quirk: whitespace between them in your HTML creates small gaps. This drove developers crazy for years and was one of the reasons people looked for better layout tools.
<!-- These inline-blocks will have tiny gaps between them --> <span style="display:inline-block; width:100px">A</span> <span style="display:inline-block; width:100px">B</span> <!-- Fix: remove whitespace, or use font-size:0 on parent -->
Flexbox
One-dimensional layout that finally makes alignment easy.
Flexbox lays out items along a single axis (row or column). It excels at distributing space, aligning items, and handling dynamic content sizes. If you've ever struggled to vertically center something — flexbox is your answer.
Interactive Playground
The Flex Shorthand
Child items have their own flex properties. The most important is the flex shorthand:
.item { flex: 1; /* flex-grow:1, flex-shrink:1, flex-basis:0% */ flex: 0 0 200px; /* don't grow, don't shrink, start at 200px */ flex: 2 1 auto; /* grow twice as much as flex:1 siblings */ }
Child Properties Playground
Adjust flex properties for each colored box:
Classic Flexbox Patterns
.parent { display: flex; justify-content: center; align-items: center; height: 100vh; }
body { display: flex; flex-direction: column; min-height: 100vh; } .content { flex: 1; } .footer { /* stays at bottom */ }
.row { display: flex; gap: 16px; } .col { flex: 1; } /* all columns share space equally */
CSS Grid
Two-dimensional layout. The one we were waiting for.
While Flexbox works in one direction at a time, Grid lets you control rows and columns simultaneously. It was the first CSS layout system actually designed for page layout from the start.
Interactive Playground
Named Grid Areas
Grid's killer feature: you can draw your layout with ASCII art. Each string is a row, each word is a column.
Grid vs Flexbox: When to Use Which?
Use Flexbox when...
- Laying out items in one direction
- You want items to dictate their own size
- Navigation bars, toolbars, button groups
- Content-first design (content drives layout)
Use Grid when...
- You need rows AND columns
- You want to control placement precisely
- Page layouts, card grids, dashboards
- Layout-first design (layout drives content)
Other Display Types
The supporting cast you should know about.
display: inline-flex & inline-grid
These make the container inline while keeping flex/grid behavior inside. Useful when you want a flex container that sits in a line of text.
display: table
Yes, display: table still exists and is sometimes useful! It makes any element behave like a <table> without using table HTML. Before flexbox, this was how you got equal-height columns.
.fake-table { display: table; } .fake-row { display: table-row; } .fake-cell { display: table-cell; vertical-align: middle; } /* Today: just use flexbox or grid instead */
display: contents
A newer and powerful one. The element's box disappears, but its children participate in the parent's layout as if they were direct children. Great for wrapper elements that shouldn't affect layout.
The wrapper (dashed border) has 3 children. The grid parent has 3 columns. Watch what happens when the wrapper becomes "contents".
display: flow-root
Creates a new block formatting context. The modern, clean replacement for the clearfix hack. If you have floated children that are overflowing their parent, display: flow-root on the parent contains them.
.parent { display: flow-root; /* Contains floated children. No more clearfix! */ }
Complete Display Type Reference
| Value | Outer | Inner | Use case |
|---|---|---|---|
block |
Block | Flow | Default for divs, paragraphs |
inline |
Inline | Flow | Text-level elements |
inline-block |
Inline | Flow-root | Inline but with sizing |
flex |
Block | Flex | One-axis component layout |
inline-flex |
Inline | Flex | Flex inside text flow |
grid |
Block | Grid | Two-axis page layout |
inline-grid |
Inline | Grid | Grid inside text flow |
contents |
None | N/A | Remove wrapper box |
flow-root |
Block | Flow-root | Contain floats (clearfix replacement) |
none |
N/A | N/A | Remove from layout |
Room Arranger
Test your CSS layout skills. Arrange the furniture to match the target layout.