Goodbye CSS Modules, Hello TailwindCSS
polytomic.com
polytomic.com
There is nothing preventing anybody from using design systems with CSS Modules.
The author makes it sound like you need stuff like Tailwind in order to leverage design systems. This is completely misleading.
> I have used Tailwind UI as a starting point and inspiration for my own components.
There are loads of UI libraries you can use as a starting point & inspiration with CSS modules as well.
The concept of a design system is orthogonal from one's styling solution of choice (CSS modules, Tailwind, CSS-in-JS).
The author certainly knows how to write a blog post and does a good job at selling Tailwind. But in my opinion he doesn't have enough experience to give this kind of opinionated advice to others yet.
It just always feels clunky for me, there's way too many divergent approaches, best practices are constantly moving.
-
I'm designing a web frontend for an embedded project I'm working on and decided on Mithril + Typescript + Tailwind CSS and DaisyUI.
And it's the first time I've felt productive with web tooling.
Tailwind/Daisy UI makes so much more sense than any other styling approach I've ever tried.
I mean, is there a reason I wouldn't want to say an element is a "large red button" rather than a "setup-wizard-login-button" and defining that somewhere else?
Maybe I'm missing something that becomes a problem at a larger scale but for me this utility based approach is great.
> I mean, is there a reason I wouldn't want to say an element is a "large red button" rather than a "setup-wizard-login-button" and defining that somewhere else
I wouldn't create a `setup-wizard-login-button` class. That's getting too granular. In the apps I've worked on there's usually been a `<Button>` component that encapsulates a lot more functionality than just being large and red. It deals with stuff like accessibility, custom disabled states, transitions, etc. It makes sense to encapsulate all of that into one component, then use something like `<Button type='primary'/>` everywhere. In this example, `primary` would map to a design token for a particular style that would handle all the permutations of how that style affects other states, transitions, etc. If you're just applying `large red button`, you're not getting any of that stuff, and I don't see a good reuse pattern for it. So I suppose it depends on how complicated your design system requirements are.
Also, if you have written about your design system implementation with CSS Modules and CSS variables, I'd love to read it.
I don't see how Tailwind would've made our job any easier, or the implementation any "better" than what it is now. So, I'd definitely love to hear a non-vague (most Tailwind CSS praise I find around is very broad strokes or vague) success story.
Like, concrete examples where Tailwind reduced complexity or helped DX-wise, (etc) would be really appreciated. I believe there's many others like me who think Tailwind is kinda cool and we wouldn't mind playing with it in personal projects, but would hesitate to choose it at work/production...
I love the productivity idea its trying to sell me, but it sounds a bit... Wrong to me? Maybe I'm just too "old school", even if I'd like to think I'm keeping up lol.
When I was working through some of the component structures and spacing, the designer explained to me which Tailwind values he used most often and in what scenarios. For example, spacing around sections/containers was 24px or p-6; spacing within sections (like between form inputs) was 12px or p-3; and spacing within elements (like between button icon and text) was 6px or p-1.5.
While these were not absolutes in the redesign, they greatly sped up my scaffolding and allowed my first passes to be either dead-on or close enough to reason about. And when it was close enough (but not dead-on), toggling up or down a value usually settled it to match the mocks.
Personally, this was the easiest time I've had for getting an implementation "pixel perfect." I chalk this up as a design system win more than Tailwind. Any well-defined design system would open up these collaboration benefits. I found Tailwind to be an asset to these both for my own DX and for collaboration with my designer. I think Theme UI or styled-components could fit that goal just as well.
> I chalk this up as a design system win more than Tailwind. Any well-defined design system would open up these collaboration benefits. I found Tailwind to be an asset to these both for my own DX and for collaboration with my designer.
Yeah, this seems like the real takeaway. If you have a framework that gives you defaults for the design system, that reduces the friction even more, but I tend to work on very custom stuff where framework defaults won't be appropriate. Are the Tailwind values customizable, so you can use the same Tailwind structure, but have your designers specify their own tokens?
The Tailwind docs get into it a bit as you can see here: https://tailwindcss.com/docs/configuration#theme
Here is the spacing section in my config where I've extended the default with interstitial values. What's really cool is these propagate to all the spacing utilities (padding, margin, margin between, etc)
spacing: {
0.75: '0.1875rem',
1.25: '0.3125rem',
1.75: '0.4375rem',
2.25: '0.5625rem',
2.75: '0.6875rem',
3.25: '0.8125rem',
4.5: '1.125rem',
4.75: '1.1875rem',
}I have to agree with a sibling comment:
> > I chalk this up as a design system win more than Tailwind. Any well-defined design system would open up these collaboration benefits. I found Tailwind to be an asset to these both for my own DX and for collaboration with my designer.
>
> Yeah, this seems like the real takeaway
And to add:> Our designer was using the Tailwind Figma file for the redesign with most of the defaults in place.
I never really thought of Tailwind as something that provides design primitives designers could also use for their designs.
If everyone on the team knows how to use Tailwind, I can see how it helps productivity. Everyone's using the same primitives and they're speaking the same design "dialect" (for a lack of better word).
Your example with negative/white space units is a really great, because it illustrates how it dispells ambiguity around units and numbers, and it brings an vocabulary for talking about those units. For example, I really like that `p-3` means the same thing to both the designer and the developer now.
Before a designer would have some systems for managing various design sub-systems (ie typography, colour, spacing), that developers rarely (ie never) spend time understanding, which then removed the system's internal consistency, which in turn affects its perceived/external consistency.
Tailwind kinda leaks the design-system's implementation details, which is great for developers, because now they don't have to know anything about design systems, just use what the designer provided.
As for my experience with CSS Modules, I don’t have a blog, but I’ve been thinking of starting one and writing up some of my experiences. Can’t promise anything though.
Tailwind seems to force users to learn its API on top of having to learn CSS, without removing the need to learn the app-specific conventions you’d have to use/create when using Components or classNames anyway.
What benefit does it provide? All I could glean from your post was that you used it and got the job done.
don't get me wrong utility classes have a place in CSS - but they should be introduced as needed.
Writing all your styles with utility classes makes it easy to just sprinkle in changes and flavours directly in your components, but come at the cost of having a mess of class strings everywhere that make it very hard to further maintain the project when it gets more complex.
css-in-js bring about the same pitfalls, all your styles are sprinkled in your templates and get modified and overwritten in places where components are called instead of providing a top down view of how things should look. Responsiveness makes it an even bigger mess and hard to control global aspects of the design.
I strongly believe that decoupled, BEM-type styling systems with a solid variable based configuration, and a well thought out and DRY cascade together with utility classes for specific recurring needs are still superior to just dumping the styles inside each and every component as you go along.
It really fixed a whole slew of my biggest pain-points with CSS. The biggest one being that you have to jump between files when modifying a single UI. Which isn't just annoying while developing, but it's also even more frustrating when refactoring and you have to come back to some files you haven't touched in a long time, trying to cross-reference ID names and styles between the template and the CSS.
It's also just way more concise than typing CSS which helps with the fast iteration. e.g. jumping to a different file and typing
#id-name { margin: auto; }
vs just being in the tag and typing
`class="m-auto"`
Maybe it doesn't seem like that huge of a difference, but I find it saves me so much mental overhead when I'm in the flow of putting together a UI, and I work way faster now. It's also really not that difficult to learn the syntax, which stays pretty consistent throughout all the different CSS properties you can apply. Also the responsive syntax is so much nicer than the typical "@media screen blah blah" syntax is vanilla CSS
On top of this, it's not even just a different way of writing CSS - you inherently end up working in a built-in design system that you can tweak to your liking. For example, the margins go up in units of 0.25rem, it comes with a pretty good built-in color palette (that you can add to/modify in the config), etc. Which keeps you on something of a consistent grid, and is really good for teams where you don't want some new guy coming in using weird sizings or units.
If you don't like the idea, I'd urge you to give it a real shot, even on a small application. Try converting styles on a page to Tailwind. I think it's worth it in the long run.
.MyHeading {color: red; font-weight: bold} // Left container <h1 class=“MyHeading”… // Right container <h1 class=“MyHeading”…
// Left container <MyHeading />
// Right container <MyHeading />
What's the point of a .MyHeading class in this instance? Why are you spending so much time naming things solely to reference them in a separate file?
In my Tailwind projects I can have an entire components/ directory with just .jsx files. No more having to spend time naming one-off elements, or cross-referencing class names with separate definitions in separate files.
Doesn't this make them untargetable if you ever use it somewhere else? Plus, it already has a CSS name: whatever you named the component (and therefore its folder).
UI code should be modular, component-based, and lends itself to being logically grouped into single folders or files:
MyComponent ->index.tsx ->style.css
Or for vue just MyComponent, etc.
Now, this component has all the abilities that we tap into these frameworks for in the first place: it's composable, it's targetable by a known & unique name, we can use it anywhere without restriction.
I don't even want to imagine a world where I render some component and it has no targetable name... Terrible for reuse
What he means by naming one off elements is that within MyComponent there are multiple native elements that may need to be styled.
I also don't think it makes sense to imply the component's creator can know all possible circumstances it will be rendered in and can therefore make an insightful API to control these things. Instead, we can target the fact that CSS is a public API to control styling choices, and expose the classic handle into that API...
Can you say more? For example, if any use case for it exists - which you seem to agree - surely you can see anyone inside such a use case would prefer to have this for free.
For example, I built a nice declarative form using React. I reuse my own Input fields and such. To control their spacing, I target their classNames for some occasions, like "MyLibraryInput" etc.. To be clear, this className belongs to the top level parent of that HTML structure and no styles of the type I described interact with the private HTML of that component, which I am surprised I have to state given that you are responding to a comment stating "I certainly don't think all components can just be slapped into the flow of an arbitrary parent container and have the right positioning and styling for free" and "we can target the fact that CSS is a public API" for single elements because that's quite exactly what it is...
> exposing html class name injection points works fine
So, after all this about how this use case is invalid, you just argue that someone who needs to do this type of thing should have to make extra wiring for no reason? I mean, certainly it functions, but it's a completely unnecessary step to force consumers to go thru. I do not understand this perspective at all.
It's not extra wiring for no reason, it's extra wiring to ensure maintainability. When you style children via css like `#parent .child` you are making it difficult to understand where and why styles are being applied. Any parent at any level of nesting could overwrite the styles of any child, potentially conflicting with other ancestor overwrites. And as the internals of the component change, all these overwrites may stop working correctly. By making the class name injection points explicit, even if it's just the root, you are avoiding this problem of "where did all these styles on my input come from??" and "what is going to break if I change this class name?"
If you use a plugin to sort your class names like Headwind[0] then it can be optimized even further.
0: https://marketplace.visualstudio.com/items?itemName=heybourn...
This is why I still like Vue files over React ones. Vue files can have a <template> tag containing the HTML structure of the component, a <script> tag containing the functionality, and a <style> tag containing the CSS.
With Vue I know exactly where the CSS goes. With Styled components, Im still in JS land where things can be defined wherever whilly-nilly as a template string; imported, passed around, and manipulated. It's a small thing, but those add up.
Using Stylus blocks, I like making things responsive just by defining a media query in a block and then doing like:
.container
flexRowCenter()
+desktop()
flexColumnCenter()I’ve heard this before, and I’m always confused by it. The way I build components is that the component has one co-located scss file that lives right next to the jsx. MyComponent.jsx has a MyComponent.scss. It may import other mixins, functions, or variables using @use, but I usually don’t have to actually navigate to any of those files. So, I’m usually just looking at that one component scss file. How are people structuring their css that they’re dealing with multiple files for a single component? Will one part of the component have a class from one file and another have a class from another file?
The “tailwind” effect is due to fast feedback and iteration - there is no jumping between two files (DOM file and CSS file).
Of course this also leads components eventually having huge lists of classes.
But I think the fast feedback loop is worth it, because with 2D visual artefacts and the complex CSS language, often the only way to learn/get what you want is change a little, reflect, change a little, … etc.
I don't view this as any more difficult than navigating a long file. Maybe I actually view it as more simple upon making that statement. What development environment are you using? Hotkeys to switch tabs is fairly base-level stuff
> often the only way to learn/get what you want is change a little, reflect, change a little
Which surely any person who is arguing for "fast feedback" would do using the browser's dev tools so you can edit CSS and instantly view the result in the same window as your actively painted view...
I would look into getting a proper editor like VSCode, setting up hotkeys, and learning to use dev tools if I was you. It sounds like you are using VIM to make your website
It is also mentally computing the “CSS cascade” to determine the final rules for a given DOM element, and keeping in mind all classes that apply.
The dev tools often cannot save styles back to your source code when you use a build step.
Even if I get my file switch down to 16ms, it is 16ms more than 0.
If I can view the JSX and see all the final styles on the element or on a nearby parent, it results in a faster feedback loop.
I am not for or against Tailwind, as I have mentioned this directness causes a lot of classes on single elements which can be hard to read later, but is often faster to edit whilst you are writing it.
This language you've used is imprecise and suggests jumping between files is a problem. The alternative is jumping from your IDE to Tailwind docs unless you are a tailwind expert no? This sounds like I'd measure it in seconds and not make a facile 16ms estimate so I could write 16ms > 0 and look smart...
> The dev tools often cannot save styles back to your source code when you use a build step.
... 5 seconds ago you were arguing about speed of iteration, now the fastest possible iterator is a problem because you have to manually curate what you save in the end. How is this not a problem when you're just slapping classnames onto something and hoping it looks right? You have to go back thru your save history or to git when you fuck up, again something I'd measure in seconds
> If I can view the JSX and see all the final styles on the element or on a nearby parent, it results in a faster feedback loop
> It is also mentally computing the “CSS cascade” to determine the final rules for a given DOM element, and keeping in mind all classes that apply.
I just feel like you must be really bad at CSS to feel this way. CSS should interact with a component's private structure only in that component's CSS file, it should expose styling & sizing variables thru a CSS var API, and its top-level container's style & position are the only legally targetable things by someone else rendering it. This does not leave any room for having to "keep in mind all classes that apply", that is a drawback only to Tailwind.
Anyways, I don't get why you argue in this fashion just to walk it back at the end as "faster to edit whilst writing" when you discarded the fastest possible solution because of externalities to it that you're unwilling to consider here. For example, loading up a normal component, I simply glance at its CSS file to see all styles applied to it; not so many fancy layers. This means it is very fast to resume writing it. For Tailwind, you clearly need to first build the mental model of all the styles applied and any interactions between them, and then second you need to go to Tailwind docs to see what styles are actually being applied in terms of CSS. It has a layer of misdirection that drastically reduces speed if you don't just sweep it under the carpet. "Dev tools can't autosave for you" is an absurd complaint in the face of brushing all this aside
> The alternative is jumping from your IDE to Tailwind docs unless you are a tailwind expert no
No, as the class names directly map to CSS key/values and the IDE auto-completes the classes. If you know vanilla CSS, you would pick up the Tailwind classes quite quickly.
> "CSS should interact with a component's private structure only"
> "that is a drawback only to Tailwind"
CSS specificity/cascading is part of vanilla CSS.
And what happens to deeply nested components? Are you are using your build step to prevent CSS cascading, giving each of your component classes a UUID?
Instead of using the Chrome dev tools, I am currently using live reload to refresh the page after any HTML/CSS changes.
When you edit your HTML + CSS in the dev tools for fast feedback, are these edits written back to your CSS source files (I assume this is not possible, as they are part of the build process)?
What's your CSS build set up?
> No, as the class names directly map to CSS key/values and the IDE auto-completes the classes
If true, definitely helpful, but I wouldn't necessarily say I agree with the assertion that it is a direct map. Looking at examples, I see craziness like `w-64` (width?) and `flex-none` (why?), and while I admit it is relatively terse it is quite clearly not just CSS.
> CSS specificity/cascading is part of vanilla CSS.
Yes, of course, but sharing CSS classes between disparate elements is the only time the developer has to consider CSS cascading, and so if you simply outlaw the practice you avoid the mental burden & all associated footguns entirely.
My CSS setup is React components styled with SASS. Each component HAS to expose a `className` hook which blends the consumer-provided className with the component. i.e. <MyComponent className="SomeOtherClass" /> ==> <div class="MyComponent SomeOtherClass" />
The styles are private to each component, using only a few shared classes such as flex-* which behave as tailwind classes, but don't really specify anything fancy. Reuse of CSS if necessary is done thru mixins, but I generally don't find much need for CSS reuse.
When I want to change my CSS, I just directly use the browser's dev tools, and then port it back myself. If my component exposes some structure, let's say like a ConfirmationModal, then the the classes are extremely easy to spot: <MyConfirmationModal className="AnotherClass" title={x} onConfirm={someFn} onCancel={someFn} /> ==>
<div class="MyConfirmationModal AnotherClass">
<h3 class="MyConfirmationModal-title">{props.title}</h3>
<div class="MyConfirmationModal-userMessage">{props.userMessage}</div>
<div class="MyConfirmationModal-controls">
<Button text="Confirm" onChange={props.onConfirm} />
<Button text="Cancel" onChange={props.onCancel} />.
</div>
</div>
No shared styles exist. Very easy to reason about. Editing something in dev tools edits something like MyConfirmationModal-userMessage which is going to be very easy to find in code. I usually write it in the browser and "stage" the text into in my CSS file but without saving if I need to modify multiple not-closely-grouped styles, but this isn't really a common flow so I just slap it into dev tools til it looks right then copy-paste into the corresponding block in my SASS structureIf I've got some outer div that's a container for other, inner things, why do I need a name for it? It's much easier to just say how it should look within the context of that component than it is to come up with a name and put the styles for that name in another area of code. I want a 1rem horizontal margin? I set a class of mx-4. No need to name it, no need to jump to another area of code, I just put the styles in the context of where I'm laying out the element.
Can you expand on why you were doing that in the first place?
Your comment reads like CSS just finally clicked for you (e.g. reusing classes instead of specific IDs, and the entire principle of cascading/inheritance) and are attributing that to Tailwind or something.
Other frameworks generally combine multiple properties with their classes.
It is a very different design choice with it's own up and downsides but equating it to bootstrap is just silly.
It's more realistic to compare it to directly setting style attributes on html tags then bootstrap, but that wouldn't let you use responsive modifiers/dark mode nor be as concise.
Reinventing the wheel by subdividing all of your css into individual classes and combining them in html again just doesn't seem like a step forward to me.
Including responsive classes? i.e. make this div X rem on mobile, Y rem on tablets, and Z rem on desktop? With 3 class attributes that require no config?
https://adamwathan.me/css-utility-classes-and-separation-of-...
CSS is a solved problem and that solution is CSS modules with global utility classes compiled to static stylesheets.
That depends on the tool. Libraries like Linaria [1] ("zero-runtime" CSS-in-JS) do generate CSS files.
A lot of helpful strategies like lazy loading components and inlining critical CSS are incompatible with linaria and cause very strange breakage due to changing precedence as components are loaded.
It's an awesome library when it works with what you need, but it's been a compatibility nightmare for us. So many popular tools in the JS ecosystem are a headache to get working with it.
tailwind comes along and surges in popularity. it really goes to show you that marketing makes a difference!
for what it's worth I prefer tachyons because it's much simpler.
You basically generate the kitchen sink in dev and then rip out everything you’re not using when it’s deployed so the actual package size is really tiny.
Utility CSS classes should augment your CSS, not be your entire CSS.
As far as the toolchain goes, meh. Building on PostCSS allowed the Tailwind team a lot of power with minimal complexity and it appears to have paid off for them. The JIT is plenty fast as is now; so fast in fact, that you can now run it in on a CDN build for prototyping/MVP.
https://github.com/tailwindlabs/tailwindcss/releases/tag/v3....
https://github.com/facebook/create-react-app/milestone/81?cl...
If you're picking a framework that will eventually be used by developers who might not have any design skills, which would you prefer?
Tachyons shadows: https://tachyons.io/docs/themes/box-shadow/
Tailwind shadows: https://tailwindcss.com/docs/box-shadow
Tachyons colors: http://tachyons.io/docs/themes/skins/
Tailwind colors: https://tailwindcss.com/docs/customizing-colors
Even something like floats look better in Tailwind documentation despite being the same one line of CSS.
Tachyons floats: http://tachyons.io/docs/layout/floats/
Tailwind floats: https://tailwindcss.com/docs/float
Part of it is marketing and part of it is the novelty of using inline utility classes, but none of this explosive growth happens without a deep focus on design at every stage.
Documentation, defaults, examples, and color palettes all matter.
This is somewhat apples to oranges, but look at Bootstraps' navbar example page versus Tailwind's (paid) examples. One is a cluttered mess, the other is a carefully designed list.
Bootstrap 5 navbars: https://getbootstrap.com/docs/5.1/examples/headers/
TailwindUI navbars: https://tailwindui.com/components/application-ui/navigation/...
I like tachyons looks more than tailwind but you can change tailwind using config to look exactly like tachyons but you cant turn tachyons into looking like tailwind.
So i use tailwind.
although probably making a decision like that based on how the documentation for the framework looks isn't actually reasonable.
How much effort someone puts into their documentation tells me a lot about how much effort they put into the product in general, and user-friendliness in particular.
Single developer projects tend to put more effort into the code than the documentation, but that doesn’t mean the product isn’t good.
Startups can afford to put massive amounts into documentation and marketing while having a terrible product.
And big businesses can be anything.
The Tailwind team is just insanely talented and committed to quality at every level. Not everything is a SV game of marketing and smoke screens.
No. I’m simply saying that the quality of a website does not indicate quality of the product being shown. The only thing that will indicate if a product is good or not is testing it.
Tailwind obviously has great marketing and a great developer base, but not everything is the same.
I couldnt use tachyons in type of work i do but i can tailwind.
I recommend going with Pollen if you really need some kind of framework for CSS. I believe that's the way to really help with CSS development without the burden of learning yet another layer of abstraction over simple CSS.
Tailwind is not for "people who don't know CSS", it's a design system. A value prop of it is for teams who don't have a dedicated designer. Tailwind gives you a set of fixed "design decisions" in the form of utility classes, which is far more flexible than something like Bootstrap, while still offering set constraints (which brings uniformity).
Your idea of CSS is not simple. It's slow, it doesn't scale well, it's difficult to refactor, and it's a higher level of abstraction than Tailwind.
And why is learning CSS mutually exclusive with learning Tailwind? Tailwind is literally just abbreviated CSS properties and some nice tooling.
Everyone who uses CSS uses a methodology, and that methodology comes with its own abstractions. For example, Tailwind is low abstraction; all you need to learn is how the config works and naming, which is relatively intuitive and can be learned in a day or two. After that you're dealing with mostly straight CSS properties.
Most methodologies people actually use when they implement "vanilla CSS" or SASS impose a high level of abstraction—naming, which classes do what, which classes map to which elements, how the DOM hierarchy must be structured, etc. Just because these things aren't necessarily committed to code anywhere (yikes) doesn't mean there's not a complex abstraction layer.
This is the problem.
Writing good CSS is hard, and is it’s own skill that needs to be learned and developed. Abstracting that out so the non css experts can create consistent interfaces without having to ‘learn css’ is the goal.
Agree that if inline styles offered all of the features you get when writing your CSS in a stylesheet that it would be a compelling option because of the simplicity, even though the verbosity would be a big knock against.
https://simonadcock.com/are-inline-styles-faster-than-atomic...
In reality the difference wasn’t so big but inline styles might not scale well. (I also realize the original poster might have been sarcastic but it’s a fun to look into even the non serious ideas).
I feel not enough people have experience with low power devices
Its like
md:p-2 which is for m devices , make padding as 2 Or xl:p-4 , which is for xl devices , make padding as 4
Similarly for hover or focus in tailwind its
bg-white hover:bg-black focus:bg-red Which says , by default keep background of this element white , when you hover over it keep it black and when you focus on it , keep it red
Works just fine, Give it a try and go through the docs of tailwind, navigating it using algolia, is a joy :D
[1] https://github.com/tailwindlabs/tailwindcss/graphs/contribut...
Your project is the reason why i like building web interfaces again.
It creates paralysis and feels like an upfront chore/ cost. Then naturally it’s hard to maintain because you didn’t know what the right abstraction was yet. You literally end up with everything modular (css files) even if it’s only used once.
I’m really enjoying elm-ui and the principles behind it.
(Like in another comment here, I'm rehashing a more extensive blog post [1] of mine here.)
[1] https://vincenttunru.com/why-tailwind/#constraints-set-you-f...
Bootstrap provides pre-made visual components, which are useful for getting stuff built quickly but can be a huge pain to tweak (e.g. you want 99% of the styles but just need the padding to be slightly different in one instance).
Tailwind provides no pre-made visual components, but instead provides a sensible design system to keep things consistent, and either a much more pleasant and scalable way to write CSS, or a horrible class soup, depending on your opinion of utility classes.
You can build bootstrap in tailwind, but you can't build tailwind with bootstrap.
Both are tools to help you design something, but the tradeoffs of each one are quite different.
Well put.
It is always truly satisfying when you land in this spot on past technical decisions.
<div class='text-base font-sans font-medium rounded-lg bg-gray-100 text-black py-3 text-center cursor-pointer'>Button</div>
What happens if I have 20 of these divs and need to modify py-3 to py-4? Can I create a "local" .css file to "group" these tailwind classes ala pseudocode below? .divxyz-default {
@extend text-base;
...
@extend bg-gray-100;
....
}
If so, then the div becomes <div class='divxyz-default'>. A thought came to mind that in that case I have gone back to CSS modules but then we still have some benefits of standardized classes e.g. instead of specifying how much roundness, I am using standard amount of roundness via "rounded-lg" and if I want to change that globally, I can edit in one place. I am probably missing something.https://tailwindcss.com/docs/extracting-components
I find @apply useful for styling HTML that I don't directly control, such as rendered Markdown. Otherwise, I do prefer Tailwind's suggestion to use templates/components instead of custom classes, wherever it makes sense.
const sharedStyles = "..."
and in my component combine the shared with the unique attributes <CoolComponent className={`${sharedStyles} py-3`} />
or using a string combiner utility like classNames/clsx <CoolComponent className={clsx(sharedStyles, "py-3")} />
If you have a props to change on, you can do this <CoolComponent className={clsx(sharedStyles, someProp ? "py-3" : "py-4")} /> const PostImage = styled(Img).attrs({
className: `
duration-100
transform
transition-transform
ease-in
hover:scale-110
`
})``The wonderful thing that it gives you is some natural feeling constraints.
*mostly
Not everyone is working at even 1/10th Google scale. Most aren't working at that scale. The vast majority of sites and web apps can get away with a relatively minimal amount of CSS and modern CSS features in a one or a few concatenated files. For the rest of us, tools like Tailwind, SCSS, etc., are best treated as tools to help us be more efficient, and they are not necessities.
If basic CSS doesn't "scale", then it's time to stop and think about whether one is doing the right thing.
consider this comment an open-invitation for other good css references!
I'll take you up on that! Any good resources for learning tailwind that jibe particularly well with your “learning tool” approach?
[0] https://css-tricks.com/lets-define-exactly-atomic-css/ [1] https://adamwathan.me/css-utility-classes-and-separation-of-... [2] https://acss.io/thinking-in-atomic.html
and finally, it's always useful to read critiques. the following article is well written, in my opinion: https://www.browserlondon.com/blog/2019/06/10/functional-css.... through that i learned that tailwind has a useful feature: @apply, which you can use in conjunction with a standard css class approach. i.e container { @apply color-grey-100; box-sizing: border-box; }
Having a name for each element in your markup is really powerful and useful, and it seems like the Tailwind approach exists specifically to avoid having to name things.
This makes it so you don't have to remember all the names specific to the project (or learn an existing projects css names).
I've never used Tailwind myself so I am not advocating it per say, just explaining how I understand it.
You guessed it right. There is no need for named selectors anymore.
And you get the advantages of standardisation. People can share snippets of code you can reuse as is.
And when you have a problem, you are probably not the first to have it, and can quickly find a solution on youtube, stack overflow, etc.
[1] https://vincenttunru.com/why-tailwind/#the-right-abstraction
I know this is controversial because of the web dev canon of "lol CSS sucks", but many people really love CSS, and find it incredibly powerful and enjoyable to use. If you put in the time to truly learn it, these tools seem silly and redundant.
Otherwise, you're just improvising.
In my experience you tend to end up in a weird thought experiment trying to work out what the third child of the body of a card should be called when actually you just want it to be bold with some padding.
As other people have said, naming things also becomes totally redundant when you’re using something like React or Vue where the abstraction is a component which is itself already named.
[0] https://tailwindcss.com/docs/extracting-components#extractin...
With the old CSS paradigm, I find most of the time you get into a rut where old styles can't be deleted for fear of breaking some obscure part of the experience that development has stagnated on in recent times. Your CSS file continues to grow as new features are added and the old stuff is never reexamined or removed. Who's to say that `P { margin: 0 auto; }` isn't implicitly being used on code being written today?
With Tailwind, all of that ambiguity goes out the door (especially with the JIT becoming the default in v3 at the end of this year). If I remove some classes in my markup, I can be confident the CSS file that is generated is optimized to only include what I need and I'm not saving any old cruft. That alone is a huge boon to confidence and productivity. If I don't have to audit old CSS, that's a win. I don't need to think about old CSS during code reviews, etc etc.
The other part I find most useful is the elimination of generating novel class names, but I will concede that isn't a problem for everyone. Personally, I find that sort of stuff mentally taxing in a way that I don't enjoy.
Finally, I feel like something nobody tends to talk about is that the real power scenario for Tailwind is when it's paired with a framework like React/Vue. The duplication problem is resolved by components that are meaningful to your app, not by arbitrary CSS classes. If I'm repeating the same class over and over again, that's a clue that there is likely a UI component to be extracted. These components also aren't limited to frontend frameworks; nearly every templating language supports partials in some flavor.
I'd be interested to hear your full write up. I believe you've experienced pain, but I wonder if in these conversations were are talking past each other or there is some other external variable that the other party is not aware of.
For example, at my day job, I do a lot of custom enterprise-y WordPress development. Tailwind is a pretty awful experience to work around in the current WP paradigm. I can relate with teams who may have tried to work in a similar scenario.
Tailwind isn't going to solve every problem, but I do think it can provide a lot of structure to teams out of the box with very little configuration, and that is it's primary value add imo.
That said, in that context, I don't see the appropriateness (?) of something such as:
.rounded-lg{ border-radius: .5rem }
It's not abstract enough (for me). That is, if the design system evolves that shouldn't become a code issue (e.g., remove .rounded-lg and replace it with something else). To me all that should be via CSS and only CSS. There sould be -as much as possible - a layer of abstraction between the code and the design system.
Is it just me? What am I mis-thinking? Or does CSS in JS all but eliminate such a possibility? But isn't that what a design system helps to resolve?
Help?? :)
I work at a well-known tech company and we have many projects using the same Tailwind configuration, and an entire components/ file with .jsx components. No need for making up arbitrary class names.
in a design system no reusable classname is abitrary, they should all mean something in your design language. If you find yourself reusing arbitrary classname, then that's just... writing css, not using a design system.
the idea of the design system is that you compose from what in your design system exclusively. This is to prevent ad-hoc slyling that goes outside established patterns. If you are just going to compose generic css then there is no point.
I never had to name any class because I use styled-components. Either the style is part of my design system, then it has a name, the component name, or it's just ad-hoc css, that I don't export. I don't reuse that kind of thing.
The tailwind way is you try to reuse & compose bits of the ad-hoc css by having generic, arbitrary classnames that you don't write but provided by a library
The important concerns to separate are business/application concerns. Putting your JS in .js files, your CSS in .css files, and HTML in .html, it's separation of languages. You could argue that the three languages solve different problems but this isn't true. We have JS writing DOM nodes which HTML does intrinsically, we have CSS and JS doing animations, we have CSS that does styling but also CSS that does layout, HTML nodes that are sometimes there specifically for design or layout concerns. We should be focused on components here, which use the three technologies to solve one business/application problem.
Tailwind can get a little ugly, but you are supposed to use it in conjunction with components. Personally, I don't find 10 inline classes that much harder to read than 10 style rules on 10 lines.
To each their own, of course!
After a week or two w/ Tailwind full-time, the verbose syntax fades into the background and it's a problem in practice. I've been through a few teams who came into it very skeptical but all conceded it wasn't a problem after becoming acclimated to the paradigm.
My uncompiled CSS file is usually less than 50 lines, with the bulk of that being some sort of one-off animation or gradient syntax that Tailwind can't do out of the box.
Because it's never just 'class="bg-blue flex items-center"'. It always becomes 'class="bg-blue flex items-center text-base font-sans font-medium rounded-lg bg-gray-100 text-black py-3 text-center cursor-pointer"'
And now I'm spending 20 minutes digging through every single one of those classes to find the reason my 2 lines of added CSS for this ticket won't apply without an !important.
Does this not raise a red flag?
I know I’m probably a dinosaur using SCSS, BEM and ITCSS but I find my code based logical, tidy and a pleasure to work with.
I do have the tailwind color and spacer presets as variable maps accessible via short functions so I can say color: c(yellow-500); in my SCSS and have all the benefits of standardized colors, spacing, font sizes.
You can use the just in time compiler to generate the classes on the fly rather than importing millions of them in as a style sheet. That’s js, but really it’s just a regex that scrapes things that look like tailwind class names from your html and generates those classes for you.
Writing class="<md:content-center sans font-normal hover:font-semibold pt-[7px]" and having a media query for phones, proper flexbox centering, font face and weight stuff autogenerated without copy pasting and googling snippets is amazing.
And you can reuse the same thing in any project without setting up imports and build steps and being constrained to a specific component framework. No selector specificity/namespace conflicts, just pure WYSIWYG.
I've tried to like TailwindCSS. I tried to use it on a project and I just found it so complicated and messy. People are quick to say, "Oh, give it time, eventually you don't need to consult the documentation as much" which is a strange thing to me, because for normal CSS it's rare I do have to consult MDN to know something (unless it's CSS Grid syntax). If you're new to Tailwind be prepared to waste hours just consulting documentation until "it clicks". You're basically learning a new language on top of CSS. And then the resulting markup in some Tailwind apps I've seen isn't easy to read, it ends up being no different than the randomised classes that CSS Modules gives you.
CSS Modules work perfectly with design systems and other CSS additions. I even use CSS Modules with Bootstrap 5, it works well.
I'm not saying Tailwind is bad, but there is a curve when you use it. It' also feels very boilerplate-y, which I know the React crowd loves, but I don't work with React. Given the amount of work I have on my plate, I would rather stick with my current approach than lose hours in productivity because I want to change how I write my CSS.
If you put all that time into learning the tailwind shorthand then you're fucked on the next project that doesn't use it.
Personally I'd prefer to not have to re-learn how to style things on every damn project just because some noobs that have to pay the learning cost anyway think they've found some 5% efficiency with some shit library.
Tailwind seems like it could be very useful if one's UI is strictly component based. Otherwise it seems like a portal back into writing HTML on Windows 98 using notepad. Either way, thankful for innovation and more options.
Tailwind has amassed 21.8k stars on GitHub and it is being used by 38,749 repositories and has over a 100 contributors, it also has 73,791 weekly downloads on NPM.
Tachyons has 9.7k stars on GitHub, has 64 contributors, and has 26,384 weekly downloads on NPM.
https://sancho.dev/blog/tailwind-and-design-systems/
What I find work for me is using tailwind in addition to css modules. All the utility class with css scoping
No thanks.
Keys in the config file generally correspond with the CSS property they refer to, and we use the singular form to make it match the CSS property.
There are 3 exceptions, which are "colors", "spacing", and "screens", and these are sort of special keys that don't map to anything in CSS. The "colors" and "spacing" keys are more like reusable variables that are consumed by the property-specific keys like "backgroundColor", "fill", and "borderColor" in the case of "colors", and "width", "height", "gap", and others in the case of "spacing".
The "screens" key is a list of breakpoints for your project, which also doesn't map to an underlying CSS property.
Originally we tried to keep things really abstract and group things together under concepts like "spacing", "typography", whatever, but ultimately found it was more flexible and predictable to make it possible to customize every CSS property using the exact property name, while only providing a couple high-level things like "spacing" and "colors" that you can use to update other dependent keys at once.
Getting complex config files right is unfortunately hard and it's not perfect for sure. At this stage in the project it's a delicate balance between improving things and maintaining backwards compatibility. Totally appreciate what you're saying, just wanted to do my best to clarify though as I'm equally picky about this sort of consistency.
I appreciate all the work that goes into something this large, but that just makes me more sad that this jumps out at me so quickly when I look at the examples. This kind of thing made PHP a laughing stock for many, many years before they finally bit the bullet and standardized their function parameter order, etc. And I say that as someone who does PHP for their day job by choice. (Among other languages.)
What would you do if backwards compatibility were out the window?
When applying them later in the code, they're both singular since they're specifying a specific family/color.
I agree with GP that this inconsistency shakes my confidence in the package.
>But, preventing global CSS removes the expressive power of the “cascading” part of Cascading Style Sheets (CSS).
lol wat? variables exist and import exists if you wanted to stick with style.module.css. StyledComponents works very nicely for this as well.
<div class='text-base font-sans font-medium rounded-lg bg-gray-100 text-black py-3 text-center cursor-pointer'>Button</div>
The above line of markup looks like utter crap tbh, not to mention, the fact that it's a div vs the name of what you consider it to be in the context of the other objects on the page seems like you're obfuscating its role. I just don't think it's a good thing to use unless you're doing a bunch of one offs that you'll never have to support.Maybe one week isn't long enough. I dunno.
Over my career I started by using global CSS files, transitioned to CSS modules, then to styled-components. I never touched Bootstrap because it was clunky, ugly, and annoying.
Creating layouts and styling everything consistently has always been a pain in the ass. There'd always be some element that I decided to style differently, or break out of the "design pattern" just because I needed a one-off or it didn't fit well. On some projects I spent the majority of my time trying to organize styles so that I could remain consistent and handle edge-cases "well enough" that everything was still clear and legible.
The first time I saw someone using Tailwind I immediately thought "ffs not this Bootstrap garbage again", but I decided to roll with it because it wasn't my project. Since then I've been using Tailwind for all of my projects.
When you combine Tailwind with React, you get nudged into creating a library of components with default styles (ie. a design library). If you need to change the styles for certain instances of the components, you extend the component itself and apply styles according to flags (using classnames utility). If you need more control in certain situations, you use inline styles.
What this means is that Tailwind actually fixed my problems with styling consistency and organization, and made me better at writing re-usable React components.
Yes, the className prop can get long, but you can use the classnames utility to break it up into separate lines (and organize the classes however you want). Testing different styles for components becomes a lot easier too once you memorize the class name patterns that Tailwind uses. It quickly becomes legible and you can usually guess what the class name is for whatever style you want to add.
Still, I hope Tailwind can improve on this somehow to remove the remaining issues (legibility for beginners, and the long-ass className prop)
JS (mycomponent.js):
export function MyComponent() {
return <div styleset={__DIR__ + "components.css#my-component"}>
<header>Hi!</header>
...
</div>;
}
CSS (components.css) : @set my-component {
:root { background:red; } /* the component itself */
:root > header { background:gold; } /* its content */
:root > footer { background:var(footerColor,#0F0); } /* use of vars that can be set in JS too */
}