While semantic styling has been around for quite a while in different shapes and forms, this is essentially the same as inlining all of your styles in a style attribute on every element, albeit just a bit shorter to write.
It's an interesting idea to entertain as a thought, and some utility classes certainly hold value in general, but I don't see how you would ever reasonably want to use this in a project. It has all the same drawbacks as writing inline styles on elements, not to mention the large size.
Say you have a number of views and decide (or a client decides) you want all of the buttons to have a bit more padding. If anything else happens to completely accidentally also use the same padding class you're always left with manually checking every single instance just for a small change like this.
The first thought everyone has when seeing it is what you described, including myself.
I suggest trying it out & reading more about it. It's the only way utility CSS frameworks like this seem to click.
In my opinion, Tailwind works best when being used to create template components. In your button case you would just modify your reusable button component or the reusable base button component if you have multiple button components.
You get the best of all worlds.
* You avoid writing a lot of CSS.
* You don't have to worry about thinking of CSS class names or name pollution
* I find it faster & easier to style components with a utility CSS framework in something like Storybook than it is with CSS.
* I recommend adding a plugin to your editor to help with the names of Tailwinds classes. VS Code has a couple that will suggest the right class name based on what you type.
You get all of those benefits without any of the downsides by using any other modern styling solution, for example scoped styles and scss. The arguments against this solution far outweigh the arguments for it. You're free to disagree, of course.
Scoped styles and SCSS do not prevent you from having to come up with unnecessary names for things like utilities do. That's the main benefit in my opinion. Wasting brain cycles to name arbitrary wrapper divs that only exist to be a target for layout styles things like ".image-card-content-wrapper" is a completely unnecessary step in the UI development process.
Using utility classes to build a UI is much more like building something with SwiftUI, where no one seems to argue about this silly "separation of concerns" thing we talk about on the web.
Using SCSS and using some utility classes isn't in any way mutually exclusive. If anything, it's a lot easier to mix and match. The argument isn't against having utility classes/mixins/extensions in general, but against relying on them solely.
I'm still not convinced — using only utility classes has very clear downsides, while using what's reasonably one of the most common approaches in the industry right now avoids those downsides while also providing additional benefits.
I don't think you can "only use utility classes". I think they cover 90%+ of your CSS though. I apologize if that came across as the argument I was making.
Yes, you probably will need to create some custom classes from time to time. Ideally those might be done in a utility fashion though.
TailwindCSS can be abused to resemble inline styles, but part of TailwindCSS is the fact that you have some sort of design system you can quickly set up and build upon. For example, I could define a color called "primary" and use it anywhere without caring what the actual color is.
Furthermore, styled buttons are such a common occurrence that TailwindCSS encourages users to extract common CSS patterns into component classes[0].
Well, you do write CSS classes, but those classes are based on your configuration. This is not a reinvention, it's a framework addressing certain issues in a certain way. It might seem counterintuitive at first, but to me it's a tool. I prefer it over SCSS (since I often rebuild in SCSS what TailwindCSS provides)
Inline styles (AFAIK) cannot be made responsive, are a pain to override given their high precedence, and do not provide any constraints—Tailwind is a design system too.
From a size perspective, compression takes care of the repetition pretty well. Utility classes allow for better reuse, meaning the overall page often is, in fact, lighter.
Nowadays most pages are put together using some kind of components anyway, so changes are that your button is already defined in one place. Otherwise you can extract your own button class.
Even if compression is the solution for most users, I would love to have a generator on the web site where I can click on "blue" and "orange" (because that's my color scheme and my site will never use teal), not click on "forms", "buttons" and "hover", and I get a quasi-minimal version of the CSS file.
My smallish sites don't use build pipelines and post-generators and so on, I'd just like a reasonably-sized CSS file to include.
(I realize that's a niche use of those frameworks)
I’ve actually been really enjoying using Hugo with PostCSS for static sites. You can run PurgeCSS automatically and have it remove all the unused crap from a CSS framework. Suddenly, it’s only 40KB for a full design system.
I think you have misunderstood the implementation. If you want all buttons to have a bit more padding then you either change the padding class on each button from .p-1 to .p-2 or, more likely with something like buttons, you abstract the styles into a .button class and then you can make a singular change to the padding and it will affect all your buttons.
The first one is a find and replace nightmare on any sufficiently large project and akin to inline styling. The second negates the point of utility classes.
No, it does not. They're not exclusive. I've written large pages that pretty much only used component classes for pills and buttons, with Tailwind using @apply. All the other elements used utilities. Paired with purge-css you get small CSS bundles and really quick styling.
You'd often use something like a component based JS framework or a server rendered templating language, making it easy to contain all utilities with shared logic in one place.
I’ve seen this issue at many “engineering” focused companies where many people write their own DSLs. for CSS in particular there are many people who just hate it and come up with many ways to not write it.
CSS however is very much like drawing rather than engineering. Some aspects are just not scalable. You can draw many things in vector by conbining shapes for example but a pen or pencil is still very necessary for many parts of both exploratory creation as well as something polished.
The same goes for HTML and Javascript to an extent. While it’s great to build railroad tracks, you can’t take a train everywhere... any abstraction has to also have the ability to easily break out of it to handle edge cases. Clean and Pure abstractions almost never have the grittiness necessary to reflect a fractally complex problem.
From a performance point-of-view, it's not the same at all. Also this is a design system, not just a set of utility functions. You can also abstract common components and reuse them.
I'm absolutely loving using Tailwind for my new projects. It makes it so easy to prototype and build out nice designs. Honestly I have never felt so productive when it comes to designing pages.
There is however one thing that prevents me from using it for everything and I'm curious if anyone knows a solution for this. If I am pulling a chunk of rich text from a CMS/backend/whatever, how can I style this?
I tend to use Django for my backend, so in my template files I just put in a chunk like <div class="container">{{ body }}</div>. If that chunk has h2,p, etc. none of those are getting styled.
In normal css I could just define what those h2, p, etc look like under a specific class. But besides using some js magic to inject those classes, I'm at a loss.
I solved this issue for myself last night using CSS selectors. My goal was to replicate Github Flavored Markdown styling using Tailwind on Hugo generated html. Here is the repo for the stylesheet for anyone who is interested: https://github.com/iandinwoodie/github-markdown-tailwindcss
The generated content is wrapped in a div with the class "markdown" (defined in the repo above). So it looks like:
<div class="markdown">{{ .Content }}</div>
I wrapped up the stylesheet around 2AM this morning, but I plan on expanding the README and un-nesting the class (it was nested for postcss-nesting).
I am just starting a personal site and it is still very much under construction, but I do have a Markdown example page publicly available that I have been using to test the styles. Here is the link in case anyone wants to take a look at the end result: https://iandinwoodie.github.io/blog/markdown-syntax-guide/
There is no good way to solve it using Tailwind classes alone. You could parse the markup first and add Tailwind classes before rendering them, but that is too much pain for very little reward.
The best trade-off I've found is to give a unique CSS classname for the container which has user-generated content, and then style it using nested selectors. Here you can use Tailwind's @apply so you're still able to rely on Tailwind's design system.
Do you have any tutorial on how to set up Django with tailwindcss (using Django templates)? I'm new to Django and somewhat confused when it comes to integrating modern front end tools.
I've used it and it is easy to use. It will generate a base.html file for you which pulls the built css from the static directory and you can extend that in your templates.
I made this TailwindCSS Playground over the last two weeks. I really like the core idea of Tailwind and the productivity gains it gives and would like to build more stuff with it in the future!
Features:
* Compile custom @tailwindcss config and CSS in the browser
* Live preview in 5 screen sizes
* Class name autocompletion with CSS definition preview
Adam Wathan (creator of Tailwind CSS) is working on an excellent screencast series about using Tailwind: https://tailwindcss.com/screencasts. I highly recommend watching it.
I don't understand frameworks like this. You are basically tying presentation with markup. God forbid you want to create more than one theme for the same HTML!
It is very liberating to use Tailwind instead of creating unique CSS classes for every div in a page. The code is much more maintainable - in a single stroke Tailwind gets rid of selector specificity issues, design inconsistency, and naming woes.
From a programming perspective, the conventional style of "semantic" CSS classes creates deep and fragile coupling between markup and CSS. It goes against everything good code is supposed to be - when you change the structure of the HTML you're forced to change the CSS, and if you truly use the power of CSS selectors, then modifying CSS becomes a difficult enterprise: one change can create multiple unrelated changes at a distance.
Tailwind solves all this, with just two downsides: until you spend a few hours mucking with it, the markup look will look unwieldy, and you have to be ready to really think about semantic CSS and "separation of concerns" dogma we've taken for granted for so long.
Bit of a tangent, but perhaps the issue with separation of concerns isn't so much that it's a bad idea, it's that it's not always a good idea; and secondly that css doesn't really enable separation of concerns like that. It's a fine idea to be able to give (essentially) a style a meaningful name, but only if you know what the meaning is and probably only if you intend to use it several times. Since CSS cannot really create complex styles from scratch meaning that you need wrappers and separators and whatnot as purely style elements, true encapsulation that's easy to use without knowing the implementation isn't really feasible.
Hence "separation of concerns" - to the extent that it's a good idea in the first place - doesn't work for html+css, and similar concerns mean it doesn't work very well for html+scripts.
Put another way, leaky abstractions are worse than no abstractions. Better to keep things transparent and merely expose helpers, instead of pretending you can hide complexity when you cannot really hide it.
So, if I'm making a theme, and I want a different color for the text, I have to replace "text-gray-900" with my hex code for purple?
Also, what if my theme is about making the design more compact, or increasing density? How do I customize thousands of elements with different combinations of class="ml-6 pt-1"?
Oh, and don't get me started on "bg-white rounded-lg". God forbid your theme wants squared boxes on black background.
Don't use `text-gray` if you're building an app that needs to be themed like that. Instead use `text-secondary` or a similar name that you'd otherwise use in a themeable system. Tailwind lets you define _every_ single design token in the system and comes with zero opinions there.
If you want to change density of the design, you can change the spacings centrally in the configuration itself. But if you need a non-proportional change, then it isn't any different than BEM+SASS environment where you'd have to go into every CSS class and make the change. I think it is even better than BEM here because you only need to change classnames in HTML, which is co-located with the style, and it doesn't cause cascading changes due to specificity etc.
To me the opinions you hold seem to come from not having tried it in good faith, which was how I used to judge functional CSS as well. If the current CSS style you use serves you well, that's fine you don't have to explore. But if you've wondered whether there is something wrong with the way we do CSS today, then Tailwind could be an eye-opener.
Imagine it more from the perspective of a component altering it's style based on some sort of global "state". That global state could simply be a class on the body like "page-contact", so your css can alter itself much easier if the style isn't defined up front in a list of prescriptive class names.
It's less theming in the sense of css zen garden and more encoding variations of style in css instead of relying on more class names.
This comes up in every thread regarding Tailwind or similar frameworks/methodologies. If you're interested in the rationale, I recommend searching for some of those and reading the comments there.
The TLDR is that you don't lose anything by starting out this way, because at any point you can extract (think @apply) higher level utilities or semantic classes, but gain that you can get a good looking design in place quickly by having useful constraints, not needing to switch context all the time, and not being forced to make up names for everything. While few sites need to conform to the "Zen Garden principle", Tailwind won't stop them from doing so, if you put in the work that it makes optional.
I've found that since using Tailwind I've got a deeper understanding of CSS, got a greater understanding of UI design (Adam Wathan's tutorials are great) and produce and replicate designs quicker and easier.
It's a design system with constraints, which automatically gives me fewer and better options.
Using something like Bootstrap is probably easier and quicker if you're unfamiliar with writing CSS though.
All in all, quite far from styling with inline `style` (but it takes a bit of learning on the thought process behind it).
Could you elaborate a bit more? I understand how something like this would help with understanding UI design as it abstracts away the unnecessary details of CSS. However without digging into what these classes actually do I don't understand how it helps with understanding of the actual CSS.
Awesome project. I am a big fan of Tailwind CSS to and I use it for my current project [1]. I will definitely make use of Tailwind.run() to as a playground to design components! Great job!
Your project is a bit similar to mine and I am very curious about how you use Blockstack.
I have been hosting a CouchDB instance myself on a VPN but considered moving to Firebase while prototyping since it seems easier to use. Considering Firebase since it offers offline-first support. However, only today I was made aware of Blockstack and I have seen it mentioned multiple places.
How do you like working with Blockstack and what made you choose it? Does your app work offline? Does it synchronize automatically between devices?
I also want to take data privacy very seriously, since I am dealing with personal and sensitive data (just like a person's journal is very sensitive to them).
Cool! I've been wanting to try a framework like Tailwind, but I get the feeling I'd like something like Atomic CSS [1] better. It generates classes based on what you use, as opposed to Tailwind, which supplies thousands of classes that you may need and leaves it up to you to strip out the ones you don't use (I think?). I dunno, it just seems like Tailwind has it backwards to me, but am I misunderstanding something? Does anyone have experience with both (or some other micro class framework) who can tell me how they compare?
I have no experience with Atomic CSS, but I think an important part of Tailwind is that it simultaneously serves as a design system. By having relatively limited options for, let's say, font sizes, you don't spend a lot of time making micro adjustments. The author of Tailwind also co-authored the Refactoring UI book that pretty much explains this philosophy that the framework was built to support. Of course, in the end you can add as much custom CSS as you want, written as utilities or not, but the point is to quickly get to a reasonable design.
You have to strike a balance between reusability + a central base of the design/theme which defines the consistent look and feel on one hand with the composable LEGO style building blocks which Basscss https://basscss.com/ and Tachyons http://tachyons.io/ and other similar functional css frameworks made popular.
The problem with the latter is you’ve coupled the styles to names embedded in the HTML which means a whole lot of grep and replace when things change.
Taking that further with actual custom values being applied in the class names in the view the way Atomic sounds like it’s taking that idea too far. Those values should be constrained the way it’s done in https://rebassjs.org/ where customization of base values is handled with CSS in JS.
Ultimately I’ve found Tailwind to have the best balance between a composable utility class base approach, with consistent base values, and a mature and good looking parent framework. Otherwise I’d go with one of the smaller utility kits mentioned above for simple projects.
We use PurgeCSS[0] as part of our build process to remove all of the Tailwind classes that weren't used. That gives our developers the freedom to work/experiment with everything Tailwind has to offer while spitting out super tiny and optimized CSS files.
I just checked one of our more recent projects and the resulting CSS is 2.4KB, about half of which is actually an SVG that's used in a background.
> We have been programming CSS for a long time wrong.
Have we though? I firmly disagree with the premise. I agree that like anything else, css can be abused and misused, and the example cited of Newspapers fits the narrative perfectly. I've worked at a major regional Newspaper in the past and their website was one of the worst sites I've seen. The lead developer at that time (10 years ago) switched to using a functional/semantic css pattern much like tailwind/UCSS, and I found it to be terrible.
Any experienced FE developer should and will know css, whereas they will not know some random css framework dsl, nor should they have to learn it.
Most modern js frameworks are moving towards web-components and scope css to a component, which eliminates much of the past structural ambiguity and reasons for seeing e.g. !important (if implemented properly).
The argument about file size is not strong imo. You are trading css file size for html file size. How many modern web apps are slow due to their css file size? I doubt many. The Newspaper websites were always slow due to churn & many people touching the code, often with lower experience, leading to spaghetti.
If it works for a team then more power to them, but I'm sticking with css :)
Hi, yes, the premise is a bit sensationalist. But any FE experienced will not work for too long on the projects. The projects I've surveyed and work for, changed personnel all the time. I didn't choose the projects to fit my narrative, I did it because most projects die within 5-10 years. Few projects live longer than that, and newspapers are great example for the research. The codebase is really hard to maintain as business grows. That's why I firmly believe, it is not the developer, it's the metodologies they use: they force you to do technical debt, bad arquitectures, etc.
Of course functional CSS is not the solution to all, there are situations where, componetize everything will make more sense. But the more you work on, the more useful you see it becomes.
The file size is strong for one reason: most connections to CSS resources are blocked by the queue of priorities, HTML files are downloaded between 12ms-100ms range and that means it's already done, plus, if you use reactive HTML, things speed up even more. Most of the CSS made with BEM or other metodologies don't end up with that much HTML saving too, they're quite verbose. You cannot control the verbosity since it's human made. With UCSS or other funcional frameworks, you can control the verbosity of the outputs, etc.
No we've been trying to turn CSS into programming for too long and doing ridiculous things that other domains wouldn't tolerate like encoding structural annotations in string names (BEM) and endlessly trying to genericise things that are similar only by coincidence or forced coercion which is the obsession of functional CSS "frameworks"
One quick extra note about the argument of file size. Those who argument about "more html size" never thought people has to download a huge CSS file for 2 o 3 pageviews. With the current state of the web, the trade off isn't that painful, in fact, you will see that projects done with React, Vue or similar the trade off is a bargain you cannot avoid.
HTML is faster, by default, as I state in the article. It's the first document and it's the first one processed. If you have a CSS file of 10kb gziped your chances will be huge in comparison with the averaga ~160kb of some projects.
The problem with inline-css (how the modern js framework are handling) is that clashes with the responsive world and cannot be preprocessed so easily as other projects.
Besides the file size factor, give a check to the other points in the arguments: amount of rules slows the loading speed, rendering and painting, etc.
One question about this approach: If I understand correctly, if I have some text that is bold, and later I want to change it into italic, I have to go into templates, locate all instances of the text and rename all the classes from font-weight--bold to font-style--italic? Seems like "quick to build, but hard to maintain" approach?
The approach is like programing with inline-css but using classes. Once you start working, you will realize you will reuse more classes and your code will be 100% redeable. Anyone coming behind your work can easily work it. Otherwise he needs to re-learn all the architectural tidbits and change it to his way to architecture it.
This doesn't stop you to code small and repetitive components like "buttons". There's an article where you can see how it works.
Well, I assume is a component. Can be a layout or a partial, you go there, read the classes, make your changes according to the new design. In case you need extra clases, let's say, you need to introduce something like pointer-events, you go to the framework, you activate it in the config file, then use the class on the div. In case you need extra customization, you write a custom class ex. c-10 and you apply interactions needed (:hover, :focus, etc.).
I played around with tailwind on a small project and found it more difficult than I expected. I think part of the problem is that when using tailwind or other similar frameworks, the conceptual model and workflow is different and requires some cognitive overhead to use effectively and efficiently.
Perhaps one day I will try again but personally I want to minimize extra stuff to learn, especially related to css and styling.
Awesome, might just give Tailwind a shot finally. Thanks for the great work. I typically use https://basscss.com/, but been meaning to checkout larger Atomic CSS frameworks like Tailwind and Tachyons.
Aside from the benefits of "functional CSS" that others have mentioned, one thing I really love about it is I don't have to constantly be looking up CSS rules or properties; I just reference the docs of whatever framework I'm using to get what I want. You might say I could do this with Bootstrap as well -- and yeah, I can, but I much prefer the piece-meal approach of atomic CSS. The times I've used Bootstrap, I found myself fighting their styles constantly, or giving up and resulting with a site that looks like every other Bootstrap page.
CSS has one major drawback - that is, there is no way to compose more complex objects from basic ones.
Say, I have a class:
foo {
color: #FF0000;
}
I want to now apply this class to another class:
bar(foo) {
background-color: #000000;
}
I want to be able to inherit from foo, but I can't do that and that's why we have these shenanigans of composable UI frameworks... there are some advantages to doing everything in the HTML but now we have repeating patterns in the HTML, such as in the OP's link
which is not extensible and creates duplicate code. You'd still have duplicate class names but I just have to change it once in the css instead of N different places in HTML.
This is awesome! I started using Tailwind for a project. I was a concern with the relatively big size of the framework on a landing page[1]. Then I used purgeCss to only leave the css classes that I used and remove all others. That solved the size issue.
Is there any purgecss equivalent, that is a local tool and is not node.js? I've recently searched for that and only found a defunct Ruby gem. Does the Golang world have nothing like that?
Because it's yet another full ecosystem with lots of dependencies where I have zero knowledge and experience.
For example, all those packages say "npm install packagename" to install, but it didn't work for me. It seems I have to (sometimes?) do a "npm init" first. No install instructions ever tell you that.
I'm sure when you're in the ecosystem, that's a given. For me it's hurdle after hurdle.
That's why I named Go and Ruby as examples. The former because you usually get a standalone executable, the latter because I already have that toolchain installed, and I know how to navigate it.
Unfortunately, everything in webdev is now node.js. Just look at any CSS framework docs (including Tailwinds, which I really like): it's an endless stream of pre-processors and post-processors, multiplied with all kinds of ES versions, Babel, yarn-or-npm, this build tool or that. It just doesn't end.
I've been using NodeJS for about 5 years and have never seen the init usage you're talking about. `npm init` initialises your new project. At least I think that's the case, because I've never run that command. I just create a package.json file myself. Also, I use yarn instead of npm. But of course, because change, there is now a new way to use init that you just alerted me to. It sure seems like an unnecessary complication. I recommend using yarn, by the way.
I read the first part of your comment and thought "okay, I expected that I probably misunderstood the init thing".
Nothing to feel bad about, as I said, I'm an outsider, but still annoying to get it wrong in my comment.
And then I read the second part and am simply astonished. I wasn't wrong? I just happened to stumble upon a quirk that changed recently? That's something.
I think the trick to using the NodeJS/webdev ecosystem is to ignore most of what goes on and to keep one ear barely listening in case something truly useful comes along.
...and update packages with caution. SemVer doesn't always save you and neither will static analysis (because there really isn't any).
Yes you can use a configuration file to build it (we integrated it into webpack). The config allows to disable features completely or only include certain versions of them (hover, responsive, focus, ...)
That are some bloated classes. it's no different in inline styling everything to me. I really don't see the benefit in doing it this way. Can you use tailwinds and scss to create actual styling rules? No that is something I could get behind.
Been really productive using Tailwind myself. Recently re-launched my SideProjectors site (https://www.sideprojectors.com) and was able to get my CSS developed very fast. I have most of Tailwind classes in my head now, so I hardly look up their site for references. Being able to quickly edit the UI without much thinking is very refreshing.
I recently tried learning Tachyons, I couldn't get the hang of it, until I was on the train with no internet and I only had an example blog site loaded, and the table of styles. I was able to put together my new website theme in an hour.
"A utility-first CSS framework for rapidly building custom designs."
It's an excellent description in of it's own, but if I were to attempt to put it in other terms, it allows you to compose css components and classes out of other smaller, essentially one liner css classes, while maintaining a consistent styling and spacing across the different utilities, which you define up front using the config but also comes with good defaults out of the box.
It is excellent and now my preferred method for hacking up a new site. It also helps you think about your design system overall so now I find myself paying attention to things up front I would normally wouldn't and it ends up saving me time in the long run. It also pairs really well with the excellent Refactoring UI book.
It's an interesting idea to entertain as a thought, and some utility classes certainly hold value in general, but I don't see how you would ever reasonably want to use this in a project. It has all the same drawbacks as writing inline styles on elements, not to mention the large size.
Say you have a number of views and decide (or a client decides) you want all of the buttons to have a bit more padding. If anything else happens to completely accidentally also use the same padding class you're always left with manually checking every single instance just for a small change like this.