CSS layout has changed completely in the past two decades. What once required nested tables and spacer GIFs now takes a few lines of modern CSS. This is the story of how we built layouts, and how we build them today.
The Table Era
In the late 1990s and early 2000s, tables were the only reliable way to create multi-column layouts. CSS existed, but browser support was inconsistent. Tables worked everywhere, so we used them for everything.
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td width="200" valign="top">
<!-- Sidebar content -->
</td>
<td width="*" valign="top">
<!-- Main content -->
</td>
</tr>
</table>This code creates a two-column layout. The sidebar gets 200 pixels. The main content fills the rest. The asterisk in width=”*” means “take remaining space.” It was a hack, but it worked.
Table-Based Layout (2000s)
Sidebar 200px fixed width | Main Content Fills remaining space |
Semantic HTML mixed with layout structure. Hard to maintain and inaccessible.
Tables worked, but they came with problems. HTML structure was tied to visual layout. Screen readers announced table navigation. Responsive design was impossible. Changing the layout meant rewriting HTML.
We used spacer GIFs for spacing. These were transparent 1×1 pixel images stretched to create gaps. Want 20 pixels of space? Insert a spacer GIF with width=”20″. It was janky, but it was the only way to control spacing consistently.
The Float Era
CSS floats arrived and changed everything. We could finally separate structure from presentation. Floats were designed for text wrapping around images, but we used them for entire layouts.
.sidebar {
float: left;
width: 200px;
}
.main {
margin-left: 220px;
}
.clearfix::after {
content: "";
display: table;
clear: both;
}This creates the same two-column layout using floats. The sidebar floats left. The main content uses margin to avoid overlapping. The clearfix hack prevents the container from collapsing.
Float-Based Layout (2000s-2010s)
Better separation of concerns, but required clearfix hacks and careful margin calculations.
Floats were better than tables, but they had quirks. Elements needed specific widths. Margins had to match exactly. The clearfix hack was mandatory. Responsive design required media queries and recalculating all the math.
Three-column layouts required nested floats. Four columns meant complex math. Equal-height columns required JavaScript or background image tricks. We built entire frameworks around float-based grid systems.
The Flexbox Revolution
Flexbox arrived and solved one-dimensional layouts. Navigation bars, form controls, and component internals became simple. No more float hacks or margin calculations.
.container {
display: flex;
gap: 1rem;
}
.sidebar {
width: 200px;
}
.main {
flex: 1;
}This creates the same layout with Flexbox. The container uses flex. The sidebar keeps its width. The main content uses flex: 1 to fill remaining space. The gap property handles spacing automatically.
Flexbox Layout (2010s)
No hacks needed. Automatic spacing. Works for one-dimensional layouts.
Flexbox made component layouts easy. Centering content required two properties instead of complex positioning. Navigation bars aligned perfectly. Form controls arranged themselves. But Flexbox still struggled with two-dimensional layouts.
The Grid Era
CSS Grid arrived in 2017 and changed everything again. We finally had a proper two-dimensional layout system. No hacks, no workarounds, no JavaScript.
.container {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1rem;
}This creates the same layout with Grid. Two lines of code. No floats, no margins, no clearfix. The grid-template-columns property defines the columns. The gap property handles spacing.
Grid Layout (2017-Present)
Clean, semantic, powerful. Two-dimensional layouts without hacks.
Grid handles complex layouts that Flexbox cannot. Page structures with header, sidebar, main, and footer become readable templates. Card grids align automatically. Overlapping elements work naturally.
Side-by-Side Comparison
Here is how the same layout evolved across four eras.
Layout Evolution Timeline
| Sidebar | Main |
Semantic HTML mixed with layout. No separation of concerns.
Better separation, but requires clearfix and margin math.
Clean one-dimensional layouts. No hacks needed.
Two-dimensional layouts. Minimal code. Maximum power.
Complex Layouts Then and Now
Simple two-column layouts show the evolution, but complex layouts reveal the real difference. Here is how we built a page with header, sidebar, main content, and footer.
The Old Way
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td colspan="2"><!-- Header --></td>
</tr>
<tr>
<td width="200" valign="top"><!-- Sidebar --></td>
<td width="*" valign="top"><!-- Main --></td>
</tr>
<tr>
<td colspan="2"><!-- Footer --></td>
</tr>
</table>This required nested tables for anything complex. The colspan attribute merged cells. valign controlled vertical alignment. Every change meant editing HTML.
The Modern Way
.page {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
}Grid template areas make the layout readable. You can see the structure in the code. Rearranging sections means changing the template, not rewriting HTML.
Complex Layout Comparison
Table-Based (Old)
| Header | |
Sidebar | Main Content |
| Footer | |
Requires colspan, nested tables for complex layouts. HTML tied to structure.
Grid-Based (Modern)
Clean CSS. Semantic HTML. Easy to rearrange and maintain.
Responsive Design Evolution
Responsive design changed how we think about layouts. Tables could not be responsive. Floats required media queries and recalculating everything. Grid makes responsive design natural.
Floats with Media Queries
.sidebar {
float: left;
width: 200px;
}
.main {
margin-left: 220px;
}
@media (max-width: 768px) {
.sidebar {
float: none;
width: 100%;
}
.main {
margin-left: 0;
}
}This required overriding floats and recalculating margins at every breakpoint. The code duplicated logic. Maintenance was difficult.
Grid with Auto-Fit
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1rem;
}Grid handles responsiveness automatically. No media queries needed. Columns adjust based on available space. The layout adapts naturally.
Responsive Card Grid
Resize your browser window. The grid adjusts automatically without media queries.
So what did we actually gain from all this evolution? The obvious stuff: separation of concerns, better accessibility, easier maintenance. But honestly, the biggest win is just writing less code. A complex Grid layout takes fewer lines than a simple table layout used to. No more clearfix hacks, no spacer GIFs, no margin calculations that break when you change one thing.
That said, tables weren’t all bad. They worked everywhere, immediately. No browser support checks, no fallbacks. Every browser rendered them the same way. And let’s be honest: HTML tables were easier to understand. Beginners could build layouts without learning CSS at all. Debugging was visual: you could see the cells right there in DevTools. CSS layout debugging requires understanding the box model, flex context, grid lines, and half a dozen other concepts.
CSS keeps evolving with container queries, better subgrid browser support, and cascade layers that help with specificity wars. But the fundamental shift already happened: we moved from HTML-based layout to CSS-based layout, from hacks to proper tools, from tables to Grid.