D
P
0
← All articles Baca dalam Bahasa Indonesia

Tailwind v4 Build-Time Pitfalls

Footer `lg:grid-cols-[1.6fr_repeat(6,minmax(0,1fr))]` Collapses to Three Columns in Tailwind v4? Arbitrary Values That Vanish at Compile

· · 5 min read
Footer `lg:grid-cols-[1.6fr_repeat(6,minmax(0,1fr))]` Collapses to Three Columns in Tailwind v4? Arbitrary Values That Vanish at Compile

The footer of a personal site I was building used lg:grid-cols-6. The brand block took one cell, five link columns filled the rest of the first row, and the Portfolio column, which happened to carry lg:col-start-2, dropped alone onto the second row right under Products. The judgement was mine, not the client's. I was the one who found that lone column awkward, standing by itself underneath everything.

I wrote down three ways out: move to lg:grid-cols-7 so the brand block and six link columns fit on one row, split the brand block off and give the six link columns their own grid, or merge two columns into one. The decision was that the footer had to be a single row.

The shape I chose for release v0.1.5 was a Tailwind arbitrary value, so the brand column could be wider than the six link columns without leaving the utility system:

<div class="grid md:grid-cols-3 lg:grid-cols-[1.6fr_repeat(6,minmax(0,1fr))]">
  <!-- brand + 6 link columns -->
</div>

That release was recorded as verified live through a cache-busted public view on the afternoon of June 24. The next footer note is dated June 25, and in it the footer on the public page has collapsed to md:grid-cols-3, with Portfolio orphaned on the following row again.

Why this happened

My notes from that day hold two accusations, and I need to write both down as what they were, suspicions rather than certainties.

The first: Tailwind v4 dropped that arbitrary class at compile, and the reason was that the commas inside repeat() broke arbitrary value parsing. What made this plausible was the comparison set in the same project. Part of that comparison set was only written down later, so it was not something I already had in hand back in June. Arbitrary grids that used underscores and no commas, like md:grid-cols-[220px_1fr] and grid-cols-[1fr_535px], provably survived in the compiled stylesheet after a recompile, and so did aspect-video. In that same project Tailwind v4 also refused scale-[0.8803], which is why that transform ended up as an inline style. So the pattern of "a particular arbitrary value never reaches the CSS" is something I have met more than once in this codebase. What I never did was isolate the comma-bearing class in an empty build to prove the comma was the culprit. I have the symptom and the comparisons, not a parser-level proof.

The second accusation explains why an admin saw the footer intact while the public saw it broken: WP Rocket Remove Unused CSS. Public visitors get a per-URL inlined "Used CSS" that was frozen before the new class existed, while logged-in admins bypass RUCSS and the page cache. For that half, the fix was a cache procedure, not code.

One note on versions, for honesty: the figure tailwindcss v4.3.0 in this project was only recorded in an update on July 22, almost a month after the footer incident. I have no record of the exact version on the day itself.

The fix

I stopped arguing with the arbitrary value and wrote the grid as a plain CSS class in input.css, responsive at two, three and then seven columns, with the seven-column track 1.6fr repeat(6, minmax(0, 1fr)) at the lg breakpoint:

.site-footer-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
 
@media (min-width: 768px) {
  .site-footer-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
 
@media (min-width: 1024px) {
  .site-footer-grid {
    grid-template-columns: 1.6fr repeat(6, minmax(0, 1fr));
  }
}

footer.php uses class="site-footer-grid". This class never passes through the arbitrary value parser at all, so whatever really happens to commas inside repeat() stopped mattering.

On the cache side I ran Clear Used CSS, Clear and Preload, purged Cloudflare, and added .site-footer-grid to the RUCSS safelist so the rule would not be stripped again on the next regeneration.

I verified without credentials, so what I saw was genuinely the public view: the footer computed to display: grid with seven columns. At 1069px wide each column came to roughly 88px, tight but functional, and that is the consequence of the single-row decision I made myself. The fix went into commit v0.2.0 on June 25 together with footer.php, input.css and tailwind-compiled.css.

The second incident, one day later

On June 26, on a fintech publication theme that is also compiled through the Tailwind v4 CLI into assets/css/tailwind-compiled.css, the person cards on the directory page used aspect-[1/1] and aspect-[4/5]. Neither class produced a single rule in the compiled CSS. The bug was latent: the old cards had relied on those classes from the start, and what made it bite was having real members to render. I made the "latest names" grid data-driven through get_posts on the directory_person post type, newest twelve, rendering real members if any exist and the launch placeholder if not.

The fix went in the same direction as the footer. The person card image uses an inline style rather than a utility:

<img ... style="aspect-ratio: 1 / 1;">

After that I recompiled tailwind-compiled.css and checked the other new utility classes one by one, among them md:grid-cols-5, sm:flex-row, lg:justify-end and scroll-mt-16, and all of them were present. The pages shipped and were committed the same day. What makes this case interesting is that on the initial port of that theme, Tailwind compiled with more than 46 arbitrary classes preserved. Arbitrary values as a feature clearly work here. What failed were specific shapes.

Lessons

The same pattern turns up elsewhere in that project, on the navbar scrim: inline styles or plain CSS classes are chosen deliberately, because a new arbitrary utility would not be compiled, and a rule keyed to a JS-toggled class is exactly what RUCSS strips. That way the Used CSS needed no wipe and no site-wide regeneration was spent.

The rule is simple. If a value is used on a single element and its shape is complicated, whether a comma-bearing repeat(), a ratio with a slash, or a four-digit decimal, I no longer put it inside Tailwind's square brackets. I write it as plain CSS and prove it exists in the compiled stylesheet before uploading. A utility that matches no rule never throws an error. It just stays silent, and your footer collapses to three columns without telling anyone.