Hacking Layout Before CSS Even Existed


Before flex
, before grid
, even before float
, we still had to lay out web pages.
Not just basic scaffolding, full designs. Carefully crafted interfaces with precise alignment, overlapping layers, and brand-driven visuals. But in the early days of the web, HTML wasn’t built for layout. CSS was either brand-new or barely supported. Positioning was unreliable. Browser behavior was inconsistent.
And yet, somehow, we made it work.
So how did we lay out the web?
With tables.
Yup. Tables.
Not the kind used for tabular data. These were layout tables, deeply nested, stretched and tweaked, often stuffed with invisible spacer GIFs to push elements into place. Text, links, and buttons were dropped into cells and floated among a scaffolding of invisible structure.
If you were building websites in the late ’90s or early 2000s, this will sound familiar. If not, consider this a quick trip back to one of the more creatively chaotic eras of frontend development, and what it can still teach us today.
HTML wasn’t made for layout
HTML began as a way to mark up academic documents. Headings, paragraphs, links, lists, that was about it. There was no real concept of “layout” in the design sense. Early browsers had little support for positioning or styling beyond font tweaks and basic alignment.
But developers still wanted structure. And clients, especially as the web grew more mainstream, wanted their brands to show up consistently. They expected full visual treatments: custom type, precise alignment, multi-column layouts, and logos in exactly the right place. Designs weren’t just documents, they were meant to look like something. Often like print. Often pixel-perfect.
So we did what developers always do: we got creative.
Enter the layout table
HTML tables gave us something no other element did at the time: control. You could create rows and columns. You could define cell widths and heights. You could nest tables inside tables to carve up the page into zones. That control was intoxicating.
It wasn’t elegant. It definitely wasn’t semantic. But it worked.
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td width="200">
<img src="spacer.gif" width="200" height="1" alt="">
</td>
<td>
Main content goes here
</td>
</tr>
</table>
Spacer GIFs like the one above were a standard trick. You’d create a 1×1 pixel transparent image, then stretch it using width and height attributes to force the browser to reserve space. There were entire toolkits built to generate spacer-driven layouts automatically.
If you wanted padding, you’d nest another table. For alignment, you’d add empty cells or tweak the align
attribute. And when that wasn’t enough, you’d resort to comment-tag hacks or browser-specific rendering quirks just to make things behave.
Slicing designs into the web
At agencies like AKQA, where I worked at the time, the designs weren’t simple page frames. They were fully realized compositions, often with custom art direction, background textures, and layered effects. We’d receive static visuals, usually Photoshop files, and break them apart manually into dozens of individual image slices.
Some slices were background textures. Some were visual foreground elements: shadows, corners, borders, custom typography before @font-face
existed. Then we’d reassemble everything with HTML tables, mixing sliced images with live HTML, real text, buttons, form inputs, to recreate the original design as closely as browsers would allow.
It was part engineering, part pixel-pushing, part dark art.
Why we did it anyway
It’s easy to laugh now, but back then layout tables gave us something CSS didn’t: predictability.
CSS support was spotty. Browsers implemented it inconsistently. You could spend hours tweaking styles, only to have them break in IE5.5. Tables weren’t perfect, but they rendered the same almost everywhere.
WYSIWYG tools like Dreamweaver leaned hard into the table model. You’d drag content into cells and it would spit out layers of nested HTML you weren’t really meant to touch.
Was it bloated? Yes. Fragile? Absolutely. But it shipped.
The long road to modern CSS
CSS1 arrived in 1996. CSS2 in 1998 brought position: absolute
, float
, and z-index
. But it took years for browsers to catch up, and even longer for developers to trust it.
The table era didn’t really end until the mid-2000s, when modern browsers matured and CSS layout finally became viable. Even then, it took time for the idea of separation of concerns to take hold: structure in HTML, style in CSS, behavior in JavaScript.
Today: grid systems, not grid hacks
Now we have display: grid
and display: flex
. We can align elements without nesting. We can reorder content for accessibility. We can build responsive layouts without a single spacer GIF in sight.
What used to take 100 lines of messy table markup now takes 10 lines of clean, declarative CSS. It’s better for developers, and for users, especially those using assistive tech that struggled to parse table-based scaffolding.
What the table era still teaches us
A few lessons from the layout table era still hold true:
Cross-browser consistency matters. Even now, not everything renders the same. Test broadly.
You’ll always work with constraints. Back then it was no CSS. Today it might be legacy code, team skills, or framework limitations. Creativity under constraint is part of the job.
Understand the tools you’re misusing. Tables weren’t designed for layout, but we understood them deeply. That same mindset helps today when bending modern tools to fit the real world.
In closing
Table-based layouts were a workaround. But they also reflect something constant about web development: we’re always adapting. Always hacking. Always building better experiences with the tools we have, until the next set of tools comes along.
So next time you float a div or write a neat little grid template, give a small nod to the table layouts that walked so Flexbox could run.
Subscribe to my newsletter
Read articles from Den Odell directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Den Odell
Den Odell
Frontend architect and author of "Pro JavaScript Development" (Apress). Built platforms for Volvo, AKQA, and Nike. Now focused on fast, accessible web apps and tools to help others build better.