A core part of Tailwind is that you reuse styles by using a templating system vs using custom CSS classes e.g. you have a button.html file that contains the styling for your buttons for reuse, so you don't have to keep repeating the same utility classes everywhere.
https://v2.tailwindcss.com/docs/extracting-components#extrac...
> It’s very rare that all of the information needed to define a UI component can live entirely in CSS — there’s almost always some important corresponding HTML structure you need to use as well.
> For this reason, it’s often better to extract reusable pieces of your UI into template partials or JavaScript components instead of writing custom CSS classes.
@apply (e.g. .btn { @apply py-2 px-4 bg-indigo-500 text-white }) is only meant for reusing styles for simple things like a single tag and is generally recommended against because you should use templates instead:
https://v2.tailwindcss.com/docs/extracting-components#extrac...
> For small components like buttons and form elements, creating a template partial or JavaScript component can often feel too heavy compared to a simple CSS class.
> In these situations, you can use Tailwind’s @apply directive to easily extract common utility patterns to CSS component classes.
Inline styles can't be responsive and can't target hover/focus states e.g. there's no inline way to write "text-black hover:text-blue py-2 md:py-4 lg:py-5 lg:flex lg:items-center" and the CSS equivalent is very verbose.
https://v2.tailwindcss.com/docs/utility-first#why-not-just-u...
> But using utility classes has a few important advantages over inline styles:
> Designing with constraints. Using inline styles, every value is a magic number. With utilities, you’re choosing styles from a predefined design system, which makes it much easier to build visually consistent UIs.
> Responsive design. You can’t use media queries in inline styles, but you can use Tailwind’s responsive utilities to build fully responsive interfaces easily.
> Hover, focus, and other states. Inline styles can’t target states like hover or focus, but Tailwind’s state variants make it easy to style those states with utility classes.
Opinion: As utility classes are quick to type and change, and it's easy to find where you need to edit to change the styles of a component, it's an order of magnitude quicker to iterate on designs compared to CSS that's scattered across files and shared between pages in hard to track ways. CSS specificity and cascading aren't often used, and mostly just cause complexity and headaches so are best avoided. Tailwind-style vs classic CSS is similar to composition vs inheritance in OOP with similar pros/cons, where complex inheritance is generally discouraged now in OOP languages. Yes, the Tailwind approach is going against the standard CSS approach, but CSS wasn't initially designed for highly custom responsive designs so it's not surprising if its "best practices" don't fit everywhere.
Also, Tailwind is really for custom responsive UI and website designs. If your site is mostly Markdown documents and you don't need a custom design or complex mobile/desktop styling, the above isn't going to make any sense and plain CSS or something like Bootstrap is likely a better choice.
But the moment you need to style real html and not some kind of component structure - you have to look for something else.
If I switch to "old man yelling at the sky" mode I would say that's an example of a nice concept (utility classes) taken way too far.
Like GP said, this kind of specificity is impossible in inline styles altogether which is relevant when not using CSS-in-JS in React et al.
Were people complaining it was too slow? Were the performance improvements just the benefit of refactoring they did for other reasons?
Considering the build happens once during your build phase, taking under half a second doesn't seem like something I would even bother looking at. So it just jumps out to me, so I'm just curious.
[1] https://tailwindcss.com/docs/v4-beta#new-high-performance-en...
Also, browsers are insanely good at parsing and applying CSS. You need hundreds of thousands of unique selectors before the browser takes more than fraction of a second to parse and render an entire CSS file.
https://www.trysmudford.com/blog/i-spent-a-day-making-the-we...
Bret Victor's Inventing on Principle is probably the best demonstration of why this matters; in the first 10 minutes he shows a very concrete example of an insight that would not have occurred without instant feedback https://www.youtube.com/watch?v=EGqwXt90ZqA
When we're talking about ms the build is not the thing that is affecting your ability to try new things out. It was already so fast it was near instant.
2. Watch Inventing on Principle if you haven't already. Pushing build times into milliseconds or sub-milliseconds enables new capabilities
That’s really interesting to think about actually! What kinds of practical things do you think could be enabled by an extremely fast tailwindcss?
Moving from pure JS to Rust also brings performance gains. Even if the main goal wasn't "performance," it's a nice side effect worth highlighting.
--
First time I heard of OKLCH tbh. Anyone know if that is part of a wider adoption trend or is Tailwind pioneering here?
Looking at the examples it does seem to offer some advantages; but was primarily surprised that they now use it as a default.
[1] https://evilmartians.com/chronicles/oklch-in-css-why-quit-rg...
https://abhisaha.com/blog/interactive-post-oklch-color-space...
It's part of a wider adoption trend. https://bottosson.github.io/posts/oklab/#oklab-implementatio...
OKLCH just basically exists because there is a hue change from blue to purple in LCH when the lightness goes less which does not match how humans think of these colors (supposedly, don't know if there is any cultural difference)
So in OKLCH the lighter blue does not look purple like it does in LCH, it looks like lighter blue.
The reason to use OKLch is so that when using CSS color mixing (via animations, gradients, etc), they look "better", as indicated by the sibling comments.
A good friend maintains a tool to tinker with various shades/tint of colors with OKLCH https://colorcolor.in
There are three things to consider here:
1. Colour interpolation, as used in things like linear-gradient() and color-mix(). The default of sRGB is not particularly good; choosing Oklab or Oklch instead will normally improve things. Oklch is an okay default these days. I reckon that for most applications, Oklab is better. Oklch tends to be too vibrant in the most extreme cases like #ff0000 → #0000ff. Here’s a simple demo you can play with:
data:text/html,<style>div{width:20em;height:2em;display:flex;align-items:center;justify-content:center;background:linear-gradient(in var(--space) to right,var(--from),var(--to));}</style><input type=color value=%23ff0000 onchange=document.body.style.setProperty("--from",this.value)><input type=color value=%230000ff onchange=document.body.style.setProperty("--to",this.value)><script>document.querySelectorAll("input").forEach(e=>e.onchange())</script><div style="--space:srgb">sRGB</div><div style="--space:oklab">Oklab</div><div style="--space:oklch">Oklch</div></body>
2. Specifying colours that are beyond the sRGB gamut. This is what they say in https://tailwindcss.com/docs/v4-beta#modernized-p3-color-pal...: “taking advantage of the wider gamut to make the colors more vivid in places where we were previously limited by the sRGB color space”. Frankly, this is seldom actually advisable. For most purposes, sRGB is quite enough, thank you: go beyond it, and your colours will be too vivid. In photographs it makes sense, but for colour palettes used with blocks of colours and such, it’s normally a bad idea. Yet if you are trying to go beyond sRGB, meh, nothing wrong with writing it in Oklch. Though color(display-p3 ‹r› ‹g› ‹b›) may be easier to reason about—3. Specifying general colours. I’ll be blunt. Perceptual colour spaces are horrible at this. With things like HSL and LCH (and even display-p3 colours), you get values in a known range (e.g. 0–100%, 0–360°, 0–1), and they all work. With things like Oklch, do you know how thin the range of acceptable values is? Just try working with yellow, I dare you, or anywhere at the edges of what things are capable of. As a human, you cannot work with these values. You have to treat them as opaque, only to be manipulated by colour-space-aware tools. To take the most extreme example, take #ffff00, which https://oklch.com/ says is approximately oklch(0.968 0.21095439261133309 109.76923207652135)… whoops, you already slipped out into Rec2020 space. And it goes approximating it to #ffff01, anyway. And fairly minor adjustments will take it out of any current (or probable!) gamut. Just look at the diagrams, see how slim the area of legal values is at this extreme.
Perceptual colour spaces are really good for some things: interpolation, and some data visualisation palette things. But beyond that, you’re in a digital world, and a limited world at that, and they’re actually quite difficult to work with, and you should normally stick with #rrggbb. Especially if you have any interest in working near the maximum of any colour channel.
As an example, look at a place oklch() is used in that document:
--color-avocado-100: oklch(0.99 0 0);
--color-avocado-200: oklch(0.98 0.04 113.22);
--color-avocado-300: oklch(0.94 0.11 115.03);
--color-avocado-400: oklch(0.92 0.19 114.08);
--color-avocado-500: oklch(0.84 0.18 117.33);
--color-avocado-600: oklch(0.53 0.12 118.34);
I don’t know how they chose those numbers, but there’s no low-order polynomial curve there; they seem entirely arbitrary choices. Not a good showing for Oklch. The “modernized P3 color palette” shown is just as bad: all of L, C and H seem arbitrarily chosen. If you’re going to do it like that, you’re completely wasting your time putting the numbers in a perceptual colour space.To be clear: Oklch is way better than RGB for some things, but it’s downright terrible for some purposes (often because you do actually care about the display technology, rather than a theoretical model of how vision works), and a lot of the claimed benefits for some purposes don’t hold up to real analysis.
and now you have 100 flags across 5 configuration files and dont know which one to change to make everything work, but changing it might break the ability for a random dependency to compile, and even if you get that to work it turns out your project doesnt render anymore
You rarely see frontend devs disparaging backend devs for their tech choices.
the main project I work on uses nx, sst, and pnpm, it’s all tightly coupled. this project is fine for now. just different.
Looking at the alternatives I was considering Vite with React and Vue plugins (and try to migrate my code so it works by default without any other configuration in Vite) or try Astro (because I have Vue and React) with their default configuration, but still not sure what would be the best option.
If it where just for me I would rather use React like framework (while I don't like that much React) but at least bun supports JSX out of the box and they seems to be working towards having a bundler integrated.
The latter is true either way. These days I’ll use HTMX and let a LLM do the CSS. Unless I specifically have to work on something which faces customers. So I’m wondering if there is some trick to tailwind I’m missing?
If you’re brute forcing Tailwind then you need to study CSS fundamentals. The vast majority of its utility classes are essentially 1:1 translations of CSS properties with some syntax sugar and DX improvements. Translating them to custom classes is a mechanical process even without @apply. There are even browser extensions like Windy that can rip HTML elements with arbitrary CSS to Tailwind classes.
Tailwind is by far the first time I'm productive with styling.
I'm impressed with everything they've built.
CSS modules is a (great) mechanism for providing isolation, very useful in the context of today's component-oriented front end development.
Tailwind provides some (quite good) building blocks for a design system, such as a palette, a sizing scale, values for shadows and rounded corners... and a vast set of classes to apply them. The set of classes covers basically every CSS feature, making it possible to slap everything in the class attribute of your HTML elements and never or almost never have to deal with CSS files.
In other words, CSS modules is a solution for those who love CSS and needed just a way to not deal with naming clashes without resorting to BEM. Tailwind is a tool for those who very much would like to avoid writing CSS
I am glad that they support container-queries[1].
But all the real goodness is in contain[2] (which sounds the same but is not related).
Tailwind really needs to support contain. I am disappointed by their statement that there will be no major additions, because contain is really that important - both from a dev perspective, and from a performance perspective. It belongs in v4 from the start.
[1]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_contain... [2]: https://developer.mozilla.org/en-US/docs/Web/CSS/contain
> Installing with Vite
What the hell is Vite? Oh, it seems to be something you need to install via npm.
I cannot help but to ask - why do we require npm, or Vite, for something that looks like it should "help" with CSS?
Why is the webdev such a shitshow, that seems to get ever deeper into its own weird rabbit hole?
Shows that the "bloated" way of doing things is now the norm.
It's also not a requirement. The binary still exists, you can run it standalone.
As for why it requires npm: I dislike the Node ecosystem as much as the next guy, but it’s a tool used for web development. Where else are you gonna install it from?
Thankfully, tailwind is available directly as a downloadable binary. Unfortunately the link in their beta docs is buried, and also broken -- the correct link is here:
Surely we can agree that distributing shared libraries via a package manager is a good practice, no?
Surely we don't need a "package manager" and a "build chain" for this?
Then again, I'm a person that writes any web-related code (HTML, JS, CSS, etc.) by hand.
Where did we take the left-turn of not understanding how things work on the actual tech level? These tools hide all of the actually required steps to npm-infested bloat.
Part of that the tailwind package is doing is making sure that only the relevant tailwind classes are included.
At one point, long ago, you could just download a file, reference it in your index.html, and use it without ever having to worry about updating this package. It had its flaws, but it also had many advantages compared to having an external dependency that might conflict with your version of Typescript, or being highjacked by bad actors.
I don't diss the concept of package managers at all, but there are lots of case where vendoring an external package is preferable than adding it as a dependency.
Why do I need a car key and worry about gasoline? I can just hop on a horsie and ride away!
Hey, I yearn for the days of good car analogies too. But the car key is not an essential part of the car experience, rather a necessity borne of the distrust of follow man.
It's the NPM ecosystem above that's being stupid, and believe that adding more tools and more processing is a good thing.
That's ok, but don't go around spouting nonsense about about investors throwing their money away on js projects.
Getting started ->Installing with Vite ->Installing with PostCSS ->Installing the CLI ->Upgrading from v3
At the bottom of the CLI paragraph it says "You can also download standalone builds of the new CLI tool from GitHub for projects that don’t otherwise depend on the Node.js ecosystem."
They just put the most common stuff at the top, because that's what most devs will use.
That’s on you. Vite is the de facto standard bundler today and one of the best things to happen to the frontend ecosystem in the last few years. Without even using it, do yourself a favor and check it out.
a (CSS) code generator.
It happens to be written in JS so it can be integrated as part of existing build steps or bundling. That's the rationale behind it. :) It's build-time, not something client-side.
for the curious: