¶ Introduction and Background
What's up, dude?
Oh, you know, just hanging out, doing a podcast, doing a podcast.
Yeah. I just recorded with Derrick Reimer, few days ago, and I'm recording with you. So, like, my, streak of recording with people I've hosted other things with continues.
Nice.
Yeah. That's cool. So sounds like what podcast is this really?
Yeah. It's, yeah, it it feels like you're not putting in a lot of effort to find guests is what it looks like.
We call that tapping the network, actually. It's a very savvy business move.
Yes. Yes. Yes. Instead of something we like, sorry, we don't have a Windows version. It's like, no.
¶ Windows Version Launch and Guest Network
This is exclusively for Mac OS.
Yeah. It's all about the positioning.
It's all about the spin.
Yeah. Exactly. And we're we have a Windows version, by the way. I think we're gonna launch Tuple Windows clients open open. Just like done out of beta. Let's go. Pretty soon.
Amazing.
Yeah. It's pretty cool.
DHH can keep using it then.
Yeah.
For the one pairing session he probably does every 3 years.
Yeah. Just a sprinkling of sprinkling of pairing.
Yeah.
Alright. So we had a plan. I well yeah. My team and I had a plan to talk to you about Rust. And you were like, "oh, bad news. I haven't written any Rust, basically."
Yeah. Barely. Not enough to, like, talk about it. Let's put it that way. But maybe, like, there's conversation to be had that stems from that insight.
¶ Rewriting Tailwind: Philosophy and Execution
I don't know.
Yes. Yeah. Yeah. So you did you're you rewrote tailwind in Rust. Is that done? Is that shipped?
No. So that's not true.
Haha, great. We're crushing it.
Yeah. So so we did rewrite all of Tailwind, which is kinda just like an interesting interesting story, I guess, in general. Everyone says don't rewrite anything ever. My experience has been, like, rewriting things is awesome, and it's always super fun. And, it is it's super fun, and it turns out better.
And yeah, I don't know. So we had Tailwind, the code base. It's been around since 2017 in various incarnations. It's been rewritten twice before, really, depend depending on how you think about it. It was originally written in, like, LESS, the CSS preprocessing language, before it was ever even released.
That was unsustainable because there's no way to, like, write tests or do anything. And as as it just got more complicated, it just was getting out of control. So we rewrote it in JavaScript, plus CSS, and, with no no real sort of deliberate effort to make it fast or or anything, you know. It was just make it work, was the mindset then. And then eventually, we rewrote it again because we had to change how the whole thing worked.
So back in the day, Tailwind used to generate a giant style sheet, and then it would look at all your class names in your template files and delete all the classes you didn't use. Tailwind 3 kinda does the opposite: where it looks at all the classes in your templates and then only generates the classes that you need, which ends up just working out a lot better. But we had to rewrite it to do that because it's just like a totally different approach to to doing the entire thing. Tailwind 4 is a rewrite again, but I kind of see it as sort of like the final form rewrite where it's like we really deeply understand what Tailwind is now and what it does and what the mental model of the whole system is. What if we just wrote code that actually represented that instead of, like, continuing to evolve this hodgepodge thing where we're, like, patching things on and figuring out just ways to add features without totally tearing things down and stuff like that.
¶ Ben’s Static HTML Website Idea
Can we rewind a bit? I have, like, a story off or, like, a off topic kind of question, which is: I've been thinking about redesigning my personal website.
Mhmm.
So it's not terrible. And I'm very tempted to just literally have an HTML file; like, no building whatsoever. This is literally a static file that I edit that has HTML in it. How bad is it to just throw the full Tailwind thing, minified, in the top there?
There's no such thing as the "full Tailwind thing" anymore.
Oh, interesting. Okay.
Because there's an infinite number of possible classes that it can generate because there's so much dynamic pieces to it. But there is like yeah. This isn't a good solution for you, but there's like a JavaScript version of Tailwind that you can pull in as like a script tag that'll generate the CSS in the browser based on the stuff that you're using.
Interesting. Okay. And that that would let me skip a a build step, right, if I do that?
You would have no you'd have no build step. There's a few, like, little downsides that will not impact you, which are details around like, the way that it works is it basically has to wait for the the classes to exist in the HTML, then it pulls them out and generates the styles for them. So if there's ever a situation where an element might appear on the screen before the JavaScript has a chance to figure out what class is there and apply the styles, you might see, like, a flicker of unstyled content.
You know? Like, if the if the class shows up after the element or something? Is that what you're saying?
No. It's it's more like, say, you have, like, a dialogue that opens and when you open it, it inserts a new element that wasn't there before and you want it to say, like, fade in. It's not gonna fade in because, like, it's gonna the styles that apply, like, the fade haven't been generated by Tailwind. Like, it's gonna be, like, one frame late. You know what I mean?
But it's enough that, like, the transition doesn't happen. But that's, like, the only situation where it really matters. The current JavaScript thing is kinda clunky too because we have to... It was built it was kinda built after the fact. We kinda took Tailwind, the CLI tool, and then tried to, like, basically, like, monkey patch anything that touched the file system and all this stuff and turn it into something that could run-in the browser. So it's a pretty big JavaScript file full of clunky stuff.
The new code base, we've we have a very pure core now that doesn't know IO or any of that stuff, and we can ship just that core to the browser now, and it's extremely small and extremely fast. And yeah. So they are yeah. A lot a lot of interesting whatever. I don't know. We're we're all over the place already. But...
That's cool. No. That's great. I want a Patrick Collison style, like, personal website. Or just, like, one that's extremely bare bones. It looks like a text file.
Yeah. Where it looks like it would have fit on, like, the Apple computer that was in, like, the tech room at your high school that had a resolution of 640 by 480 or whatever?
Pretty much. Yeah. Exactly. It would it could have appeared on any screen between like, from now to, like, 1980 Mhmm. And been totally fine and, like, would fit on a floppy disk.
And you literally want it to be an HTML file? Like, you wanna write blog posts where, like, every time you start a new paragraph, you have to do angle bracket, p, closing angle bracket?
I mean, like, I don't really want to do that...
When the rubber hits the road here...
Yeah.
Or do you just want a nice theme for a CMS? I feel like the older I get, the more I understand the benefits of a CMS.
I feel like I want, like yeah. I guess, though. Like, I just like there's there's so much appeal to me right now and, like, I directly edit this file. It's, like, so simple. There's no generation happening. There's no build step. No JavaScript is involved whatsoever nor ever will be. Like, I just I want the simplicity of index.html .
Yeah.
I don't know.
If you were gonna yeah. I know what you mean, but then
But then do I?
At the same time, I think you wanna write in markdown, you know.
I do actually wanna write in markdown.
That's true.
That's why I want the Tailwind Prose plugin. Yeah. But that was that was peak Tailwind for me.
You still need to compile the markdown to HTML.
Yeah. I know.
So it's still a build step.
Yeah. I guess there is. Maybe I need someone who knows programming to, like, set me up a simple, staticky Yeah.
Yeah. Personal like that. Some some eager young lad. You know?
That's right. Some new hotshot.
Person who maybe would help you fix your printer. You know?
Exactly. Who, like, go yeah. Help me lift heavy luggage so I don't hurt my old back.
Yeah.
Yeah. Okay. Yeah. Fine. So you ruined my life by getting rid of minified tailwind being a thing on a CDN.
But well, here's the thing. I was gonna say, if your site never had new styles, like, you just kinda said, this is what it looks like. This is what our records look like. It's done. There's no there's no new visual anything that's ever gonna appear except new articles that look the same as the old articles.
Then you could just compile the JavaScript once, throw it in the repository or sorry, compile the CSS once and pretend that it's a CSS file that you wrote by hand once and just sits there, you know? Yeah.
That's that's yeah.
Okay. That's another move.
Yeah. I like that. That seems that seems better, I think. Okay. Cool. Well, that's helpful. Alright. So Tailwind 4: how is this how is this a big reimagining of the system?
¶ Re-imagining Tailwind with Tailwind 4
Yeah. Okay. Good question. So we the original motivation for it was sort of 2 things. 1, it's
really a lot of it is kind
of one thing, but the one thing has multiple pieces to it. The one thing is it sounded fun.
And Yes.
And also it would the other piece to that is, like, this code base and project is really important to me, and I wanna be, like, obscenely proud of every corner of the code base. And that's just not the the state that it was in up until now. It's just kind of
These are the best motivations to do this kind of thing. I love it.
Yeah. And and it was killing my motivation for the project in a lot of ways, you know, like, just feeling like if I poked around in there, I'd find, like, some horrible band aid fix we had to put in that was just I I just felt like we hadn't been treating, like, Cobas with respect. You know what I mean? And and I think everyone on the team was sort feeling the same way. It's like kind of like what do they call it?
Like broken windows theory or something where it's just like, oh, well, we did something fucking gross there. So the code base is already done for. So what does it matter if I do a nice implementation here or a dirty implementation here? And it takes a lot of discipline to, like, fight that once it's already started creeping in. So I kinda just got everyone rallied around this idea of, like, let's, like, get in it a fresh thing and just, like, approach it with extreme care, you know, and just just try to make, like, the best thing that we can possibly make.
Even, like, in in a rational ways, like, let's decide when we use, like, double slash comments versus, like, slash star comments and be consistent about that everywhere. Let's decide. Do we end comments with a period or do we not end comments with a period? You know, like, these sorts of, like, superficial things.
Did you have to convince people to do this? It sounds like every developer's dream.
No. I don't I don't think so. The thing that I think is interesting about it from, like, a team dynamics perspective is, like, these same people on the team, myself included, who are totally willing to just say, like, fuck it. It's already a mess. Who cares?
Take basically no encouragement to get excited about the idea of let's make the perfect thing and keep it perfect forever. You know? I think I think everyone wants to do that. So it it it's exciting for everyone on the team, I think, to feel like they work at a company that's gonna even gonna let them do that and let alone, like, like, put place value on it and almost hold them accountable for it. You know? Mhmm.
Mhmm. Right. Because because if we're doing this whole rewrite, it like, it's gonna be worth it. Like, don't even think about making it nominal.
If if we screw up if we get off the rails, then, like, what was the point, You know? We have to, like, hold the line. I think back to that tweet that me and you've talked about in the past that Toby shared ages ago where he talks about, like, the hardest thing about, like, building a business is I can't remember exactly what he said, but the essence of it was basically, like, setting a bar for quality and, like, preventing, like, regression to the mean, you know, just, like, holding it, like, as tight as you can.
Yep.
And, yeah, because, like, that's the natural state of of everything, including, like, myself, you know, which is honestly the the hardest part. It's easier to sort of hold other people to your standards than than yourself in a lot of ways.
You're more likely to extend yourself some leeway.
Exactly. Yeah. Exactly. Yeah. Yeah. So that was kind of the big motivation for it
was just, like, let's make it as clean as possible. And then combined with that, it was, like, what's something that we could just
put an absurd amount of focus on that would never be like a dumb thing to invest in? And we thought let's just make it the fastest possible version of Tailwind ever. Let's make it insanely fast, basically at the expense of anything. I I don't really care, like, what it takes, if the code is harder to understand but it's faster, I don't care. I just want it to be faster.
And so that benefits users sort of, you know, tailwind was already pretty fast but, like, people love fast things. But it was more just like a really interesting challenge for us because it's it's just like a clear goal and gives you a way to sort of measure what you're doing. And it was also, like, everyone on the team is interested in performance. No one on the team has ever worked on a project where that got to be the focus to that extent. So it's a sort of a learning experience too.
Everyone's just excited to, like, learn more about the stuff and get better at it and understand, like, when it's faster to, like, access something in memory versus recompute it or stuff like that. And we had this sort of tail one three's internals was like a lot of the speed came from like very aggressive caching. So we had this goal, like, how can we make 10 14 faster than v 3 with no caching, you know, which is
Because there's, like, too much over. There's, like, conceptual overhead for the caching. Like, it's it's hard to think about.
It's a lot of complexity in the code. And and if we're gonna put any caching in, like, let's do it in a way where it's, like, layered on and not, like, just intertwined into, like, horrible places where you can't tell if it's a cache or not a cache.
Got it.
You know? Yeah. So that was kind of, like, the goal, and that led to a few different things. Stop me at any point if you wanna, like, talk about anything in specific.
Totally. Yeah. Yeah. Well, so tail and 4 is, like, speed and beauty, it seems like, is the like, the kind of goals
here. Yeah. And the other thing that falls out of it too is, like, part of the beauty thing is just modeling the problem as sort of accurately as possible based on our understanding of it. Yeah. And a major benefit of that is that a bunch of, like, features fall out of Tailwind for free that Tailwind didn't have before because things were not modeled well.
Got it. Yep.
So some of these are, like, really specific and you you need to kinda be like a tailwind power user to even appreciate it, but I'll talk about them anyways and for the benefit of the audience even if like
Yeah. Yeah. I'll just nod like I understand what's happening.
But there's like some things in Tailwind where have you ever used have you have you ever built a website with Tailwind yourself?
A bit. Yes.
So there's, like, some sort of advanced features for styling things based on the state of other elements, for example. So we have features like group hover. So say you have, like, an element and you wanna say, I want something about this element to change when the parent element is hovered. You know?
So you
could do that in CSS. Like, you can imagine what the selector is for that. But to do that in Tailwind where you're just sticking classes on things, you somehow have to, like, connect those. So in Tailwind, the way you do that is on the parent element, you could and it can be any parent up the tree. This is why it sort of needs to be specific, but you add this class called group that's sort of like a marker that's saying this is the group that we care about.
Then on the child, you add a class that says group hover and then whatever you wanna do, like change the background color or add an underline or whatever.
Yep.
And group hover is an is an example of, like, a group version of the existing hover modifier. So you can already change something on regular hover, on regular focus, but we added group hover, group focus. And the way all that was built in Tailwind 3 was we just hard coded those in. There was a group hover feature, a group focus feature, a group active, group disabled, group whatever. Like, the whole list goes on.
We just looped over them and generated them all. Yeah. With the way that the system is modeled and tow and and and sorry. The big downside of how that worked is that if someone added, like, a custom thing to Tailwind, because people add, like, their own little modifiers that represent different selectors, There would be no group version of it because we just hard coded in all the ones that existed.
Yep.
Tailwind 4, because we've figured out a way to just model the problem better, now, like, group is this it's we call it like a compound variant where it takes, like, another variant as an argument. And even if that variant is registered after the fact by the user, it still just works. Yeah. So that's, like, an example of, like, a feature that just, like, exists in 21 4 that we didn't redesign Tailwind 4 with this thing in mind, but it actually kind of just happened for free. We literally just rebuilt it, tried to think, I don't wanna loop over all these things.
How can we, like, model this better? What concepts do we have to invent that didn't exist before? And now we make it work and then you almost realize after the fact, wait a minute, that means this works now. You know, and it's just like such a exciting moment when you get features for free just because you sort of modeled the problem better.
Yeah. That feels like a incredibly strong feed like, positive feedback that, like, you made a good choice there. It's like stuff just happened.
Yeah. Yeah. Exactly. Every time you can, like, delete a conditional because you do something better somewhere else, that just means, oh, that actually is just how it works now.
Right.
You know, stuff like that.
Like, getting your data structures and the modeling, like, the problem domain modeling right should lead to that kind of outcome. Yeah. And if it doesn't, then well, like, are you are you going in the right direction at all? Like, that
A 100%. Yeah. Yeah. So there's a lot of internal changes that we're excited about that gave us those sorts of benefits and and made things easier.
That's that's such an interesting heuristic to me, which is, like, are we getting things for free with this approach? Yeah. Yeah. Let's just, like, just ask about of the 3 approaches we're considering, like, what gives us what gives us things for free? Because that's a probably a really strong sign of, like, that's the right direction.
A 100%. It's like swimming, like, up stream versus downstream, you know. Yeah. When things, like, start falling out of the architecture that you didn't expect that just start to work.
Yeah. I like that a lot.
It's just a beautiful, beautiful feeling.
Right. You've got the right abstractions probably, like the right like, you've.
Yeah. The code represents the problem accurately. You know what I mean? Like, it's just like modeled better, you know?
Right. Yep. That's cool.
Yeah. So, like, that was like a big a big point of focus, like, specifically changes that we made to clean things up. In the old code base, class names were just like passed around just strings a lot and things that needed to, like, pull out little bits of them to detect like, oh, we need to apply the hover thing here or this is like a color that has a slash after it, so we have to change the opacity. That was all like string manipulation that just happened in random parts of the code base. And, like, the new version of the code base, the first thing that we do when we find a class is we parse it into, like, a abstract syntax tree that we have created ourselves, like a data structure that represents okay.
It has an array of modifiers at the beginning. Each modifier is a data type that possibly has, like, an arbitrary part or a named part. And if it's color, it has, like, an opacity. And now it's just an object that gets passed around everywhere and you can get exactly what you need anytime you want instead of having to just like rip through strings and pull things out with regular expressions. Like, you wouldn't really even believe how bad the old code base was, I guess, you know, in in this sort of regard because it it just evolved over time from something so simple.
So when you know the level of complexity of the needs, like, at the beginning, you can just do such a better such a better job.
Does is this did this ship yet? Is Fort Telefort out?
¶ Challenges and Solutions in Tailwind Development
No. There's an Alpha out, and that came out, like, 1st week of March or something like that.
Mhmm. How long has it been? When did you start working on it?
As soon as, like, January 3rd or whatever, the first day back after, like, the New Year's kinda holidays were.
Okay. So you're, like, only, like, 4 months into this, basically. Something like that.
Yeah. Only really 2 because and once we got the alpha out, we shifted gears back to some, like, commercial work that we need to get caught up on, and we're about to release that stuff, like, this week, next week. Then we're gonna spend probably the next 2 to 4 months wrapping up v four proper.
I'm it's interesting how I mean, it's interesting to me how fast that went. Like, I'm like, why didn't you get sucked into, like, some of the downsides of rewriting things? Is it because Tailwind is just kinda small?
It can be small. Like, the code base is way smaller than it was before. I think the biggest thing is more of a it's more of a project management story than it is a code story. Alright. So we were just very well, okay.
So it took 2 months, but it it took, like, 2 years at the same time because so many of the things that we did in those 2 months were things that we sort of prototyped in different repositories leading up to that time just to, like, know if they worked or not and then just kept that in the back of our mind. It's something we wanna, like, approach problems that just like our hammock thoughts, you know, approach problems that just like our hammock thoughts, you know Yeah.
Yeah. Yeah.
That you need to just wrestle with for a long time. So this was a lot of this was very pure execution because we had much clearer ideas, I guess, of of how we wanted, certain things to work. And but in terms of getting it actually done in that period of time, we were very explicit about what was going to be in the alpha and what was not. So there's some big pieces that are not in there like backwards compatibility. So historically, Tailwind has had a JavaScript configuration file.
The v 4 does not support that right now and we'll need to figure out how we wanna do that going forward. But we really wanted to write this, like, v 4 code base as if it was as if we were just gonna do, say this was the first version knowing everything that we know now and there was no baggage, like, what would it be? So anything that we kinda wish we didn't have, we just didn't build. And did it intentionally because I wanna make sure that when we add support for that stuff, it's layered on to, like, the clean core and not just mixed in with it because we tried to do it from the beginning. So if we just build for, like, the pure, happy, cleanest, exactly what we wish the code base was path, and then backwards compatibility is as much of a layer as possible, that to me just felt, like, more likely to end up with a project that we're proud of.
Like, when v 5 comes around and we feel comfortable getting rid of certain things that, you know, are not really the encouraged path anymore. I want that to be like deleting a file. I don't want that to be like hunting around for all the places that cared about this backwards compatibility thing. You know?
Got it. So so you're kind of, like, constraining the mess into, like, a spot or, like, the the the wrinkles or whatever? Yeah.
It's it's a deliberately small scope. It it's it's very complete in the sense that we we just finished up the headless UI 2 point o documentation site. It's a tail and 4 project. It's gonna be live at the end of this week, and it totally works and we were not limited really in any way, you know.
Mhmm. Okay.
But but yeah. There's a lot of things, the hardest parts missing.
Yeah. Yeah. I mean, are you worried that all the beauty is gonna come unraveled when you are faced with these gnarly bits?
I'm not too worried about it. Mostly because we are so determined for that not to happen. You know what I mean? Like, that is the goal. So I don't think anyone will settle for that happening.
We will find ways to avoid that from happening. Even to the point where, like, one of the ways we've been talking about dealing with some backwards compatibility stuff is not to make the old thing work, but to just make it in possibly easy to upgrade to the new thing. So code mod tooling and stuff like that that can, like, go through your project and change things in a very bulletproof way, stuff like that where, okay, now we don't actually have to support the old thing because we've just helped you get to the new thing. And the more I think about that, the more I feel there's a lot of value in it. I I feel like if you let an old deprecated thing work, People will use it and be content in a way that they've been able to upgrade to the new version.
But they'll also kinda just feel like that they're not done upgrading yet. You know what I mean? It's just like, okay. This feels like a stop gap temporary thing. I'm still not doing it the best practice way or the encouraged way.
I'm kinda relying on this legacy thing. And, yes, technically, the version number of the version that I have installed is the latest thing, but I don't feel like I'm using the latest thing. So I kinda feel like just making the old thing work is not actually helping people with the core problem, which is they wanna feel like they're on the new and shiny. You know? So how can we, like, actually get them to the true new and shiny, not just Yeah.
You know? So that's something I'm thinking about a lot, and that's something we're gonna be putting a lot of time into the next couple months is exploring some of this code mod stuff.
So So you say backwards compatibility, but you actually you're not gonna be backwards compatible. You're going to have new ways of doing things that people will have to change to get to?
I'm not sure yet. That's the thing. Like, I I kinda think in a perfect world, we would have both. The old thing would work, but if you haven't done anything too too crazy, where we can sort of teach the computer how to upgrade your code base, that the computer can upgrade your code base and you don't have to use the old way. You know?
Mhmm. Specific examples like the JavaScript configuration file. Now in 214, the encouraged path is to, like, set up all your colors and stuff in your CSS file. And we could totally ingest your JavaScript configuration file, figure out what the resolved sort of color palette and everything is that you've configured, and then spit out the equivalent CSS file, and then you don't need the JavaScript file. But, of course, people do crazy things in JavaScript files.
And if we can't make that if we can't figure out a way to translate that into a CSS file, we probably should just let the JavaScript file continue to work. You know? That make sense?
Yeah. I mean, maybe. But then again, like, if you are doing a major version bump and you really value the, like, beauty and elegance of the new solution is hard, backwards incompatibility where you just remove this thing, like, on the table?
I don't know. I kinda think no because I I just feel like my personal stance is that open source maintainers generally underappreciate, the importance of backwards compatibility even with major version increases. I'd rather make, like, very small breaking changes that affect barely anybody than and and use major versions as an opportunity to do that. Like, my general strategy that has worked in Tailwind in the past is sort of, like, do a major version, reintroduce a new way to do something, stop documenting the old way to do something, wait a few years, then make the old thing, like, no longer work when it basically affects nobody because everyone's forgotten that that was even a way to do things. You know?
And I feel like that's sort of the most considered way to to do it. In my head, the way I imagine it working specifically in, like, the JavaScript configuration file case is there's just gonna be some code near the very beginning of, like, when the library starts to do its thing that looks at that file, turns it into the exact same data structure we'd get from parsing your CSS, and then passes that into the core. You know? So it's like an onion layered sort of thing. I I don't know if you ever I did a podcast with Michelle Boo, who I think she might still be at Stripe, but she was at Stripe at the time.
You were making me think of the Stripe the Stripe thing.
Yeah. The Stripe API upgrade thing. Like, they basically try to keep, like, the core API nice, and they they write layers around it to preserve backwards compatibility with, like, the changing in the API data structures and stuff. And it just goes through this, like, pipeline of transformations. So if you're on, like, the 2013 Stripe API, they have, like, a layer that you hit that then upgrades it to, like, the 2014q21, that upgrades it to the 2014q41, that upgrades it to the 2016 one, you know, until
Yeah.
Your request matches, like, what the expected input is of the current API.
Yeah. That's kind of insanely baller. I love that.
Yeah. Totally. Yeah.
It's super cool.
Yeah. So that's how I envision it though is that same idea. Like, how can how can we basically upgrade you on the edge, you know? Right. So that the core can stay clean. And obviously, until we actually do it, I can't know for sure that it's gonna be as, you know, perfect as I hope. But that's the that's the dream.
And unlike Stripe, you will eventually remove that edge translation.
That is the nice thing about not running a payment processor. Yes.
Right.
We will probably eventually remove it. Yeah.
Yeah. It's it's a bit less important to you to keep support a a way of doing it from 6 years ago.
And again, I think the code mod stuff is valuable too. There's probably certain breaking changes we've made that would be better to just give people a code mod and don't even support the old thing because like, there's a couple things where, like, we've changed the order of 2 pieces of a class name, for example. And I could either give you a configuration option that preserves, like, the legacy one that now you have some configuration option that's staring you in the face saying, LOL, you're actually using the old version of Tailwind. Or I could just give you, like, a tool that generates the diff. You open a PR to your code base and just compare them, and then now it's done and I don't need to live with this configuration option in my thing for the rest of the time.
You know?
Yeah. That's good news.
I think we gotta think about it on a on a change by change basis.
Got it.
But yeah.
So sometimes you're gonna add the compatibility, like, layer at the edge, and sometimes you're just gonna say, because this is an easily automated change Yeah. I'm just you're gonna have to do it. We're gonna make you do it.
That's how I'm thinking about it right now, for sure.
This is pretty sensible. I like that.
Yeah. I guess, like, in terms of the, the Rust stuff, just to kind of talk about that because I think that's what you're expecting us to talk about. I think, I can talk about what we used it for, but I think it's also interesting to talk about why maybe we didn't need to use it as much.
¶ Rust Components in Tailwind 4
Yeah. So so toe and fore is not does does it have any rust components or no?
It does have some rust. Okay. But it's carefully factored. So tailwind's the way tailwind works, there's a few stages to the processing. Right?
So the the very first thing we have to do is find all the files in your project that have class names in them, pull out all those class names, and then generate the CSS for all those class names. So if you think about like just those two top level stages, the first step we are now doing in Rust. So we have a crate that we've written in Rust that can receive the paths to where all your templates are, either your configured paths. We've we've also been working on features to sort of auto detect them, which we're able to do in Rust quite fast, which is interesting. But that part we wrote in Rust, and get major benefits from it for two reasons.
So the JavaScript version of this where we'd look at a file and try to find all the class names, the only way to do that and have it be fast in JavaScript is regular expressions because regular expressions are heavily optimized by the engine to, like, very native type code, you know. Mhmm. Mhmm. But they're insane. The regular expressions that we have to write and dynamically create and maintain, like, trying if you have run into a bug where it's like, oh, for some reason, this class, when I have this quoted brace in this one spot doesn't work, and I've it seems like it should because it's so similar to this other class.
Trying to find the spot to add that allowed character in some regular expression is nontrivial and, like, highly terrifying because you don't know what the consequences are. Mhmm. In Rust, though, we're able to rewrite all that logic as like a byte level parser. So now we just open up the file and we just scan through a character by character with a state machine that we maintain that's just like, you know, are we currently consuming a potential class? If so, then there's all these rules about which characters are allowed and which spots and whatever.
Otherwise, we just, like, skip characters until we see a spot that could be the start of a class and then start consuming them.
Mhmm.
And we can write that with just pure regular human understandable loops and conditionals, you know, not regular expressions that are really hard to sort of figure out because it compiles to native code. So it's just as fast as the regular expression engine and node, actually faster.
Are you using a parser generator for this? No.
It It says written by hand. We did try a parser generator, and it was much slower than writing it by hand. Interesting. Okay. But, yeah.
So, you know, that's how that part works in Rust Now. But then, like, the other huge, huge, huge benefit is this is, like, extremely parallelizable work. Mhmm. Because all it is is, like, if you can get all these text files into memory and then just, like, spread that work across all the threads, pulling in all the class names and dumping them into, like, one shared set. So you only get unique copies of each class.
¶ Tailwind 4 Goals and Achievements
You can do that across all course, and Rust has concurrency and JavaScript does not. You know, Node does not. Mhmm. Mhmm. So that instantly makes that, you know, whatever, 12 times faster or something compared to how it would be in JavaScript and then also just way faster because the it's faster to do the same work even single threaded.
Yep. But there was even even there, there's all sorts of just, like, interesting little things that we had to test and benchmark to figure out the fastest way to do stuff like so, of course, you don't want Tailwind to try and, like, generate the CSS for the same class name multiple times. So if the flex class exists in 10 of your files, you don't wanna pass it to Tailwind 10 times, because that's wasted work. You already know what CSS that generates, and it's only gonna appear in the final CSS once anyways. So, you know, figuring out what's the fastest way to end up with a unique list of of class names is an interesting problem.
Do we put them all into like a list and then like de dupe the items in that list in rust, especially since you have to do this from across threads, which is kind of interesting or do we create a set, you know, which is like, okay, well that's interesting because a set, a property of a set is that every item is unique. Can we dump things into a shared set? And that worked and that was pretty fast. But then we also discovered that by default, like the way that a set works right is it has to like under the hood it's using like hashing algorithms to like figure out where things should go and stuff because it's not just like a continuous list of things in memory, it has to like, so under the hood in Rust by default, they use, like, a cryptographically secured hashing algorithm because that's important for some use cases, but it's not important for our use case. So realizing, hey.
We can swap out the hashing algorithm with, like, a faster hashing algorithm that's not cryptographically secure. You get, like, a ton of, you know, performance benefits from that. So these are like the sorts of little knobs and dials that we're, like, tweaking and playing with and just bench benchmarking everything. It's totally gamified, right, because you're just running these, like, measurements in the command line. You're saying, can we get this under 10 milliseconds or can we get it under 5 milliseconds and comparing implementations and stuff.
But that's the only part that happens in Rust is just like pulling the class names out and then all of the work to actually take a class name, figure out what all the different parts mean, generate the CSS, turn that into like a big CSS file. All that is happening in in Node still, and that's by far the bulk of the the code. And then the other interesting, like, constraint is, okay, we wanna ship a browser based version of this like we were talking about before, like the script tag. Well, if we start writing things in Rust, how the hell do we ship that to the browser? You know, we have to compile it to WASM, which has this huge size overhead.
Now we're gonna ship, like, a 7 megabyte, like, JavaScript file or something to the browser. That's horrible. So we also had this constraint in our heads of making sure that anything that had to hap anything we did in Rust was work that didn't ever need to happen in the browser. So in the CDN based version, we don't have to, like, look at text files and pull out strings. Right?
We can actually just, like, look at the HTML tree and look at literally the class attribute on each element and pull those out which you can just use like existing browser APIs to pull out the class list from each element. And that was like a huge unlock because it it let us it it helped us understand what could go in Rust and what could go in JavaScript because in, in a regular project that's written in React or whatever, a lot of class names appear in places that aren't the class attribute because you're doing the work to figure out the class in some function somewhere and then putting it in the class attribute. But in the browser, that's just like the finished HTML, you know? The classes are in their final place. So we don't need to look at the whole file.
We literally only need to look at the the class attribute, you know. So that was like recognizing that it gave us this clear line of, like, what could happen in Rust, what could happen in JavaScript. Anyways, I, I feel like I'm just blabbering about stuff. No. No.
No. No. No. You're not. That's great. That's super good. Well, what's I'm curious what the testing story is in tone 4. Mhmm. There are a lot of tests?
¶ Testing and Quality Assurance in Tailwind 4
There's a lot of tests. Yes. There's fewer tests in v three because I think we're a bit more intentional about how to test different different things. So everything is using this tool called V test, which is just the current kind of flavor of the month JavaScript test runner and it works quite well. I'm pretty happy with that.
There's a lot of sort of pretty outside in tests where like the input to the test is a list of class names and like the CSS file that contains some of the configuration information, and we just sort of test given this list of class names and, like, this color configuration, this is the CSS that should be generated. And that's where we get probably the most confidence in terms of how like the overall system works. But there are parts of the system that are unit tested too. And that's mostly a developer experience decision because a lot of time you're working on like a little function and I just want to know that when I put this into it that this comes out. I think the way we've generally decided when to unit test something versus when to sort of test things at the system level is could I imagine this function being a package that I installed, you know, because if I did extract it as a package, well then I would write unit tests for it because I would want the package to have its own tests.
So I find that to be like a useful heuristic for deciding if something feels like useful to have unit tests for versus test it a bit more black box where you have a bit more flexibility to sort of move things around and refactor things. You know?
So if so if you would extract it as a package, you will write unit tests for it? Is that the heuristic?
If I could imagine, almost like if someone else had written this, would I have just installed it as a package? You know what I mean?
Okay. And then when if if the answer is yes, then you do write unit tests for it?
That's yes. Yeah. That's when anything that we have unit tests for generally fits that criteria. It's not really deliberate, but, like, if I just look out and look at things, that's what they seem to have in common. Like, a very specific part of the framework that has quite a few unit tests is we have this one function whose only job is to take a string and split it on a certain character into all the different parts, but in a way that's like aware of, braces and quotes.
So you can imagine, like, in Tailwind, if you have, like so something we have to do a lot is if you have, like, hover colon focus colon active flex, we have to split on all those colons, get all those parts out. 3 of them are modifiers. 1 of them is like a utility class. But some utility classes in tailwind, like the content utility, which is the sort of thing you use with a lot of, like, pseudo elements and stuff, content can take, like, a string as its argument. So you could you could have, like, a a colon inside the two quotes of the content property.
So if we Got it. If we have, like, hovercoloncontentdashsquarebracketquotecolonquote square bracket, and we try to split that on colons. We don't wanna split on the last one because it's like quoted colon. You know?
It's in the string. Yeah.
Yeah. And similarly, we don't wanna split on colons that are within braces or anything either. So we can't just use, like, the regular string split function in JavaScript. We had to write our own function that sort of traverses the string, detects when we're inside, like, a pair of braces and ignores the split character there. And so we have this function called segment is what we called it, that did that. And I just looked up synonyms for the word split and,
Yep. I like segment. I'm gonna say that's a good name.
So it segments the the string into these different things. But, yeah, it's aware of sort of, like, nested structures basically in quotes. And that's, like, a good example of something that I could see being an MPM package. You know?
I see.
Yeah. So that has unit tests because we knew exactly what it needed to do. It's used everywhere. It's useful in a lot of different ways. And we just threw a bunch of test cases at it. And it's also, like, an interesting place to, like, do an insane amount of performance optimizations and benchmarking because that it literally runs, like, thousands of times in in the code base. Yeah. So yeah.
Right. And I can imagine, like, oh, you discover a bug in a certain edge case and, like, you don't wanna throw that in like a end to end test probably.
Yeah. Literally just had merged the PR to do that yesterday, where we had a bug where let me remember exactly what it was. It was to do with, braces in quotes. So we weren't accounting for that properly.
Okay.
And we could have written, like, some test that has, like, a tailwind class that sort of does that and make sure that it works. And we actually did do that too just to sort of prove that. But we all but for that one test, there's like 9 unit tests that show, like, all the different characters that could possibly in quote in quotes and stuff like that and and hit them properly. And that fix actually made it faster too, which was kinda interesting because we realized that, once you're inside of a quote, you don't need to keep track of the braces and make sure they balance because, like, you know you're in quotes. These are just characters that don't syntactically matter.
So we could just, like, skip processing every character until we find, like, the matching quote. So that was, like, a delight to fix a bug that makes the code faster. You know? Yeah.
Do did you know it was faster because you're, like, checking the benchmarks after you do it, or are there some, like, automated benchmarking that happens?
We benchmarked it after, which is how we knew, but it was also the sort of thing where when you're writing the code, you sort of just have a sense. It's like, oh, I'm doing less work now, you know. Yeah.
¶ Tailwind 4 Release
Nice. So when what do you think tailwind 4 comes out?
I had a goal originally for end of June. I don't think that's gonna happen now because we're a little bit behind on the stuff we're supposed to have out this week, and we have a team retreat coming up in 2 weeks. So I kinda think we're not gonna start on anything new till after the team retreat. We won't be back from the team retreat until, like, May 20th. And then I've got another trip in June.
So I think we're gonna try to get a beta out at the end of June, which is basically gonna be hopefully all the features are there, but we haven't updated the docs site and redesigned the website and stuff like that. And then do more of the more of a marketing project focus for, like, July, August, and do, like, a proper release then. But I don't know. I can't wait much longer than that because we have other other work to do too. Thankfully, it is good for business, for us in general to, like, release open source stuff.
Even if we've released nothing new or changed nothing about the commercial stuff, there's just a lot of sort of activity and excitement that comes around with the open source things. So
Right. Yeah. So sales go up when you release a new
Sales go up when we release open source stuff, which is, yeah, thank god because otherwise it would be a lot it would be scary to be investing time in the open source stuff. It would it would it would feel stressful. You know what I mean? Whereas in at least now we sort of know that, yeah, the open source stuff is good for business, thankfully. So
Yeah. That's so nice. You live you live a charmed existence business wise.
I do. Yeah. In a lot of ways. I take it for granted a lot and, big time.
Like, you're working working on the core free thing is, like, doing marketing? Yeah. Like, that's most people don't have that, I think. Like, marketing is separate. It you know, it doesn't doesn't it's not an out, it's not an outcome of working on the core thing.
It's kinda separate, though, because I think there's a world where we could work on it and never talk about it until it was done and not get any of the benefits. You know?
Well, once you launch it, that's probably there's gonna be a significant benefit. Right? Like, tailwind 4 will cause a lot of buzz inherent.
Yeah. That's true. But even when we're working on v 4 and I'm tweeting out benchmarks and stuff like that, sales are higher on those days. Yeah. Yeah. Totally.
Yep. So That's beautiful. Yeah. Yeah. So there is a separate marketing effort, but it's really it's it's the it's using the sawdust of the improving the core thing.
Yeah. And that is really just, like, by necessity. When you're this small, I feel like all you can really do is you have to just, like, try to hit as many birds with that stone as you can, you know.
Mhmm. Mhmm.
And thankfully, it's good. It's the right type of content for our audience too, you know, a lot. Right. I don't know. There's a lot to be thankful for in terms of things just, like, happening to happening to be the right way to do things in our situation.
You know? Yeah. But, yeah. Totally. I mean, when every time I tell somebody that, like, the tailwind origin story, I just feel like it's the most preposterous thing.
¶ The Tailwind Origin Story
And what's your version of the tailwind origin story?
I actually was I was gonna say, like, I I kinda wanna run the story by you to make sure I'm not just, like, spreading this, like, fake narrative. But the the way I say it is you were trying to make a SaaS, I think, to sell info products. Yep. Kite tail. Yep. And you had this little set of, like, CSS utility classes that you would copy between projects Mhmm.
To, like,
let you go faster as you're styling things. And you were doing live streams to try to, like, market the thing. And people kept you like, what's that CSS framework? And you're, like, it's not a framework. It's just, like, this little file I have. And then, like, stop talking about that. I like that you would keep going and they're, like, no. What's that CSS framework? And it's, like, the framework. What's the framework?
And, eventually, you're just like, goddamn it. And you gave up. You're like, fine. It's a framework. Here you go. And, like, you released it, and that was the start of Tailwind.
Yeah. That's a 100 a 100% it.
Yep. Nice. Okay. Cool. Yeah. This is so crazy.
I know. It's awesome.
Like, it's so funny. So yeah. I like this is like I often tell people this story in the con like because I'm trying to, like, tell them, like, to just go do stuff.
Yes. Exactly.
Or, like like, make like, work like, if you hadn't worked in public, you wouldn't have known that this thing was that useful.
Yeah. And and and even if I hadn't worked in even for people who aren't working in public, I do generally feel I think, like, Matt Wensink tweeted this once. It was like, come up with an idea, build it, run into some really hard problem, solve that problem. The thing that you did there, that's your real business, you know.
Totally. Yeah. I think there's some real wisdom in that.
Yeah. Just do anything, and you will encounter real problems. You know?
Right. Exactly. Yeah. Like, the real problem was the problems we found along the way.
Yeah. Exactly. The real business was the businesses we found along the way. You know?
Yeah. Because yeah. And, like, that first idea, like, maybe you're really good at this, but, nah, probably you picked something stupid. Like, you never launched Kitel. That never worked.
Never. And I never would because it had to have, like, PayPal integration. I don't wanna maintain that code, you know, and there's taxes and, like, all sorts of horrible things. But I'm so glad that and and and I was real serious about it too, which I think is maybe, like, another reason why that was I did go, like, very all in on it. Like, I did wanna build it for real.
I wanted it to be a real thing. I really believed that's what I was doing, which I think is the only reason that I did the live streaming and and all this other stuff and encountered what the market actually wanted from me. You know? Right. Totally.
¶ Business Strategies and Open Source Impact
And this is not like an isolated thing. I think there's a number of good examples of these. Like, Segment comes to mind Yeah. Or, like, the Segment Analytics Is that
Slack too? You know?
Yes. Yes. Totally. Yeah. Yeah. I think so as well. So it's it's just I think this happens kinda frequently. Yeah. Definitely. So so I often when people are, like, saying, like, oh, I can't like, what do I do for business ideas? How do I find an idea? I'm often kind of, like, look at the things you're doing at work. Like, what are the what are the internal libraries you built that are actually useful Yeah. That people like? That you though you'd be sad if they went away.
Like, pay attention to those. Maybe, like, commercialize that.
Yep. A 100%. Yeah. It's it's it's crazy that the problems that you run into when you just try to do a real thing. Like
Mhmm.
Yeah. I'm itching to build, like, a hiring tool now because we've been hiring. You know what I mean?
Yeah. Yeah. Are you gonna do that or what?
I don't know. I am thinking about it. It's definitely I'm telling myself it would be good for Tailwind if we did it because it would it would be the most real sort of application that we'd ever sort of worked on. But obviously, it's a big big project to work on. My concern around doing it, I think, is that we do it so little that I don't think it's reasonable to, like, pretend to be an authority on it. But maybe that's not a reason to not do it. I can always learn. You know?
Yeah. I get to me, the biggest risk is actually just this. Like, it's just so different than what your core business is. That's like it's almost like a new company.
Yeah. But in some ways, that's kind of, like, the thing that's that's fun about it too. This is, like, a whole whole other topic. This is, like, an other a topic for other podcasts. But I do I do often wonder if, like, my long term best play is to keep pushing hard on the tailwind business or to recognize that the tailwind business is my opportunity to build the next bigger business.
Like this is the thing that will fund the next thing and it won't last forever and I should probably maybe I should take advantage of that, you know? Mhmm. It's so hard to know. Do you Is it like this is working, why would you get distracted and work on something else? Or is it like you have this amazing cash flowing thing and like a bunch of runway.
Now is your opportunity to invest before, like, that goes away. And historically, that's what I've always done. You know? Like, first book funds me building the next course, which funds me and Steve doing refractory AI, which funds me and Steve building Tailwind UI, which I run forever Yeah. Or funds the next thing. I'm not sure, you know. So
Yeah. Yeah. Yeah. I mean, I almost feel like it's like, the way you're framing it feels wrong to me sort of where you're like, oh, should I do this while I have this runway? And it's, like, okay. Like, you already have a successful enough business. You already have it's, like, this is not a money thing. It's not like to, like, make enough money to live anymore.
Sure. Yeah. There's multiple pieces.
Now it's like, how do you want your life to be? And so to me, it's like, if you want your life to be a thing where, like, you work on a new business, like, like, if you're, like, feel called to and compelled to start this new thing, then, like, absolutely do it regardless of what the like, whether or not it's like a financially whatever thing. I don't know.
Yeah. That's true. I guess I guess in my head I feel like I've seen it happen to other people in the past where they had like a good thing going and they should've like made the leap to like go all in on it but then it didn't they didn't and then it the thing kind of like fizzled out a little bit and they sort of missed their chance to sort of like go all in, you know?
To go all in on something else, you mean?
Even to just, like, go all in on on the first thing so they had the chance to do, like, the next thing. Like, the comparison, I guess, I'm seeing in my mind with this is, like, does does Tailwind is there enough people and this is the whole fucking lifetime pricing conversation forever. Right? But, like, do we run out of programmers eventually who, like, are wanting to buy the templates and stuff that, we sell, you know?
I just think no.
Yeah. Maybe no. So maybe we could just do this same thing forever, you know. But Mhmm. Eventually, Tailwind's not gonna be cool anymore, you know.
Yeah. But I mean, like, I don't know. Good luck competing with tail and 4. It seems like you're just kinda keep
I know. I I just have to remind myself that it's our job to, like, keep it cool. Like, React is still cool and it's over 10 years old at this point, You know? And that's by continuing to evolve it.
You reach a point where you become immortal as a technology. Like, 10 years into Tailwind, you're probably like, just forever, there will be, like, Tailwind projects.
Yeah. There will be Tailwind projects, but will there be, like, people choosing Tailwind for new projects? That's
Yeah. That's that's true. Yeah. Because you're only yeah. You're you're pretty much making money on the people that are new to it, most likely. Yeah. But I but, again, I I don't feel like they're like, what if the gravy train stops and, like, we no more money comes out? It feels like the wrong motivation for you to pick the next thing or not.
I I agree. Like, I'm fine. Yeah.
Yeah. So Exactly. Yeah. So just choose what kind of life you wanna have.
Yeah. I don't know that I wanna have a life where I run a SaaS business where I have to have, like, 20 customer support staff and all that sort of thing, personally.
Mhmm. You can go go up your levels level stuff.
Yeah. Maybe but maybe that's just like you don't need to do that. Maybe you can still build can you think of off the top of your head? I know we gotta wrap up in a couple minutes here. But can you name a successful SaaS company that you think is doing at least 7 figures a year, say at least mid 7 figures a year that is, like, super lean, you know, like, in you know, like, 10 people or less.
I mean, we're we are close to this parameter.
Yeah. But others that come to mind, like, is there is there is there, like, oh, that shining example that, like, we're, you know what I mean?
I I feel like that almost doesn't matter. Like Sure. You could just decide how you're going to build this business and probably make it work. So if you say, like, I'm never gonna have more than 10 employees, and I don't care what the trade offs are involved in that, like, that's just like a that constraint is important to me, and I won't relax it, then, like, maybe you just hit this thing. And maybe no one else has even if no one else has, you might just anyway because you just actually decided that's what you care about.
Yep. I think you're right about that, actually.
And you're Adam fucking wadding. Plus, like, the whole, like, headcount thing too now. It's like if you're like, oh, yeah. We are never gonna go above 10. So we're gonna, like, leverage, like, AI Sure. As hard as possible all the time. And, like, it'll be ridiculous for us to add even one new person to this company. So, like, we're gonna be, like, 3 people for, like, for years before we, like, surrender and accept we have to have 4, and we'll just, like, automate, like, crazy people.
Yeah. I I think that can sort of work. I'm sure there's good examples of that. I'm still struggling to see, like, where that totally replaces people. Like, I don't think that totally replaces a programmer. I don't think it totally replaces a designer. I don't think it totally replaces even a customer support person. You know?
I I just I just really think if you go into this business and decide what your your deal breaker parameters are, you can probably still be quite successful even with those constraints. And if it limits some of that success then who cares because you've already decided that those constraints are more important so if it makes Yeah. $4,000,000 a year instead of $8,000,000 a year because you refuse to hire an enterprise salesperson, fine. Like, who cares? Like, are you having a good time?
Yep. I think you're right. Food for thought.
Alright. Let's wrap on that.
Yeah. Sounds good.
We did it. We made a podcast even though you didn't rewrite Tailwind and Rust.
No. Yeah. That can be the episode
title. Not rewriting Tailwind and Rest. Sweet, man. Thanks for coming by. It was good to talk.
Of course.
Alright. See you.
See you.
