The Evolution of CSS Layout: From Tables and Floats to Flexbox and Grid
For a long stretch of the web's history, building a page layout meant bending tools to a purpose they were never meant for. The techniques worked, in the sense that pages shipped and businesses ran on them, but anyone who wrote CSS in those years remembers the feeling of fighting the language instead of using it. The story of CSS layout is really the story of the tools slowly catching up to what people were actually trying to do.
The table era
In the late 1990s and early 2000s, the standard way to lay out a page was the HTML table. Tables were designed for tabular data, rows and columns of numbers, but they were the only reliable way to put two things side by side that worked across the browsers of the day. So designers nested tables inside tables inside tables, sometimes many levels deep, to carve a page into regions.
It produced layouts, and it was miserable. The markup was a tangle that mixed structure and presentation, it was nearly impossible to change, and it was hostile to screen readers, which tried to read these layout tables as if they held real data. Everyone knew it was wrong. There was just nothing better yet.
The box model, the foundation underneath
Before the alternatives make sense, it helps to name the thing all of CSS layout rests on: the box model. Every element on a page is a rectangular box, and that box has content in the middle, padding around the content, a border around the padding, and margin outside the border[1]. Almost every layout confusion a beginner hits traces back to how those four layers add up.
One detail caused years of pain. By default, the width you set applied only to the content, so padding and border were added on top, making elements wider than you expected. The fix became so universal that most projects now start with one rule.
*, *::before, *::after {
box-sizing: border-box;
}
That tells the browser to include padding and border inside the width you set[2], so a box that is 300 pixels wide stays 300 pixels wide. It is a small line with an outsized effect on sanity.
Floats and the clearfix years
The move away from tables ran through a property that, like tables, was built for something else. The float property was designed to let text wrap around an image, the way a magazine wraps a paragraph around a photo. Developers realized they could float whole columns to place them side by side, and for most of a decade, floats became the backbone of page layout.
It never fit cleanly. A floated element is taken out of the normal flow[3], which caused parent containers to collapse as if the floated children were not there. The workaround, a bundle of CSS known as the clearfix, had to be pasted into project after project to force containers to wrap their floated contents.
.clearfix::after {
content: "";
display: table;
clear: both;
}
Nobody enjoyed writing that. Floats could be made to work, but vertical centering remained famously hard, equal-height columns required hacks, and the whole approach felt like a workaround because it was one.
Flexbox, layout in one dimension
Flexbox, which became usable in browsers in the mid 2010s[4], was the first CSS tool designed from the start for laying out interfaces. It handles one dimension at a time, a row or a column[5], and it is very good at distributing space among items along that line.
The problems that used to need hacks became single declarations. Centering something vertically and horizontally, the task that generated a thousand frustrated forum posts, became three lines.
.container {
display: flex;
justify-content: center;
align-items: center;
}
Flexbox understands how to space items apart, how to let them grow or shrink to fill available room, and how to align them. For navigation bars, button groups, card rows, and the countless small arrangements that make up an interface, it remains the right tool.
Grid, layout in two dimensions
Flexbox handles a line. Grid, which arrived in browsers in 2017[6], handles a plane. It lets you define rows and columns together and place items into that structure, which is what you actually want for the overall skeleton of a page.
.page {
display: grid;
grid-template-columns: 240px 1fr;
gap: 24px;
}
That creates a sidebar of a fixed width next to a main area that takes the rest of the space, with a consistent gap between them, in four lines and no hacks. Grid can describe complex, magazine-style arrangements that would have taken nested tables or a pile of floats, and it does so in a way that is readable months later. The common guidance holds up well: reach for Grid for the two-dimensional structure of a page, and Flexbox for the one-dimensional flow of components inside it.
Container queries, the modern step
For years, responsive design meant reacting to the size of the whole browser window through media queries[7]. That was a real advance, but it had a blind spot. A component does not care how wide the window is. It cares how much room it has been given, and the same card might sit in a narrow sidebar on one page and a wide main column on another.
Container queries, now supported across modern browsers, close that gap. A component can respond to the size of its own container rather than the size of the screen[8].
@container (min-width: 400px) {
.card { grid-template-columns: 1fr 1fr; }
}
This is the shift from thinking about pages to thinking about components. A card can be written once and trusted to arrange itself correctly wherever it is placed, which is exactly how modern interfaces are built out of reusable pieces.
Where this leaves us
CSS layout went from borrowing tables and floats, tools meant for data and for wrapping text, to a real set of purpose-built tools. Flexbox and Grid handle the arrangement, container queries handle responsiveness at the component level, and the whole thing finally matches the mental model designers had all along. If you learned layout recently, the old hacks sound like folklore. They were daily life, and the modern tools are worth appreciating precisely because of what they replaced.