Tailwind CSS layouts: keep the grid in your markup

For years the accepted way to build Tailwind CSS Layouts was to not build them there at all. You pushed everything into a stylesheet and argued about BEM names. I did that too, until I spent one afternoon too many debugging a class called “wrapper-inner-v2-final” and admitted the split was costing me more than it saved.

In 14 years of this work I have watched plenty of sites break because the CSS and the HTML drifted apart. Somebody renames a wrapper, nobody touches the stylesheet, and one template quietly collapses. Tailwind puts the layout back in the markup, so what you read is what renders.

1. Layouts depend on the HTML structure

Move a layout into an external file and you lose the map of the document. Read a grid definition on its own and you have to reconstruct the markup in your head to work out which child lands in which track. Two files, one picture, and you are the only thing keeping them in sync.

/* The traditional way: Hard to visualize without the HTML */
.grid-container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.main-content {
  grid-column: span 2;
}

Now put the same thing in utility classes. With Tailwind’s grid utilities, the shape of the page sits in the tags you are already reading, so Tailwind CSS Layouts stop being a second file you have to open.

<!-- The Tailwind way: Immediate clarity -->
<div class="grid grid-cols-3">
  <div class="col-span-2">Main content</div>
  <div class="col-span-1">Sidebar</div>
</div>

2. You do not have to name every layout

Layouts are the hardest thing in CSS to name, and I have had years of practice. A class like .two-columns says nothing about the ratio, the gap, or the alignment, so every slightly different case earns a variant of its own and the file fills up with near-duplicates. I went further into where that line sits in Tailwind components vs utilities.

A layout used once does not need a name. Put the numbers in the markup instead. An arbitrary value or a CSS variable in the class list, something like [--cols:7], tells the next reader more than an abstract name would.

3. Layouts change with their context

The same grid wants gap-8 in a marketing section and gap-4 inside a nested newsletter form. In plain CSS that means a modifier class or another level of nesting. In Tailwind you change the number where the markup is, and the global stylesheet never hears about it.

This is the point where you have to actually know the CSS underneath, and MDN’s material on grid track sizing is worth the read. Once the native behaviour is in your head you stop arguing with the framework and start treating it as shorthand.

4. Responsive changes stay on the element

WordPress footers are the usual offender: two columns on mobile, five on desktop. Writing a media query for each of those is how a stylesheet turns into a refactor you keep postponing. Tailwind’s prefixes (md:, lg:) let you set the responsive factor on the element itself, which is where you are already looking.

If naming conventions are still how you handle scope, CSS scope as an alternative covers the other route. Different mechanism, same aim, and the structure stays yours.

If layout work like this is eating your week, I take it on for clients. I have been building WordPress sites since the 4.x days.

Pragmatic layouts

Writing less CSS was never the point. The point is that six months later you can open a template and see what it does. Tailwind utilities plus a CSS variable where a utility runs out gets me there: quick to write, still exact about the numbers. That is the approach I teach in my “Unorthodox Tailwind” course.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.