You should do what works for you, but even so.

When you declare `py-4 px-2 bg-blue text-white rounded-lg` on five buttons in a row, doesn't this work almost exactly like inline strings when you compare to the semantic classname `button-secondary` that is maintained in one single place much like a constant? And wouldn't you just rely on constants to maintain consistency between components, in other words use CSS variables?

.button-secondary {
background-color: var(--color-action-secondary);
}

Inline media queries are not easy to read. What does this button do?

<button class="inline-block px-large py-medium rounded-large border-solid border-thin font-regular bg-core-yellow-100 border-core-yellow-300 bg-text-core-yellow-800 bp-medium:font-medium bp-medium:bg-core-green-100 bp-medium:border-core-green-300 bp-medium:text-core-green-800 bp-large:font-bold bp-large:bg-core-blue-100 bp-large:border-core-blue-300 bp-large:text-core-blue-800 bp-x-large:font-black bp-x-large:bg-core-red-100 bp-x-large:border-core-red-300 bp-x-large:text-core-red-800">Sign in</button>button>

It changes font size, background color, border color and text color when you resize. That's easy to tell if you don't inline it

.button {
@apply inline-block px-large py-medium rounded-large border-solid border-thin;
@apply font-regular bg-core-yellow-100 border-core-yellow-300 text-core-yellow-800;
@media (--bp-medium) {
@apply font-medium bg-core-green-100 border-core-green-300 text-core-green-800;
}
@media (--bp-large) {
@apply font-bold bg-core-blue-100 border-core-blue-300 text-core-blue-800;
}
@media (--bp-x-large) {
@apply font-black bg-core-red-100 border-core-red-300 text-core-red-800;
}
}

Edit: Hacker News did of course inline it, but that only proves the point.

The same goes for light and dark mode. These are by the way Custom Media Queries [1], but Tailwind has a `@screen` directive much to the same effect.

Gradients aren't much different in CSS nowadays, really, and the "built in grid system" is really just a super inflexible subset of CSS Grid that will run out of luck if you need something other than equal sized columns. If you use it for the core page layout, chances are low that you will want your sidemenu, main content and sidebar always appear in equal width columns. In Tailwind you don't, of course, but it is a wonderful feature of CSS.

Tailwind actually is future proof, but that's only because you remove Tailwind from the list of dependencies and check the generated CSS into your souce code repository. At some point all these websites will need an overhaul of the visual identity and people will realize all over again why presentational attributes were removed in HTML4 along with the introduction of CSS. You can update your Tailwind version, then, but nothing will happen. Fortunately, this "plugin system for custom code generation" can be replaced with a stylesheet.

Tailwind has certain qualities, but the things you mention are not some of them. I like that you can't simply copy the CSS from some textarea in Figma and paste it into the CSS, because that will soon make your website even more unmaintable even if it is vanilla CSS. You are in other words forced to think about the layout, the spacing system, the color variables and the breakpoint behaviors, ironically what the typical proponent of Tailwind would like to avoid. If all of these things are declared in a highly customized Tailwind preset, and if you elect to maintain complex components (eg. with synchronized breakpoint changes) in CSS files via `@apply`, it becomes an effective tool to enforce a "design system". Some might say that Tailwind's own creator advises against this strategy, but that should only encourage you, in my opinion.

Essentially, what he is saying [2] is that you should not use SQL to transform you date from one format to the other because the SQL will break when someone changes the database structure. Instead, you should just hardcode the result of this transformation directly into the database and now you won't need SQL at all. You could argue that this would be retarded since the abstraction layer enables different, future presentations of the data in question and that is exactly what I am trying to say.

My best advise is to go light on presentational attributes, which is what utility classes is, and move your Tailwind into proper CSS as soon as the implementation indicates moderate complexity [3]. Here you can make use of the `@layer components` stuff that bears an uncanny resemblance to semantic classnames, but more importantanly, now your Tailwind can easily break out into real CSS, for example to author your core page design with a Grid of uneven column sizing. Make sure to Use CSS variables for everything including your Tailwind preset so that `12px` are never mentioned in the JSON, but instead refers to `var(--base-unit-or-something)` which can be referenced equally in vanilla CSS. Keep the best of both worlds and Tailwind can add more value than it destroys.

[1] https://preset-env.cssdb.org/features/#custom-media-queries

[2] https://adamwathan.me/css-utility-classes-and-separation-of-...

[3] https://frameable.com/company/tech/how-we-found-sanity-in-a-...