I can't understand why people bother about HTML with long class attributes so much. If you use "btn" instead of a long string of Tailwind classes, you just move the actual CSS properties into a stylesheet file, which you need to switch back and forth to just to change the looks of that button.
And having said that, when using Tailwind with some kind of component-centred framework, those classes will be local to a <Button> component anyway, which is exactly the same as some kind of custom component the author praises (that also needs styles within.)
If you need to adopt the HTML to fit a certain style, you're doing it wrong. HTML should be a semantic markup, it shouldn't change much when you need to change the styling/CSS. So you don't really go "back-and-forth" to architecture CSS, you just write your CSS. But maybe it's a Tailwind thing that you need to add additional elements to HTML just to be able to style it correctly, I'm not sure. It's like saying you shouldn't have JSON files (and embed the data inside the business logic) as you need to change the JSON file when you need to change the logic of the app. In short, you adopt your CSS architecture to your semantic markup, not the other way around.
I don't know about you, but I've never seen anyone create a user interface by writing down the full, semantic markup, and then proceed to create CSS for that finished HTML structure. Instead, it's usually an iterative process of adding HTML, styling that, then adding more content.
That isn't even related to Tailwind, or any other CSS framework -- just that Tailwind allows you to stay in the HTML (or component) context, without having to switch to a stylesheet there.
Your JSON comparison doesn't fit: We're talking about styling, not business logic. But if you mean that I would need to define `"userAgeOver21": true` in a JSON file instead of having some `{{ %if user.age > 21 }}` in the code, then yes, I'm firmly in the latter camp. Not having to switch context often overrules pedantic purity every time.
I don't understand the big problem with switching different files. Don't we write components and tests in separate files? When we develop a component we add it to other components/views. When you need to add new props to a component, you open up the component file, add your prop, open up your component test file write your tests, if there are any tests fails you go back to component file and fix it there, then if everything works you add your props in the view components. Especially when we are talking about components and component libraries, you don't change the styling every other day especially when design tokens comes from centralised places (spacing, colors, fonts etc.).
What people misses out is CSS should be written in state-based architecture. For example for a button you have idle, hover, active, focus, focus visible, disabled states, and combinations of each button intent (default, primary, destructive, confirmation, warning) and color scheme (dark, light, high-contrast, low-contrast) states. Each of these states should enable-disable or modify some prop of the element, in most cases combination of different elements (sibling, child-parent). Trying to write them in inline classes is a PITA, especially the main concern for choosing that direction is just so we don't have a separate CSS file or need to find a name for the class (if you have a component, which Tailwind creators recommend you should, then you already need to find names for your components).
Also in addition to having separate component, component test and view/layout components; we also have separate hook files, separate state/context/store files. Even the component files doesn't encapsulate its own logic inside the same file anymore as to increase reuse of such logic. Somehow we practice separating almost every thing in the frontend to its own file, but when it comes to CSS it's too much "back-and-forth".
> I don't understand the big problem with switching different files.
Maybe it's really just fundamentally different workflows - I doubt we'll reach consensus here, and that's probably okay.
> What people misses out is CSS should be written in state-based architecture
I agree with you here. That breaks down as soon as you have states that depend on the layout - saying a button should be larger on smaller devices requires adding a style into a media query that lives separate from the button styles, unless you add hundreds of individual media query blocks. Tailwind alleviates that by making "md:" a state prefix just like "hover:".
> if you have a component, which Tailwind creators recommend you should, then you already need to find names for your components
True; I need to name my button component "Button", but I don't have to find a name for the wrapper container of the icon that goes to the right side of buttons that include an icon, which it needs to create a flex context.
How do you change a single CSS property (let's say "border-color") with Tailwind, when the conditions are: "color-scheme: dark", "high-contrast", "focus-visible", "not:disabled", "intent: destructive"? And how do you change 3 such CSS property (border-color, color, background-color) on the same conditions? As far as I can tell, you need to repeat the same condition for each property you need to change. And when you have all these combinations together, which is not uncommon for component libraries to have such different states and combinations, it's just become unreadable mess; just to be able to see which CSS properties the element has without switching files.
I guess to change these 3 property on the same condition, you need to write this TW classes (or create a "variant" in separate config file):
class="dark:contrast-more:focus-visible:enabled:data[intent=destructive]:border-slate-700 dark:contrast-more:focus-visible:enabled:data[intent=destructive]:bg-slate-50 dark:contrast-more:focus-visible:enabled:data[intent=destructive]:text-gray-50"
Or am I missing something that Tailwind makes it as easy as writing these states and combinations in regular CSS (without apply, which TW creators don't recommend using)? If that example is the preferred way of writing TW, and it's only 1 condition combination, I would happily switch back-and-forth files and write regular CSS without that syntactic high-fructose corn syrup.
> I've never seen anyone create a user interface by writing down the full, semantic markup, and then proceed to create CSS for that
Indeed, not in the past 7-8 years. But we used to do it, and the outcome was great - semantic, accessible and machine-readable HTML was the norm. It’s unfortunate that we lost this in the transition to components.
Is that so? From my memory, most people used tables for layouts, or full-out relied on div tags for everything. At some point, HTML5 brought headers and footers and navs and more, and if anything, that made the situation better since.
But if the working groups continue to pretend HTML is for text documents instead of web applications, things will never get more semantic.
> But if the working groups continue to pretend HTML is for text documents instead of web applications, things will never get more semantic.
I mean, HTML is and will always forever be a system to display a few pages of text with some images thrown in. And even for that it sucks big time.
Anything standards committees have been throwing at it over the years are just haphazard hacks to make it into something it's not with no coherent goal in mind.
I started in 2004. Spent the years of 2007-2012 writing semantic, standards-compliant html (microformats, the works), css and js. Even my first years at booking.com still had that as a concern. Unless this was all a fever dream…
Maybe we simply had significantly different experiences? We were involved in many conferences and can definitely say it wasn’t just us.
Everyone I knew in the field was doing the same, Zeldman / ALA was everyone’s hero, UX, accessibility and standards compliance were the highlights of every project, not tooling or frameworks. Not saying everything was perfect but the focus was clearly different.
“Need to switch back and forth” is a strangely negative way of putting it. Do you “switch back and forth” to a function definition, and is that an argument for procedural code and repetition?
Then your second paragraph is a great point against the need for Tailwind and the long class attributes, as you’re going to encapsulate all of that in a component (and “switch back and forth” to see its styles) anyway.
You've pretty much answered this yourself - a function, just like a component, is a self-contained unit. If you're working within the scope of a function, you don't switch back and forth; but I'm pretty sure you wouldn't want to have to declare the names of any local variables in that function in some kind of global variable table in another file ("let's add a loop here.. switch to vars.file, add someFunction_firstLoop_i as 0... back to the loop, add the variables...").
And having said that, when using Tailwind with some kind of component-centred framework, those classes will be local to a <Button> component anyway, which is exactly the same as some kind of custom component the author praises (that also needs styles within.)