Hacker News new | ask | show | jobs
by zschuessler 1804 days ago
The underlying problem I see that Tailwind solves is component-izing a single idea. And that it doesn't get in the way with opinions like Bootstrap, et al. It does that pretty well. You can point to a single stack of code and say "this one thing is a navbar" and it's easy to know where to make changes. No searching, no stress.

What I haven't seen Tailwind do well on my projects is respond to change in the same way other methods would. I'm not saying it isn't possible, just that the several projects I've seen, it was a big pain.

A method I've been quite fond of to accomplish the same task, but remove the difficulties this author's other posts articulate, is to recreate the concept of a component in a single folder. Much like you would a Vue single-file component. If you read the "Components" heading here you'll get a good feel for it by looking at the folder/file structure:

* https://github.com/flyntwp/flynt#components

That same idea I've enabled in a framework/language-agnostic way with an NPM module I created (not public). It watches the folder, namespaces css/js like a single-file Vue component would (using a manifest name or defaults to the folder name). It's a freeing feeling to create a folder and just start coding sass and js without having to wire anything together. As it's auto-namespaced, sass files are very clean too, you can use generic naming like ".heading" without a problem.

This solution is nice because:

1. devs get the "one source of editing" in a single folder. Even image assets or your language's logic files.

2. Immense benefit with static analysis tools. Want to assert all your components have a readme.md? or generate docs from all component readmes? Sure.

3. it's performant, with vendor/tree-shaking and inheritance built in. Also parallel build execution of all components.

4. it's agnostic to language/frameworks. Include any linters or unit tests you want.

5. is a paradigm shift from themes to components, just like Tailwind

6. No more wiring together components! specify a global like Vue or your favorite sass package, and all components can inherit it

7. Simple setup.. all you do is point to a folder of components, specify a dist folder, and you're done unless you want to modify specific behaviors

I'm curious if there are Tailwinders out there who could teach me if Tailwind solves other problems I'm not seeing. Or if this type of solution would be something I should open source. It ends up being a pretty simple Webpack file under the hood, and it has been an absolute dream to work with over the past year.

1 comments

> I'm curious if there are Tailwind users out there that could teach me if Tailwind solves other problems I'm not seeing.

I'm building a RAD platform (I hate the term low code) and just pushed through Tailwind as the only method for styling apps and banned custom CSS.

The primary value Tailwind provides is a reasonably well considered design system. I believe CSS' primary weakness is the general inability to define cross-cutting abstractions. I think of the style of an object as a composition of concerns: spacing, position, size, typography, color, decoration, etc. Programming languages have interfaces or traits or protocols to handle this sort of thing but there's no equivalent in CSS. The addition of CSS variables provides limited ability to do this like you can set up a primary color and use that everywhere but CSS variables alone don't let you set up type or spacing scales. I realized this in 2009 and switched over to defining these abstractions as sass mixins that basically boil down @apply with Tailwind utilities where my mixins were the first part of the utility name and the argument(s) the last part. Having a design system simplifies the job of both the designers and developers. There's no guessing on whether it's a 5px or 6px margin, it's a `margin(2)`. Further, if you pick cooperating scales, the page tends to fall into a vertical rhythm, which is tough to do ad-hoc.

Tailwind is an improvement over my self-designed sass mixins mostly because it has more effort behind it and enough people banging on it that it's been expanded to cover a majority of the CSS you need to implement apps. I add a set of CSS-variable based color names to handle theming (`pri` for primary, `sec` for secondary, `ter` for teritary, and `acc` for accent, with `-dark` and `-light` variations) and allow custom utilities for the project. The latter provides an unsupported escape hatch but is enough friction that I'm hoping people won't abuse it excessively.

For my specific use case there are other benefits:

* No specificity issues

* Having to come up with a name for elements is a significant source of friction in a graphical interface

* Naming CSS classes well requires some concept of the overall system and how the element being targeted fits into it. I do not expect my users to have this knowledge.

* I only allow modification of specific points in a component that have a data- attribute containing component-unique names. This will allow me to change the underlying markup and have a fighting chance at successfully migrating custom styles.

* By breaking the link between styling intent and page structure I have the potential to translate apps into non-browser programming environments.

I find Tailwind markup to be quite ugly and think it doesn't have as much advantage in other situations but it fits my specific problem well.

> I believe CSS' primary weakness is the general inability to define cross-cutting abstractions. I think of the style of an object as a composition of concerns: spacing, position, size, typography, color, decoration, etc.

I see this come up and it's so strange to me; the only thing I can think of is that what people are complaining about is too many ways to define cross-cutting abstractions (variables and mixins / other apply boosters and even css classes themselves) so the imposition of a framework-specific way to do it feels like it relieves a burden.

Similarly, I'd guess that you'd see a lot of overlap between people who'd chosen their own conventions for addressing this in combination with other design systems and people who don't like Tailwind much.

> the only thing I can think of is that what people are complaining about is too many ways to define cross-cutting abstraction

I meant exactly what I wrote. The primary axis for styling is applying a set of properties using a selector. The cross-axis abstraction for providing restrictions on what values the properties contain has not been part of the language for the vast majority of its existence.

> variables and mixins / other apply boosters and even css classes themselves

The only options here that are actually CSS are variables (which are relatively new to the language) and utility classes, which work against the general desire for semantic class names and separating structure from presentation. I'm familiar with the proposals for the others but I'm not aware of them being standardized and I know they aren't supported in any browsers.

There are plenty of reasons to not like Tailwind but I expect most people who do like Tailwind aren't coming off a project with an established design system and method for applying it. I have some complaints about Tailwind's choices but it's a fine set of defaults and has enough customization for me to disable or replace the utilities I don't like.

Possibly I'm not understanding what you're getting at, so maybe it's worth another round of exchange.

> The only options here that are actually CSS are variables (which are relatively new to the language) and utility classes

So before widespread adoption of SASS/LESS, I might have expected some places that took a systematic approach to design to use utility classes as kind of a mid-tier in a three-layer model that took care of balancing general, cross-cutting, and specific concerns:

- general (or very widely cross-cutting) concerns would go at high levels: *, html, body, sometimes qualified with semantic classes for varying pages (e.g. body.departmentname, html.sitesection). A lot of type/line related information would go here, probably some page level margin/padding/alignment and a few other instructions. Then there'd be some kind of generalizations for other tag-level elements (ul, p, table, h1-6, etc).

- modestly cross-cutting concerns would get their own utility class names, roughly grouped by categories you're talking about (spacing, position, size, typography, color, decoration, etc), so you'd see .box-I, .type-IV, .col-X, .scheme-C

- specific/exceptional "last mile" scoped concerns would get semantic class names (also usually used for attaching behavior via JS).

The rise of SASS/LESS seemed to change one major thing: a lot of the middle/modest layer of cross-cutting concerns could/would find their way into mixins and then get imported into semantic class names, keeping the concerns centralized while cutting down on utility class name clutter in the markup. Some shops would use variables instead. Either way, it streamlined the already existing manner of handling the middle layer of cross-cutting concerns.

Now, mixins/@apply aren't part of native CSS yet (and though variables are, they're are probably inferior from a DRY/structural perspective), but existing preprocessors have had such widespread adoption for most of the last decade that it's confusing to me for someone to approach Tailwind as if isolating cross-cutting concerns weren't a problem solved in other ways previously. Certainly Tailwind's @apply isn't native either and I'm not clear on why its utility classes would be inherently superior to other pre-existing utility practices.

Hence my assumption that most of Tailwind's fans are people who hadn't found themselves working with these concepts before they picked it up. And if Tailwind is people's introduction to working that way -- and it was that hard to come by beforehand -- maybe if nothing else it is in fact doing people that favor, albeit with the overhead of being married to a level of utility classes that I think is simultaneously too scattered and duplicative and its own specific tooling.

Or maybe there's something else I don't understand about it yet?

When I talk about cross-cutting I'm very specifically referring to restricting the values of properties to the ones matching the design system at the CSS language level and not the can I arrange the page to be styled level.

I haven't explained my biases. Perhaps a technicality but I don't consider the reset styles to really be a consideration. They're something set up at the start of a project and not really touched day to day. With the rise of component-based development, I strongly prefer my styling to not be context dependent and so I avoid relying on styles cascading from outside the component aside from the reset styles. Neither of these is required but I think this is relatively common. This leaves the utility/mixin layer in your taxonomy and thus my focus on it.

> existing preprocessors have had such widespread adoption for most of the last decade

I'd mentioned using Sass mixins as my workaround in my initial post. The compile-to-CSS languages have solved the problem but that doesn't mean it's not a weakness in CSS itself.

Despite having the tools to solve the problem, I'm quite confident very few people have. Virtually all Sass/Less use I've encountered outside the Sass online community has been nested selectors and variables.

> most of Tailwind's fans are people who hadn't found themselves working with these concepts before they picked it up.

Yes, this is the primary advantage for most. Most developers do not have a design system let alone come up with the idea to design something to enforce it. I had to derive mine from Bringhurst's Elements of Typographic style, adapt it to every designer I worked with (they're generally familiar with the overall concept, it just doesn't make it to the popular frontend body of knowledge) and then sell the approach to every team I worked with. I ran into utility CSS much later and then only in online articles until Tailwind.

Since you're familiar with the concepts, you won't encounter a lot of new material here but might find some of the details interesting (I mention some specifics below).

> Or maybe there's something else I don't understand about it yet?

Tailwind's scattered/duplicative nature is a good match for people with the biases matching my own. It's largely competing with inline styles, CSS-in-JS, or scoped CSS. The redundancy of setting styles without relying on the cascade is a feature for component-focused uses where they're desired to stand alone for use in multiple places. The comprehensive (i.e. scattered) nature of the utilities allows for a nothing-but-utilities approach. This avoids class name conflicts and specificity issues, which competing approaches were specifically created to address. Psychologically it also keeps people focused on finding utilities (and thus staying in the design system) instead of hacking in their own CSS.

There's less duplication than I thought at first. Recommended coding style uses component-local cascade for things like font or text color and parent shortcut utilities like space, divide, and gap cut down on the really painful duplication.

I do think Tailwind takes their utilities too far. I think the transition/animation, gradient, and transform utilities are ridiculous even if I do respect the effort. Going the other way, the layout options are underwhelming (grid is anemic) and there really isn't anything around handling images. I consider these relatively minor, nit-picking concerns and simply define custom utilities has solved all the issues I've had here.

In my mind, the more substantive argument against it is an only-utility approach completely tramples on the idea of separating markup from presentation. This leads to a lot of edits and workarounds when trying to use a single component in multiple design contexts, which is a general problem for the component-local approaches. Straightforward css-var things like color are easily handled by making custom utilities with css vars in them. There's also a straightforward general solution in the form of @apply in a named class.

Tailwind is gaining popularity because it's a pretty good fit for the space. My current project is my first professional one on Tailwind but I expect to be using it over my own system going forward simply because it largely does the same thing and is an easier sell.