spotifyovercastrssapple-podcasts

elm-tailwind-modules

We discuss using elm-tailwind-modules to build type-safe Tailwind views. It's composable, uses Elm's built-in dead code elimination, and is a delightful way to style Elm apps!
March 29, 2021
#27

Transcript

[00:00:00]
Hello Jeroen! Hello Dillon! And once again today we've got a special guest with us here to talk about a very cool library called Elm Tailwind Modules.
[00:00:11]
Philip, thank you so much for joining us. Hi, thanks for having me. It's our pleasure.
[00:00:15]
Do you want to give us a brief intro, you know, what you do, where you work? Tell us a little bit about yourself.
[00:00:22]
Okay, so up until recently, I guess, I've been a student in Karlsruhe at KIT. But I started working for Fission last year in July.
[00:00:36]
And yeah, since then I've been freelancing and starting this year, early this year, I've become a well now part time but soon full time employee of Fission.
[00:00:47]
And there I'm writing Elm for all of the core applications.
[00:00:51]
Awesome. And you were you doing some research at the university as well? You were you were pretty, pretty in on a lot of like the deep dive category theory stuff and a lot of those things when when I last talked to you.
[00:01:03]
Yeah. So well, most of the deep dive category stuff is from conversations and yeah, lots of thoughts with together with my brother and I guess self study a little bit.
[00:01:17]
So that's where I guess I picked up that. And we were trying stuff. We also tried to start up something but that was really early and and other than that, I was also doing some research in university like officially.
[00:01:30]
And that was, for example, my bachelor's thesis on reduce, which maybe some of you know. Yeah.
[00:01:36]
Yeah. So that was something else I've been doing in Elm, which was not totally hobby or side project before.
[00:01:43]
That's awesome.
[00:01:44]
Yeah. The topic that we invited you for is Elm Tailwind Modules. So maybe give us a brief intro to what Tailwind is and then what Elm Tailwind Modules is.
[00:01:56]
Yeah. So no one has really figured out how to do styling in the web today yet. I guess it feels like it feels like that at least.
[00:02:06]
So yeah, Tailwind is this utility first CSS framework, which is how they describe themselves, which means that instead of trying to structure your CSS using lots of class selectors and complicated selectors, you simply use small snippets of reusable CSS, I guess, in your HTML.
[00:02:27]
So you have lots more lots more changes in your HTML than you have in your CSS. Your CSS then gets only generated by like a configuration file, a Tailwind configuration file with all of the colors and spacings you use.
[00:02:41]
And then you end up using only classes when writing HTML.
[00:02:46]
Yeah. So it's it looks like inline CSS, but instead of applying specific properties like color is red, you say you apply class saying red or exactly font red, background red, stuff like that.
[00:03:03]
Yeah, it's almost like a CSS class based API. It's like you're calling API functions to style HTML elements by applying classes.
[00:03:14]
Yeah. And at the beginning, you think, oh, this is so limiting. I mean, how would you, for example, use another color? But most of the time by limiting your options by only having certain spacings available, like a set of maybe 16 different spacings, you end up having really, how's it called?
[00:03:34]
Consistent. Yeah, consistent user interfaces with consistent spacings and lots of different spacings. Yeah, I think that's that's one of the features that makes Tailwind a really nice experience to work with and allows you to create really nice experiences on the web, because it kind of has this built in concept of a palette.
[00:03:54]
So you have like a palette of colors. So you have like, you know, BG red 500 would be like the built in default color palette they give you. But the intended way to use it is really to define your own palette.
[00:04:09]
So you have you can define a primary color or secondary or however you name them, define those colors, and then it generates these CSS classes for you to do BG primary or BG secondary or whatever your colors are called.
[00:04:24]
And that, you know, so it gives you this palette for, like you said, spacing for colors and for breakpoints. So you can define your palette of breakpoints and those constraints, those limited selections in the palette really make it so much easier to create nice experiences for your web design.
[00:04:42]
Yeah. And at the beginning, when I was starting with, or when I first looked at Tailwind CSS, I was thinking, hmm, is this how CSS is meant to be used? But after I tried, I tried a lot with my blog, and I experimented a little bit with different ways of using CSS.
[00:04:59]
First using plain old CSS, then using LUI, then using SCSS, SAS, yeah, and finally, converging on Tailwind CSS. And I really felt like, for example, the SAS based version was hard to maintain.
[00:05:17]
Because if I was making a change to the layout, my blog, I ended up having to go into my CSS and change it again, and never really knowing how the CSS and the HTML are coupled together. So is the CSS selector even active anymore? I don't know.
[00:05:33]
And it's really hard to check. So like in this way, I've never felt like the way CSS is, I mean, meant to be used, I guess, was really working for me.
[00:05:44]
Right. And then you have this like layer of indirection for like the selectors that you write in your CSS style sheets using, you know, the kind of traditional way of writing CSS. And you don't know what unintended consequences are going to come from changing a CSS selector somewhere, or changing the style somewhere.
[00:06:04]
So you have this like, with this like utility based approach, you have this like localized reasoning benefit, where, you know, if you change the CSS classes, the Tailwind classes that are being applied to one element, you know, it's not going to have, you know, an effect on some spooky effect at a distance on some other element, which is similar to the feeling of using LMI, in a way.
[00:06:27]
It can still have an effect on the children of the elements, right? Like the if you set the color to be red on an element, all the children of that will be red.
[00:06:38]
So yeah, there's CSS inheritance. So some properties are inheritable, and children inherit these properties, but I don't know which ones are exactly inheritable.
[00:06:49]
Okay, I didn't figure, I didn't know that. Yeah, it's true. For example, for text, and font sizes, and colors, not colors, but fonts, fonts in general, they all get inherited, but that's still true with Tailwind CSS.
[00:07:03]
Yeah, okay. And I never got that distinction of inheritable and not inheritable properties. Good to know.
[00:07:10]
Yeah, so for example, display flex is something that just is just set on like the element you set it on and not on children.
[00:07:18]
Right. And if you apply padding, you know, if you say padding, you know, p2, or px2 for padding horizontal to, if you apply that Tailwind class, it's not going to change the padding for any of the child elements, it's just the parent element that's going to have that padding applied.
[00:07:35]
So you can think locally about a lot of those utility classes. But you know, it's, it's different than if you, you know, go in and change something in your CSS style sheet. And you say you change the padding on a particular type of element, a particular type of selector, you don't know what that's going to affect.
[00:07:55]
But when you say, you know, px2, you know, it's going to apply to the thing you're saying that HTML tag that you're applying it to.
[00:08:02]
Yes, exactly. And if you, for example, had this more complicated selector, which actually selected something in your HTML, and you had another place, you were thinking of changing the structure of your HTML, you end up looking into your CSS and wondering, can I just change the selector?
[00:08:19]
But changing the selector is so dangerous, or at least it feels like that. And you end up copying your whole definition and changing the selector then. And so you end up with duplicate CSS. And to me, that feels a lot like the symptoms of premature optimization, I'd say.
[00:08:36]
So you optimize, not optimization, but abstraction, premature abstract. So you extracted something, but ended up feeling like you needed to tweak it a little bit, and then you duplicate it. And so you defeated the purpose of abstracting the first place.
[00:08:51]
Yeah, and sometimes these like CSS selector abstractions can be very awkward to think about, you know, it's like, if something has this CSS class, and then it's the immediate child of that, which is a span, or, you know, whatever, then it's going to have the styling applied, right?
[00:09:09]
So you're trying to figure out why is this styling not applied. And the abstraction is, you know, you can easily end up just applying a bunch of these rules that don't have a clean abstraction. And then you're stuck with them, because you don't know what you're going to break if you change it in the future.
[00:09:25]
Whereas if you're using utility classes, you're just sort of like, the padding seems off on this element, I'm just going to remove this utility class here. And you know, it's not going to break anything else. And because it's not applying this abstraction where anything that follows this structure where it has this class, and then it has this following CSS selector within that is going to get this styling applied.
[00:09:50]
And I also think most or a lot of that has to do with I think there was this concept of cohesion. And I forgot this coupling, coupling, I think, yeah. And so if two things are tightly coupled, they should be in a place close to each other.
[00:10:07]
But if you end up having like CSS in a totally different file than HTML, it becomes difficult. It's easier to just delete both, I guess, if you have them in a similar place, like it is when you use CSS utility class.
[00:10:22]
Yeah, I totally agree. Like separating things is not inherently good. It doesn't inherently make the code easier to reason about. If you've separated two things that are intimately connected and need to be understood together to make sense of the whole separating them actually makes it harder to understand.
[00:10:40]
So that's why you want the cohesion. And so I completely agree like utility classes give you more cohesion when compared to using CSS selectors in a style sheet. So you were writing code with Tailwind CSS, and you start applying these utility classes.
[00:10:58]
And you're thinking, this is pretty cool. But wait a minute, it's not type safe. What if I type a Tailwind class incorrectly? I wouldn't get a compiler error.
[00:11:08]
Exactly. So there it was writing my blog, and I haven't made the refactor yet. But so if you look code up, it's all class names inside of strings.
[00:11:18]
And you've got a couple of weeks so
[00:11:24]
No pressure.
[00:11:25]
Thinking about stuff in my backlog.
[00:11:29]
Okay, so yeah, I have these strings full of CSS classes. And what if I make a typo? So yeah, I end up wondering why does my CSS not get applied? Or what if I tweak my CSS configuration, like my Tailwind config file, and it generates different class names, and I end up changing the class names almost everywhere but forget one place.
[00:11:51]
It silently fails. So this is not ideal. And there's been solutions in the Elm community. For example, I should look it up. It's Dean's project.
[00:12:03]
Monty5811PoseCSSElmTailwind?
[00:12:06]
Exactly. It's a little bit hard to remember name, I'd say. But yes, exactly.
[00:12:11]
I just read it in my memory.
[00:12:15]
Nice. Exactly. And in that project, that project solves the issue of incorrect class names appearing somewhere silently, without failing.
[00:12:25]
So how does it do that?
[00:12:27]
It takes your CSS, looks at the generated CSS, the CSS generated by Tailwind, looks at all the class names that appear in there, and creates an Elm definition, which is basically just an alias for the type class.
[00:12:40]
So you end up importing this generated CSS file, and if the CSS in quotation definition doesn't exist, so it's just a plain value, the Elm compiler will tell you, hey, you're referencing something that doesn't exist.
[00:12:58]
So you either need to recompile to get the updated class name, or it just doesn't exist at all, and you should probably fix this bug.
[00:13:07]
Yes, exactly.
[00:13:08]
We do a lot of work in the Elm community to not be afraid of typos. Don't hurt me.
[00:13:14]
Types without borders.
[00:13:16]
Elm is really safe. You can do so many refactoring in Elm. Oh, but what if you do typos? Oh, no.
[00:13:24]
Is the Elm community just paranoid?
[00:13:27]
Maybe. Maybe. Just maybe.
[00:13:30]
Don't we trust ourselves?
[00:13:33]
No.
[00:13:34]
I don't. Absolutely not.
[00:13:35]
Me neither.
[00:13:38]
So what was the problem with Monty's project? Why didn't you just use that one?
[00:13:43]
I did use that one. Well, at least for work related things. That's what we use at Fission. Oh, we still use at Fission. Well, and also another version of it, but I guess keep it short.
[00:13:56]
Details.
[00:13:57]
Details. Yeah. The thing I was noticing when using Monty's Tailwind generation project was that it created pretty big Elm files.
[00:14:05]
At one point, I ended up with a 300,000 lines of code Elm file because I was using all of the Tailwind utilities plus two plugins in Tailwind, the typography plugin and the Tailwind UI plugin.
[00:14:20]
I think I used it to stress the performance of Elm review, just by the way. It took like five seconds to parse. So it's really big. It's really big.
[00:14:33]
Exactly. So for people who might be wondering why, Tailwind uses a sort of brute force approach to generate every permutation of all of these different variations.
[00:14:46]
So if you have your color palette, so if you have like 20 colors and then you have your breakpoints and then you have your spacing, it's going to create for every breakpoint and for every color, it's going to create every type of padding.
[00:15:03]
Padding X for all of the different, or well, I guess you don't have padding for colors, but it's going to create every BG color for every breakpoint.
[00:15:13]
So if you have like the medium breakpoint, it's going to be like MD colon BG dash red dash 500.
[00:15:22]
And it's going to generate every permutation of all of these different possibilities mixed together, which is essentially emulating almost like calling an API and saying like, okay, for this breakpoint, pass in this argument of this color for background, background color or something.
[00:15:41]
Right. But it's doing it through CSS classes. So it just brute force generates every permutation and it's a combinatoric explosion.
[00:15:50]
And then the way Tailwind deals with that is it uses something called purge CSS, which uses like regex to go through your source code, find all the CSS classes that are used and strip out from the CSS all the ones you don't use to have a reasonably sized CSS bundle in production.
[00:16:09]
So that didn't work well for the Elm compiler and output. It didn't like that.
[00:16:14]
Well, I don't think the Elm compiler was actually surprisingly fast. It wasn't the bottleneck, at least.
[00:16:19]
It was more my tooling, which seemed to slow down a lot more when I was using projects with generators, Elm modules, which were that big.
[00:16:29]
Yeah, I think I remember VS code slowing down pretty heavily, at least.
[00:16:34]
There's been lots of improvements, though. I've used these files to do more debugging in VS code, actually.
[00:16:41]
Nice. Not much, but a little bit. And they figured out some improvements to it. So that was nice.
[00:16:49]
Still, it slowed down my tooling a lot. And in September last year, I saw one project which was post CSS, Elm, Tailwind, no wait, post CSS, Tailwind, Elm CSS.
[00:17:03]
Post CSS, Elm CSS, Tailwind from Justin Reyes.
[00:17:06]
Exactly. You've got all of the links prepared. Nice. This turns into some kind of bingo of me trying to pronounce the...
[00:17:16]
This is our special Christmas episode again.
[00:17:21]
Exactly. That project from Justin. And he had this genius idea of using Elm CSS and transforming all of the CSS that Tailwind would generate and transform the generated CSS into generated Elm code in a way so you can just throw away the CSS again.
[00:17:40]
Just use the generated Elm code. So there's Elm CSS, which lets you define CSS within Elm, kind of similar and inspired to CSS in JS.
[00:17:51]
And he was using that to define an Elm file which contained all of the properties that would be defined in the Tailwind CSS file and have a neat Elm definition for each of the utility classes then.
[00:18:06]
Right. So and I think he also had this innovation where it could be more composable to put those pieces together, right? Because you had instead of having a single...
[00:18:17]
So, you know, like I was saying before, like conceptually Tailwind is essentially a hack to give you an API through CSS classes where, you know, you can say at the medium breakpoint on hover, the background color of this button should be blue.
[00:18:36]
And so it generates MD colon hover colon BG dash blue or, you know, whatever. But, you know, conceptually it's an API that you're passing in a bunch of arguments to these functions.
[00:18:51]
And well, that's sort of what Justin Ressier had as his innovation was that you could actually generate Elm code that composes those things together to give you the CSS class names. Is that the idea behind his library?
[00:19:05]
Exactly. And like one of the examples I was having at work was we use dark mode. So we ended up having just twice as much CSS and Elm code, therefore.
[00:19:17]
Right. Right.
[00:19:19]
But what you would do in Elm CSS was just define another function which would be called maybe dark and it would just wrap your CSS you were using with a media query which queries for prefers color theme dark.
[00:19:37]
And you don't have to add anything else. So it's just one more definition instead of doubling all of your definitions.
[00:19:43]
That's awesome. So it's using Elm CSS under the hood as well, right? This library here?
[00:19:48]
Yes. Not as well. This one does and Monti's didn't.
[00:19:53]
Oh, right. I meant compared to Philip's library.
[00:19:56]
Exactly. True. Yes.
[00:19:59]
So we talked about, you know, the pain points with Monti's having, you know, generated code that was difficult for tooling to parse.
[00:20:08]
What was the what was the reason that made you want to build something? What were the pain points of using Justin Ressier's approach?
[00:20:15]
Justin's project was not complete at the point I was trying to use it. So I was trying to use it with, for example, Italian CSS plugins like the typography plugin or the UI plugin.
[00:20:28]
But it ended up not generating correct Elm code. And so I was trying to I was taking this project and making changes to it.
[00:20:37]
And while I was doing that, I was thinking of more architectural improvements to it, I guess. And in the end, it happened to be, I guess, more of a coincidence that I did not that I forked this project instead of really contributing to it.
[00:20:52]
I was planning on switching to another Elm library.
[00:20:56]
So instead of using Elm CSS, I was thinking about using Elm Origami, which is some not well known at all Elm project, which is again a fork of Elm CSS.
[00:21:08]
So I guess we're like getting into the red lines on the wall kind of thing here.
[00:21:15]
I know the meme you're referring to.
[00:21:18]
But yeah, so accidents happened. I ended up significantly reworking his project, but starting out from his and making changes to it, switching the code generation from plain JavaScript to TypeScript,
[00:21:35]
changing it from doing internal changes and adding more features. So it became possible to, for example, use generators, Tailwinds, CSS with plugins. Exactly.
[00:21:48]
Right. So the plugins and the customization of the palette is really cool because you tweak your Tailwind config and you change your palette, you change your color palette to have your primary and secondary instead of the built in blue 500, blue 600.
[00:22:07]
And now it's generating these palettes for you and reducing your options. So let's talk a little bit about what the experience now feels like using your Elm Tailwind modules tool.
[00:22:21]
So it's built on Elm CSS under the hood.
[00:22:24]
It's just giving you Elm CSS code that's actually defining the same CSS that Tailwind would. So you don't actually have a CSS file with the built in permutations of every possible Tailwind class.
[00:22:39]
There are actually no Tailwind classes in your code when you use your Elm Tailwind modules tool.
[00:22:45]
Yes. So the fastest way to check out this project would be just to use some, I guess, pre compiled or pre generated Elm code, which I uploaded to the Elm packages directory.
[00:22:56]
I think the package is called Matthias23 slash Elm default Tailwind modules. And in there, there's just two files with two modules with lots of definitions.
[00:23:09]
It's basically the generated code. And then you'd use Elm HTML just like you're used to. And there is one more HTML attribute you can use because of Elm CSS, which is the CSS attribute.
[00:23:23]
It takes a list of parameters and there you can just import these definitions and insert them there. And it'll apply them like if you were applying a list of classes.
[00:23:34]
It feels similar at least. By the way, why does the Elm CSS API have that CSS function? So you've got Elm CSS.
[00:23:43]
There are like a lot of things to define for this episode, but once you get into the flow of using it, it doesn't feel overwhelming.
[00:23:52]
But there are a lot of concepts to introduce. So with Elm CSS, it's a drop in replacement for Elm HTML essentially.
[00:24:01]
It aims to be that plus then a superset with the added features. So then if you have like an anchor tag, if you have, you know, with Elm HTML, you would do an anchor tag with just a and then list of attributes and then list of children.
[00:24:18]
And the list of attributes, you can do HTML attributes dot href. And then you can add HTML attributes dot style and you can add inline styles.
[00:24:28]
So I guess Elm CSS, it would look exactly the same except your imports would be different. Instead of importing HTML, you import styles dot HTML as HTML or something like that.
[00:24:39]
And you still have a for the anchor tag. You still have the list of attributes and the list of children. But now in the list of attributes, you can include you can call the CSS function, which then takes a list of styles.
[00:24:52]
So I guess that would be like the alternative to doing HTML attributes dot style background read. You would do CSS list of CSS attributes and then you would do CSS dot background and then CSS dot RGB 25500 or something like that.
[00:25:12]
And the value of that is, again, not to have typo errors. Right. Right. The goal the goal of R.T. Feldman slash Elm CSS is to give you a drop in replacement library for Elm HTML that adds CSS styling utilities that give you a type safe API for doing CSS.
[00:25:32]
It's not attempting to be, you know, giving you a simplified way of expressing things like Elm UI. It's just like you want to use CSS. Here's a type safe, high level Elm API for doing exactly CSS.
[00:25:44]
It's not trying to change how you interact with CSS except for making it type safe.
[00:25:49]
So then within that CSS attribute that you can you can include in your list of attributes, you can have a list of CSS styles to apply. Now you can you can use those because it's regular Elm CSS.
[00:26:02]
But now you have these little generated definitions from your from Elm tailwind modules.
[00:26:09]
So you can use the generated modules, which are tailwinds utilities and tailwind dot breakpoints. And those give you helper functions that you can use within that list of CSS Elm CSS styles.
[00:26:23]
Yeah, exactly. And they're composable, too.
[00:26:25]
So like what that would look like if you were saying in dark mode for a, you know, at the medium breakpoint, I want to have a blue background, then what that would look like is CSS.
[00:26:39]
And it's a list of Elm CSS attributes. And then you would say dark, and then you give a list of dark attributes. And then you would say tailwind dot utilities dot or no sorry, tailwind dot breakpoints dot medium, or dot MD.
[00:26:57]
And you'd give a list of things for that, which it sounds like overwhelming that it's a list of lists of lists. But really, it's just this composable thing, which is really what tailwind is attempting to emulate is the experience of composing together these different utilities.
[00:27:13]
And it does that by a combinatoric explosion of every permutation being generated as a CSS class. But this is actually the ideal that it's striving to emulate, which is, it's just functions. And you can, and since it's composable, you can now do programmatic things with that where you, you can kind of pass around things and compose them together through code,
[00:27:33]
or abstract them or make your own small definitions for the group or a list of CSS you want to apply. For example, I have this uppercase button style in my project that I reuse. Yeah. And where you would before use some special CSS syntax that is added by tailwind in using post CSS, you can now use Elm definitions and have your type save code back.
[00:28:01]
Let me just get this straight. So Justin and Monty, whose name is actually Dean.
[00:28:07]
I think so, yes.
[00:28:08]
Yeah. So Justin and Dean's solution were to generate functions that alias the tailwind classes. But since, yeah.
[00:28:18]
No, no, no, not quite. It's the case for Monty's project, but it wasn't the case for Justin's project. Justin already didn't only alias the definitions, but kind of like inlined the styles. So it was actually generating, for example, mx underscore auto equals, and then CSS dot batch of margin left something auto and margin right auto, for example.
[00:28:43]
Yeah, because he was also targeting Elm CSS. So with Elm CSS, use the CSS function, which takes styles, which does not take classes. So if you want to pass in tailwind utilities to that, you need to have them translated to properties, right? So that's what you generate with your project.
[00:29:03]
And what Justin also did.
[00:29:04]
Yes. So my project is in the idea the same as Justin's. I just picked up the project and actually his code base, modified it, improved it and added to it until I felt it was ready for some of my projects I wanted to use.
[00:29:21]
So we mentioned before that tailwind uses purged CSS to do dead code elimination. So what it did was, as Dillon said, use a regex to find all CSS classes in your code base, which may have some false positives, false negatives, I guess.
[00:29:39]
Wait, don't regexes just work perfectly every time and never have any issues?
[00:29:43]
Well, yeah, except with typos.
[00:29:49]
I guess it can be both. Yeah, there's like the issue of what if you use some code to generate a class name. So what if you were doing the kind of abstraction of, yeah, I don't want to write this color name like this. I want to abstract my, I guess, color from my border radius, border color.
[00:30:09]
And you split up your strings. Exactly. If you split a string, your string won't be found by purged CSS and some classes will go missing and they won't be included in your production build.
[00:30:23]
That might be hard to debug.
[00:30:25]
Which, as you said, it's actually desirable in many cases to programmatically create like a composable way of defining something. And so you actually that's not purged CSS friendly. So having an actual generated programmatic API for this is really desirable.
[00:30:43]
If you don't want to purge CSS, the result.
[00:30:46]
You essentially have to. I think it's like over a megabyte. The default generated tailwind CSS asset, I think it's over a megabyte, maybe even several megabytes.
[00:30:56]
I think you quickly get in the megabyte range if you have the certain variants enabled and dark mode and stuff like that.
[00:31:04]
Because if you add dark mode, now it's applying something for every other class that existed. So it literally just doubles the classes. So if you had one megabyte, now it's two megabytes. Of course, minification might reduce that.
[00:31:18]
But even so, minification, even if it's minified and the minified asset is not twice as big strictly, you still have to parse and interpret all of those CSS classes once you've decompressed it. Plus, you have to actually decompress it.
[00:31:34]
So it's a lot of work for the browser engine to do that you want to avoid. Essentially, I would say it's necessary to purge CSS with vanilla tailwind.
[00:31:44]
But I mean, you can't use purge CSS if you do that concatenation.
[00:31:48]
Yes.
[00:31:49]
Even just if you don't do purge CSS, if you want yourself to find the CSS classes that you don't use anymore in your Elm code, just finding them manually in your editor, that becomes a problem. In your case, you don't need to do that anymore.
[00:32:04]
Yes. So right now, we don't generate any more CSS files, or at least it is possible to do this without generating CSS files, or that maybe later.
[00:32:14]
And you can rely on just your Elm code and the Elm compiler in turn with the minus minus optimize flag will remove every function that can't be called or won't be called by your Elm program.
[00:32:28]
So it does that code elimination and all of the classes you won't use in your Elm app, they won't appear in your generated Elm code because Elm figures out their dead code.
[00:32:40]
Yeah, yeah, that's really clever. I love how that design all just kind of works out.
[00:32:45]
I don't remember. Is it the Elm compiler that removes those functions or is it the minifier?
[00:32:50]
It's actually the Elm compiler. So what the Elm compiler does is just take your main function and look at all of the functions that are referenced by the main function, and then in turn, look at all the functions referenced by them.
[00:33:02]
So it basically just and it takes them and includes them in the generated bundle. Instead of like removing stuff, it just takes everything that is reachable code and transforms it and compiles it.
[00:33:15]
Yeah, it doesn't eliminate code. It just pulls only what it needs.
[00:33:19]
Also, the number of like the amount of code that's being generated is not a combinatoric explosion because the code you're generating is these composable helpers.
[00:33:29]
So it's not that for each variant of dark mode, you need to generate a class for each variant of breakpoint and for each variant within that of color.
[00:33:42]
You actually just generate a function, you know, tailwind.breakpoints.md, tailwind.breakpoints.sm, and those are generating functions that compose together and apply the media queries for those breakpoints, which is really, really cool.
[00:33:59]
And it makes it so much nicer to work with. And I know on the roadmap, Philip, you've got that you want to work on trying to take that a step further and actually extract out colors to be composable in that way as well.
[00:34:15]
Yes, exactly. So at the moment, we have lots of definitions for, for example, background color. And then you say bg underscore red underscore 700, for example, and the red underscore 700 part is your color name.
[00:34:33]
And it's taken from your tailwind configuration. And tailwind will just generate a class name, or in this case, in the end, a definition for each of your colors. And the idea is to only generate one definition, which will then be bg underscore color.
[00:34:51]
And it will take another parameter, which would then be, for example, red underscore 700, which would then be its own definition. So this way, instead of having like background color, border color, text color, and all of them, and each of them being defined for every of your colors defined in your tailwind configuration,
[00:35:14]
you end up only needing one definition for text, background, and border color, and all of your colors once. So this is the end goal.
[00:35:24]
That would probably reduce the size of the compiled code also, right?
[00:35:28]
Exactly. This would be even smaller code. At the moment, we don't generate variants. So if you like, generate these sm underscore underscore definitions for the small breakpoint, then my tool will spit out a warning and say, please just turn on variants, it makes your code a lot shorter.
[00:35:48]
So and you can use L and CSS to do the same. So use that. And this would be another way to make it even less code.
[00:35:56]
Yeah, I was just searching for the hover function, because I know that tailwind has that, but you're actually just using CSS dot hover from L and CSS.
[00:36:04]
Exactly.
[00:36:05]
And same thing for the breakpoints.
[00:36:07]
So the breakpoints are actually configured in tailwind in the tailwind configuration. So I use those breakpoint configurations to generate another file with your standard breakpoints, so to say.
[00:36:20]
Another file?
[00:36:21]
Yeah, exactly. Another module. So that's the tailwind dot breakpoints module. And it contains then, for example, in the standards or default tailwind configuration, it contains a function named sm, md, lg for the small, medium and large breakpoints and more.
[00:36:41]
And they take in turn just a list of styles to apply when you're at this kind of breakpoint.
[00:36:47]
It's not perfect right now. There's one thing that I really don't know how to fix. And this is something that Justin stumbled upon too, and which is kind of difficult to handle.
[00:36:58]
Tailwind CSS actually. So you have to be careful with the order of your styles when you write them down.
[00:37:05]
So you need with the L tailwind modules projects, you need to order your breakpoints from big to small if you're using min width breakpoints.
[00:37:15]
So that means if you don't honor this order, you'll end up having some styles overwrite other styles in cases you might not be used to when using tailwind CSS.
[00:37:27]
And this can be really surprising. And this is due to the fact that tailwind CSS relies on the generated tailwind to have a certain order of definitions.
[00:37:37]
So that the, I think, first definition that applies in the CSS file will be the one that the browser actually uses.
[00:37:44]
Either the first or the last, I don't actually remember. And this, yeah, this is kind of unfortunate and I don't really know how to solve this issue yet.
[00:37:52]
But there might be some way to use a clever set of helper functions and combination of that with the generated code where you could have like instead of the CSS, which takes a list of the CSS properties or styles,
[00:38:06]
you could have like a special tailwind one and you could have some special type instead of just being a direct CSS property.
[00:38:16]
You could have it be something that has like, you know, a tuple where one of the pieces of information is ordering information.
[00:38:22]
So if it's a breakpoint or if it's no breakpoint determines the order and then it applies those when you compose them together.
[00:38:29]
But it's a challenging technical difficulty there.
[00:38:34]
Yes. And again, I have to give props to Justin there because he took a stab at solving this issue and wrote a generated function.
[00:38:43]
So he added a function to the generated code, which would actually take a list of tuples.
[00:38:49]
So for each breakpoint, you can define a list of styles and then it would order the breakpoints automatically.
[00:38:57]
And I think that was a step in the right direction. It just felt a little bit too clunky and a little bit too verbose for my taste.
[00:39:05]
And there's like the question of how do you want to group your code if you, for example, have a list of states,
[00:39:13]
styles to apply when you're hovering an element and you want to have this list of styles change when you have another breakpoint.
[00:39:22]
So you think about which one is wrapped around the other.
[00:39:26]
But yes, so it's I'm not entirely sure how to do that yet.
[00:39:30]
It certainly makes it less composable. And then if you wanted to do like CSS dot batch,
[00:39:35]
which is a function that comes with Elm CSS that allows you to group together multiple styles.
[00:39:41]
Now they would wouldn't be styles, they would be tuples. And so you couldn't use things like CSS dot batch.
[00:39:46]
Like that's part of the beauty of this approach is that you're just generating these Elm CSS properties.
[00:39:53]
And so you can compose them together and use them just like a first class citizen in Elm CSS.
[00:39:59]
I wonder if there would be some sort of like post processing that could be done like with Elm review or something that you could have it order things.
[00:40:08]
The problem is if you're using if you're calling out to functions and things like that, it could become difficult if you're concatenating lists and things like that.
[00:40:17]
Yeah, this is always the problem with static analysis.
[00:40:20]
My thing is that instead of having every property be a CSS style, you could probably have an intermediate type.
[00:40:27]
And then when you try to transform it into a CSS property, then you do all the nice things.
[00:40:32]
And maybe if you do that, you can also make it work for Elm HTML.
[00:40:37]
But that would be a bit more tricky because of how the CSS classes are handled. Maybe not.
[00:40:43]
You could fork Elm CSS and make a drop in replacement for Elm CSS, and that would allow you to include extra pieces of metadata in your CSS styles.
[00:40:54]
That would actually probably solve the problem pretty nicely.
[00:40:57]
But then now you have like this is something that sometimes happens in the Elm ecosystem is like you have like this is a drop in replacement for this.
[00:41:06]
This is a drop in replacement for this. It's like Elm accessible HTML.
[00:41:10]
And then there's Elm CSS. And what if you want to use Elm CSS with accessible HTML?
[00:41:16]
They don't really compose because you keep like creating these drop in replacements for things.
[00:41:20]
Absolutely, yes. And this is something I was thinking about.
[00:41:23]
But exactly. I don't want to make yet another competing project, I guess, which is not compatible.
[00:41:29]
And I think I didn't think of Jeroen's idea, actually, which I think is a good probably a really good idea to have.
[00:41:37]
Oh, right. This intermediary library, I guess, which would allow you to sort your definitions by some kind of internal key.
[00:41:48]
And that would solve the issue. The solution is always to add more types.
[00:41:52]
Yeah, I think there's like this one quote, every problem in computer science can be solved by another layer of indirection.
[00:41:58]
Except the problem of too many layers of indirection. Exactly. Yes. Except performance problems.
[00:42:05]
So what would be the reasons for using Elm UI or using Tailwind?
[00:42:10]
Or any other variants like Elm HTML?
[00:42:13]
Yeah, so I think like at the very core, Elm UI and Elm or Tailwind are solving similar issues,
[00:42:21]
which is trying to make the developer experience of using styling applications in the web better.
[00:42:29]
So then, yeah, it's the question when do you use one or the other?
[00:42:33]
And I think Elm UI is really useful for people who don't know or don't want to know or don't have to know how to use CSS yet,
[00:42:42]
because it really is much simpler, though I haven't used it much, I have to say.
[00:42:46]
When I started, for example, writing my blog, I was thinking about using Elm UI and it was and it is the default for Elm pages.
[00:42:53]
The Elm pages start right now, right? That's true. I actually have an Elm pages Tailwind starter repo,
[00:43:00]
but it's out of date and I'm planning to update it to use Elm Tailwind modules.
[00:43:04]
Oh, yes. Great. But there's, for example, there's some benefits to using CSS compared to Elm UI, which would be, for example,
[00:43:14]
the way you can use media queries for different device sizes and I guess some smaller things,
[00:43:21]
which would be like newer features in CSS, which have to be added in Elm UI first.
[00:43:27]
Like CSS Grid, for example.
[00:43:29]
Exactly. CSS Grid or I don't know, like things like focus within or stuff like that.
[00:43:36]
Maybe I'm not sure. Maybe I'm wrong here. And this is actually already implemented. But yeah, CSS Grid would be an example.
[00:43:43]
So when I was using when I was writing my blog, I was also thinking about this question,
[00:43:48]
should I use Tailwind or should I use Elm UI? And it ended up being Tailwind because I need the breakpoints to work correctly and without JavaScript in that case.
[00:43:57]
So with media queries. Yeah, I'll give my personal experience report as somebody who's used both a fair amount.
[00:44:05]
I've done a lot of Elm UI. I've done a lot of Tailwind.
[00:44:09]
And so with Tailwind, some of the things that are kind of challenging coming from Elm UI are that you do have to think about CSS and not pretend it doesn't exist.
[00:44:22]
You can pretend that certain parts of CSS don't exist, which is great.
[00:44:27]
And it's in general a way smoother experience than what I've had personally trying to just write, you know, CSS files and SCSS files.
[00:44:35]
But you still have to understand CSS and how things work. Right.
[00:44:40]
You do. You still have to understand flex and flex grow and flex shrink and item center and justify around.
[00:44:50]
Or, you know, all of these things you have to. And I find myself constantly having to look them up.
[00:44:56]
The Tailwind CSS documentation is great, but Elm UI has like a clean slate and it can say, OK, if we were to imagine CSS from scratch, what terms would we use to describe these things?
[00:45:07]
And it's beautiful. You know, you don't have to like remember the to put flex in the right place.
[00:45:13]
And you just say spacing on the parent. And it's it's beautiful.
[00:45:18]
The pain points using Elm UI for me have been one of the most important things for making a polished professional web design is having it be very mobile friendly and responsive.
[00:45:30]
And I've found that to be very difficult to do with Elm UI in my personal experience, because a couple of things.
[00:45:36]
One, I I want it to pre render nicely, which there's just no no story for in Elm UI at the moment.
[00:45:44]
At some point there may be. But right now there's no way to do that using media queries, which means it's not going to pre render well, which we discussed on our Elm UI episode.
[00:45:53]
And the second thing is you have to like wire through state of the current browser dimensions in a lot of places to do it.
[00:46:00]
And I just prefer a more declarative way of saying at this break point to this at this break point to this.
[00:46:05]
So Tailwind is amazing for that. Tailwind is amazing at having like a palette of options and being able to just.
[00:46:13]
That's just a feature that's baked into Tailwind and it works very nicely with Elm Tailwind modules.
[00:46:18]
So that's a killer feature. That's not something that is impossible to do in Elm UI.
[00:46:23]
And at some point maybe we'll have a cool tool for like building a palette, which would be great.
[00:46:28]
Also, Mini Bill Leonardo built a package that wraps Elm UI.
[00:46:34]
Again, it's a drop in replacement that does a slight twist on something.
[00:46:38]
And what it does is it allows you to have state that you can call into without passing arguments through every place that you're calling your Elm UI functions.
[00:46:47]
So you can have state like the current width to do conditional breakpoints and things like that.
[00:46:54]
So that alleviates that pain point a little bit, but still it doesn't pre render nicely.
[00:46:58]
But the other killer feature of Tailwind CSS is Tailwind UI.
[00:47:04]
Tailwind UI is like a paid like component library of these copy pastable snippets of Tailwind HTML with CSS classes.
[00:47:15]
And it is so easy to make like professional landing pages and application designs using that.
[00:47:22]
And that's one of the killer features, I would say, of using Tailwind in general.
[00:47:26]
But that is not compatible with what Philip did because you can't take that Tailwind UI and put it into your Elm projects, right?
[00:47:35]
Yeah, not directly. So it's just HTML with CSS class references in strings.
[00:47:42]
So you can use it as a drop in. But...
[00:47:46]
But, well, yeah, right. So it's just these templates basically, right? It's just templates of HTML code.
[00:47:53]
And I so when I was building the Elm, ElmTS Interop.com landing page, I built it with Elm Tailwind modules, which was an amazing experience.
[00:48:02]
I was very pleased. And I discovered that I was copy pasting and, you know,
[00:48:08]
tweaking the the Tailwind UI templates, HTML templates so often that I would save myself time if I built a tool that automatically parsed them.
[00:48:17]
So I built I built a tool which is at HTML to Elm.com.
[00:48:24]
And it has the ability to parse Tailwind CSS classes and give you the same format that the generated code from Elm Tailwind modules uses.
[00:48:34]
So so all you do is you, you know, copy paste your template from from Tailwind UI.com.
[00:48:41]
And it will give you something that you can just paste into your Elm Tailwind modules project.
[00:48:47]
And it just works. And it's quite nice. And I built an Elm review rule, which maybe will be hopefully it'll be published by the time this episode is released,
[00:48:57]
which allows you to do debug to do. And then you can do a triple quote string and paste in that HTML template from Tailwind UI.
[00:49:06]
And it just generates all of the code that is now going to be compiling working Elm Tailwind modules code.
[00:49:12]
So it definitely saved me time in the process of building it, or at least was more fun than manually going in and doing that.
[00:49:21]
That's a more truthful answer. I have to thank Dillon that in the future when I'm using Tailwind UI components, I'm going to have more fun because yes,
[00:49:31]
I won't have to, yeah, fiddle with the HTML and transform it into Elm code and then fiddle with the classes and transform them to Tailwind utility classes.
[00:49:41]
And that's great. Yeah. I didn't get that you did this for Tailwind UI, but when you started talking about it was oh, yep, that makes sense.
[00:49:51]
Yeah, it even it even will do things like, you know, if you have hover colon BG blue, right, then it's actually going to create CSS that says CSS dot hover,
[00:50:05]
not Tailwind dot utilities dot hover, because that's how it's generated. So it handles all of those special cases and gives you compiling code.
[00:50:13]
It definitely enhances the experience. It just like it's no fun to like have in the middle of your workload to have to go manually tweak a bunch of stuff.
[00:50:23]
Yeah. So just to clarify, it's HTML to Elm dot com separated by dashes. So HTML dash to dash Elm dot com.
[00:50:31]
Yes, we'll put a link in the show notes. Yeah. So did we answer the question about Elm UI versus Tailwind?
[00:50:37]
Right. I remember I wanted to say something about that. I think ultimately I'm having like dreams of there being the ultimate M.U.I. in the end,
[00:50:47]
because I see lots of similarities between Tailwind and M.U.I. even if like you have to flinch a lot.
[00:50:55]
There is things like, for example, the spacing utility in Tailwind, which they just use space underscore X underscore eight, for example.
[00:51:05]
And it'll add, I think, yeah, 32 pixels of spacing between your children elements.
[00:51:11]
And I guess this is something similar to using a row or column in Elm UI and adding the spacing modifier.
[00:51:19]
And it's an abstraction over CSS. So in the end, it's both is actually CSS code, but you're using the same API.
[00:51:25]
So they are having similarities. And if you start to abstract, if you start to abstract Elm Tailwind modules like what I'm planning in the future,
[00:51:34]
it's going to be even similar when you're using background color and just putting in a color you have to find somewhere else.
[00:51:43]
It's going to start to look very similar. And it makes me dream of this ultimate M.U.I. I guess,
[00:51:50]
which has all of these features and maybe works with media queries.
[00:51:54]
And then in the end, maybe you don't even have to think about Flexbox anymore and how stretching and growing and shrinking works.
[00:52:03]
And I think that would be that's that's something awesome.
[00:52:06]
So I think of Tailwind and Elm Tailwind modules more as a compromise between having all of the, in quotes,
[00:52:15]
advanced features available today, but moving into the same direction or but approaching from a very different angle that Elm UI does.
[00:52:25]
Yeah, I think that makes a ton of sense. Like one concrete example of how you might translate that, that just to give people an idea of what that experience might feel like,
[00:52:35]
would be instead of having, you know, Tailwind.Utilities.Flex, which is actually just display flex under the hood,
[00:52:44]
you would not generate Tailwind.Utilities.Flex, but instead you would have a function, you know, and it would be like for a flex tag.
[00:52:53]
Although that does become interesting now because then now you get into the same thing as Elm UI, right?
[00:53:00]
Where Elm UI separates the notion of like containers and their markup.
[00:53:05]
So if something is the term in Elm UI is a region. So if the region is main or nav bar, that's like the semantics and the semantics are separated from the styling.
[00:53:20]
So you would have to get into that territory a little, which I mean, there's really no reason that you couldn't,
[00:53:25]
that you couldn't take that same direction of having it be an attribute to define the region, the semantics of the HTML elements.
[00:53:34]
But if you have an API like that, now flex becomes something where like you say, OK, flex,
[00:53:40]
and you can create this nice experience where you can define within that the different variants that you could have for centering things
[00:53:47]
and having the spacing and you could have a higher level API for saying those things.
[00:53:51]
Yeah. One thing I was thinking about is there is, for example, the Tailwind CSS definition of flex and flex row and flex call.
[00:54:00]
And I was thinking, well, why don't flex row and flex call just imply display flex?
[00:54:05]
And once you use that, why even flex dash row? Why not just row and call?
[00:54:10]
And then you start to have more similar APIs between Elm UI and Tailwind.
[00:54:15]
Yeah, Tailwind already tries to abstract those things to a certain extent. So it's just taking it a step further.
[00:54:21]
So one thing that we didn't mention, think, is that because you now have functions or constants to define your CSS,
[00:54:29]
you can use your editor to auto complete those definitions. Right.
[00:54:33]
So you can do a tag, square brackets, tailwind.
[00:54:38]
And then it gives you auto completion for saying background red 500 or something.
[00:54:43]
Yes, that's true. And there's even tooling and there is tooling for Tailwind CSS in general to give you auto complete
[00:54:52]
when you're writing a string and you're starting with something that looks like a tailwind class.
[00:54:58]
But this project, you don't need another tool which will need to understand Elm code, but instead use the normal Elm tooling.
[00:55:05]
And one thing, one other thing I haven't talked about too much in public yet is I've added documentation generation
[00:55:12]
to the Tailwind modules tool so you can now generate document or doc comments together with your generated code,
[00:55:20]
which will include the CSS that was consumed to generate these definitions.
[00:55:26]
So when you hover over a Tailwind generated Tailwind utility class constant, you will in your tooling,
[00:55:34]
if you have tooling installed, Elm tooling installed, will see in its documentation what CSS was consumed
[00:55:40]
for generating this and in essence will apply when you use this class or this constant.
[00:55:46]
Nice. I hope that doesn't make the file much, much bigger.
[00:55:50]
It does. In fact, it does, unfortunately. But I hope in the future it's going to become smaller anyway with less and less definitions.
[00:55:58]
Do you know how many definitions you create with your generation at the moment?
[00:56:02]
I don't know. It depends. It depends a lot on your Tailwind configuration.
[00:56:08]
So, for example, do you have dark mode on or off or do you have variants on or off?
[00:56:12]
I don't know what the values are right now for the default.
[00:56:16]
I just checked and it's about 3600 for the Elm default Tailwind modules.
[00:56:22]
Yeah. So it's a lot of definitions right now.
[00:56:24]
It's still quite a lot.
[00:56:26]
Wait, is that lines of code or number of definitions?
[00:56:29]
Number of definitions.
[00:56:30]
Really? It's going to become a lot smaller when you extract out the colors because the permutations for the colors is a lot.
[00:56:37]
Also, just if you reduce your palette. So to reiterate something we talked about earlier,
[00:56:42]
when you go to your Tailwind configuration file, you can define your custom palette, which is a best practice.
[00:56:48]
You should do that. And when you do that, it's going to reduce the number of permutations by quite a bit.
[00:56:55]
You can even half it if you only define dark mode.
[00:56:59]
Yeah. And if you I mean, the number of built in color variations is like like 30 or 40 or something, right?
[00:57:07]
So if you if you have like eight colors instead, then you've drastically reduced the number of defined top level values.
[00:57:16]
And I imagine you could do the same thing with spacing, too. Is that something you're thinking about?
[00:57:21]
Yeah, exactly. Both of these things. And I think spacing might even have way more.
[00:57:27]
Yes, I guess effect or an even more drastic effect because it applies in more places.
[00:57:32]
Yes. So it has the permutation like the combinatoric explosion in more places.
[00:57:37]
Yes. But I still have to think about how to best do this.
[00:57:41]
It's more straightforward with colors than with spacing, because then you're thinking about, hmm,
[00:57:45]
should you introduce an int argument to your definitions or will you generate some kind of type
[00:57:53]
which encapsulates all of the possible spacings to reduce the set of them you can pass to your definition?
[00:58:00]
Because I think this is a feature of tailwind CSS.
[00:58:03]
Absolutely. Yeah. Having it be a restricted palette is like a huge feature.
[00:58:07]
And also like the the tailwind team in in tailwind itself and in the paid pro tailwind UI, like library of templates.
[00:58:17]
They've put so much thought into the designs.
[00:58:20]
They've done so much research and and tweaks things. And it's very reliable in terms, you know, just like should the, you know,
[00:58:28]
the default font size be 16 pixels and then should you have like 14 or 12 pixels for certain, you know,
[00:58:35]
text around forms and things like that. And they really put so much thought into the spacing and the font sizes and all these things.
[00:58:42]
So the palette is like one of the most compelling features of using tailwind.
[00:58:47]
Absolutely. And one thing I really like when working with tailwind is that it's written by people who've done a lot of HTML and CSS development and design.
[00:58:57]
So I think the two heads of tailwind CSS are our like the founders, I guess, were Adam Waethin and Steven Schroeger.
[00:59:05]
And they also wrote a book which I read and really, really recommend this refactoring UI.
[00:59:11]
So this was something for me as a mostly programmer, which would inspire me to try more design and to get better as visually making things aesthetically pleasing.
[00:59:21]
So what are some best practices when using Elm Tailwind modules?
[00:59:26]
We talked about like narrowing down your palette in your tailwind configuration to reduce the, you know, color palette that you're using.
[00:59:33]
You know, you can reduce the breakpoints if you don't use all of the breakpoints.
[00:59:38]
For example, there are some things like that that just sort of constrain things to help you build more consistent designs.
[00:59:46]
What other best practices do you think help you build nice designs and nice code using Elm Tailwind modules?
[00:59:54]
Yeah, one other thing, and I think I mentioned that the command line interface will also tell you is just turn off your variants and completely use Elm CSS for that.
[01:00:04]
And I mentioned it's order your breakpoints accordingly, otherwise they don't apply.
[01:00:09]
And then there's like a big question of what's important to use, I guess.
[01:00:15]
So I personally have an interesting way of using the tailwind modules project and writing HTML code myself in Elm, which is I usually write modules which read like HTML templates.
[01:00:32]
They don't contain any logic.
[01:00:35]
The most complicated thing it contains is maybe list.map.
[01:00:39]
Even that's very rarely.
[01:00:41]
And other than that, it's also very light on dependencies.
[01:00:45]
And these modules only depend on HTML.
[01:00:48]
So the HTML.style module, the HTML.style.attributes module, the tailwind modules and maybe an icon set and stuff like that, but not on actual logic code.
[01:01:01]
And the types are mostly HTML and attributes which might even get passed in or passed out.
[01:01:09]
So it's basically all everything is like with HTML templates and the outside code then has logic to use it.
[01:01:18]
And what I end up doing in these modules to make them more readable, and that's the whole point for them in the first place, is I use explicit imports, which is very unconventional in Elm.
[01:01:29]
Do you mean like unqualified imports?
[01:01:31]
Yes.
[01:01:32]
Like exposing dot dot kind of?
[01:01:34]
Oh, yeah. Yeah, not explicit.
[01:01:36]
Yeah, I'm using the exposing dot dot imports.
[01:01:39]
Exactly. And I see Jeroen shaking his head at me.
[01:01:45]
I'm going to get a game of the stink eye.
[01:01:48]
Jeroen is the personification of Elm review telling you, I think you might want to fix this.
[01:01:55]
I don't even have an Elm review rule for that.
[01:01:58]
It's so uncommon.
[01:02:00]
But it's just the gaze.
[01:02:02]
Oh, no, actually, I do have a rule for that.
[01:02:06]
I actually have one that fixes it. Just use that.
[01:02:11]
All right. Yeah. So I like doing that because it reads more similarly to, for example, Tailwind UI templates that you see.
[01:02:20]
And I think if you really restrict yourself to only using HTML and CSS in those template modules,
[01:02:28]
you don't start to have this question of, well, is this definition defined in this module or in another module or where is it defined?
[01:02:37]
But you really can, you should or you mostly know like section is an HTML element.
[01:02:43]
And you see there's maybe an MX underscore auto in there and you see, yes, this is a tailwind class.
[01:02:52]
And so these there's less of an issue with that there.
[01:02:55]
It is still something unconventional. I see that.
[01:02:58]
But I think there's even more value behind separating these kind of template classes from the rest of the code.
[01:03:07]
And I think this is the biggest thing, maybe.
[01:03:09]
But yeah, this is how I like to work with it.
[01:03:12]
I haven't developed any other best practices yet.
[01:03:15]
And I would say this is a developed best practice yet at all.
[01:03:19]
So basically what you're just doing is you're defining reusable view functions.
[01:03:24]
Yes.
[01:03:25]
That you just, yeah, you can compose them together.
[01:03:27]
I think of them like, I think in React, people usually like to split their React components into presentational components and functional.
[01:03:36]
No, I don't know what the other one was.
[01:03:38]
I remember smart and dumb components.
[01:03:41]
I think they renamed it, but all right.
[01:03:43]
Presentational and logic based.
[01:03:46]
Yeah, basically I try to do like some kind of similar splitting where I have these modules which have no logic and only care about your HTML and CSS fitting in nicely together with each other.
[01:03:59]
Yeah, I definitely like that idea a lot.
[01:04:01]
As far as like the unqualified imports, like I don't have any problem with people doing that.
[01:04:08]
But for my own personal preferences, I really like auto completion and it's just like inseparable from my workflow.
[01:04:16]
Like my brain will cease to function if I don't have that feedback mechanism at play.
[01:04:22]
And so I just write like module name dot.
[01:04:25]
That's how I write code.
[01:04:27]
And my brain would stop working if I didn't have that.
[01:04:29]
So, you know, for that reason, I might like do a short import alias or something like that in that context.
[01:04:35]
But otherwise, I think the idea of separating, you know, these nice like view helper modules is a really nice pattern.
[01:04:43]
And like one one thing I want to add to that is that it can be really helpful, like in the object oriented space.
[01:04:50]
People talk about these view objects.
[01:04:52]
I'll post a link that talks about that.
[01:04:54]
I think it's a nice pattern that applies in functional programming as well, which is having like the logic for presentational aspects of like,
[01:05:03]
how do you present given a user data object?
[01:05:06]
How do you present that user's name?
[01:05:08]
How do you present, you know, all these different strings that you need?
[01:05:12]
And at that point, you build up this data type, maybe just a record with a bunch of fields that are pretty much strings.
[01:05:18]
Right. And one of the cool things about that is if you're doing unit testing, now you can have a unit test that doesn't pull in HTML to test your views.
[01:05:27]
You can test those view, you know, little records.
[01:05:31]
And you can have a nice decoupled thing that has the presentational elements and gives you all the little bits of data that you need.
[01:05:39]
And then you can have kind of dumb view helpers that don't have to have all the knowledge of how to how to build that up.
[01:05:47]
So that's one pattern that I think can be helpful for for using that approach.
[01:05:51]
Yeah, that sounds nice. It sounds really similar to what ends up happening in this kind of way of working.
[01:05:57]
That's what I was imagining, that that's something that you naturally are going to do if you're going to have these dumb things that basically just use list dot map and are otherwise dumb.
[01:06:06]
Another question I had is, do you do you use or can you use at apply with Elm tail end modules?
[01:06:13]
And would you would you even want to or would you use Elm abstractions to do to get that effect?
[01:06:19]
Oh, yeah. Right. This gets back to another best practice, I guess.
[01:06:22]
You can use at apply. So for those that don't know, at apply is something that a kind of syntax in CSS, which gets introduced when you use tailwind CSS.
[01:06:32]
Yeah, it's like a special tailwind directive for like smushing together utility classes for like at apply.
[01:06:39]
And you create a button that has this background and this hover class and whatever.
[01:06:44]
Exactly. And the best practice in our case would then be don't use it.
[01:06:48]
Just define these things in your own land. But it also depends very much on your use case, unfortunately, because some teams, for example, use the same CSS and tailwind configuration that they use for other pages, which would, for example, be statically rendered next.
[01:07:06]
For example, and then they end up not well being able to import these definitions.
[01:07:11]
So it's also about interoperability there.
[01:07:15]
So I guess if you can, it's best to use the Elm CSS definitions and use it within your own code because it's much easier and less prone to maybe some mistakes in translation when generating the Elm code if you had used it in CSS directly.
[01:07:32]
But it is possible. And yeah, it is possible to use at apply in your CSS and have it generate some custom code.
[01:07:42]
Yeah, I sort of had a similar philosophy with Elm GraphQL, which was like as much as I could do with Elm high level abstractions, I would prefer that over GraphQL abstractions.
[01:07:55]
You know, so like GraphQL has has this notion of variables that you can pass parameters into things.
[01:08:02]
But Elm has parameters and functions, so prefer using those over sort of GraphQL domain concepts when possible, because it becomes like a more high level abstraction that's more idiomatic to Elm.
[01:08:15]
And you can leverage all the features like you can refactor refactoring tools in your IDE and you can refactor static analysis tools like Elm Review to help you analyze dead code.
[01:08:27]
And, you know, it just becomes a really nice experience.
[01:08:31]
And also, it's less moving parts. So when you're using a generator, you would draw and you can like not use it instead.
[01:08:40]
That's one less moving part in that case. So it's always what I recommend. The best code is the code that was never written, I guess.
[01:08:46]
Yeah, yeah, absolutely. Yeah, I love that it's just CSS dot hover instead of generating a special tailwind one because you could just compose those together.
[01:08:55]
I love how you say the best code is the one that has not been written and you generate a giant file with 2600 functions.
[01:09:04]
I'm super guilty.
[01:09:09]
Another thing, like when when working with tailwind, I believe a lot of people who work with tailwind have this experience that they rely heavily on the docs and on tailwind UI in particular.
[01:09:21]
It's a paid product. But if you use tailwind a lot, it's so helpful.
[01:09:25]
It's like my Bible for it because the cool thing about it is it gives you these templates.
[01:09:30]
But it's not like bootstrap templates where it sort of gives you a style where it's like a dark, dark template or a light template.
[01:09:38]
It's like a starter point that you just tweak as needed.
[01:09:42]
And so you can build off of it and turn it into something like not every tailwind site looks the same, whereas with like Twitter bootstrap, every site looks the same.
[01:09:52]
And then maybe you can tell that something uses a particular Twitter bootstrap template, but it just changes the color scheme and fonts.
[01:09:59]
Do we have to mention that we were not sponsored or?
[01:10:02]
I know. Maybe or maybe we should just ask them to retroactively sponsor us.
[01:10:08]
Sounds good to me.
[01:10:10]
So that's just super handy is using those templates and then you can tweak them and make it look like it's something you built a customized thing.
[01:10:19]
Not like it's not immediately recognizable because you can turn it into your own look and feel.
[01:10:24]
But when you do that, you end up with all this boilerplate code because they repeat things constantly because it's just HTML.
[01:10:31]
So you'll have like, you know, if you copy paste one of their nav bar examples, then you'll have like, you know, an unordered list of list items with like 10 list items for all the nav bar content.
[01:10:45]
And the same tailwind CSS classes are applied to all of the unselected nav bar elements.
[01:10:52]
And then the current page that's on has a specific one because it's not like a JavaScript framework.
[01:10:57]
It's just HTML.
[01:10:59]
So then you have to sort of reverse engineer pulling that up into abstractions.
[01:11:04]
So that's another best practice for people to keep in mind is I would say like start with just getting that template on the page and then reverse engineer it through refactoring.
[01:11:16]
Like you can listen to our Elm Radio incremental steps episode and that's the approach I would recommend for incrementally refactoring to pull up those abstractions.
[01:11:26]
But don't try to do that, like at the same time to refactor and extract those abstractions as you're getting your layout working.
[01:11:34]
Get it like rendering and then little by little extract out parameters and create abstractions.
[01:11:41]
I've felt that I wanted to abstract things also very often.
[01:11:45]
I was thinking, hey, I've used the same parts of the same set of table and UI classes in multiple places.
[01:11:52]
Maybe I should abstract it, but it should be really painfully aware that you have to abstract this when you're doing this.
[01:12:00]
So in the end, I often ended up still switching some parts and then they don't match up exactly and abstracting gets hard.
[01:12:07]
But yeah, starting from these concrete examples, especially intelligent UI, which is great.
[01:12:12]
It's really useful and helpful.
[01:12:15]
And then once it really gets painful and you really think this gets repeated a lot, then you can start abstracting and grouping, for example, some CSS utility class.
[01:12:27]
Right. That's a great point. I think Adam Wethin talks about this a lot, like on his on Fullstack Radio, his podcast.
[01:12:35]
He it's cool because you can sort of get into his brain a little bit and hear the way he thinks about these things.
[01:12:41]
And so I'll post a few links in the show notes to some episodes where he talks about the philosophy behind Tailwind CSS.
[01:12:47]
But one thing that comes up often is he talks about how people have this fear of, wait a minute, all these utility classes in line in my HTML, it's going to be a terrible experience.
[01:12:58]
And he's like, well, I mean, if you have like 15 utility classes on your nav bar, but the nav bar is the only one that has that specific set of 15 utility classes, it's fine.
[01:13:10]
You don't need to abstract that. And people at first are horrified and they're like, surely I have to abstract this.
[01:13:16]
But he's like, well, if that's the one place it shows up, it's abstracted in this night.
[01:13:20]
You know, maybe you have a React component or, you know, an Elm module or whatever for your nav bar that abstracts that.
[01:13:28]
That's fine. Just leave it there with those 15 Tailwind utilities in line. It's perfectly fine.
[01:13:34]
I think Tailwind CSS land, lots of people have a similar reaction to the at apply functionality.
[01:13:40]
And they ask like, when should I start using at apply and like making my own, for example, button class?
[01:13:47]
And the answers are very similar. And they're all like, wait it, wait for it. Just leave it like it is and do it as late as you can.
[01:13:56]
And it really, most people, I think, try to abstract too early and that applies to at apply or grouping CSS Tailwind utilities with Elm CSS.
[01:14:07]
Well, great stuff. I think we've given people a lot to chew on here. And thank you again for building this really lovely tool.
[01:14:16]
It's been delightful to use. I've enjoyed it quite a bit. Thank you.
[01:14:20]
So, Philipp, how should people get started with the Elm Tailwind modules?
[01:14:24]
I think the best way would be to, I think it depends. It depends on whether, okay, so there's two kinds of people who I guess want to use on Tailwind modules.
[01:14:36]
One is the people who are using, who've been using other Tailwind and Elm things before and want to switch to this or who have only been using plain Tailwind CSS and want to switch to this.
[01:14:49]
And there's one kinds of people who, for example, at work have been using Tailwind CSS before and want to switch to something with more auto completion, more type safety, maybe.
[01:15:01]
And there's the other camp with people who want to try out Tailwind CSS and love Elm.
[01:15:08]
And for the second crowd, I would suggest using Elm default Tailwind modules, which is the Elm package with the generated code with the default Tailwind configuration.
[01:15:19]
So then you can get a feel for how Tailwind itself feels and don't have to use any JavaScript and fiddle with bundling and NPM or anything like that.
[01:15:30]
You don't even have to use NPM install. You just install this package.
[01:15:34]
And later you can switch to the code generator itself and write your own Tailwind config once you want to use your own colors or some custom fonts, things like that.
[01:15:45]
And for everyone else who is already accustomed to Tailwind CSS and maybe has their own already has their own Tailwind configuration file, just check out the readme on GitHub.
[01:15:57]
It explains how to use the command line interface tool.
[01:16:01]
And if you have a really, really advanced use case, you might even use the node API.
[01:16:08]
So how to get started really depends on who you are. I try to have include like everyone include people who want to get started as quickly as possible.
[01:16:19]
And I want to include companies who have complicated structures with their own Tailwind configurations and their own added CSS and post CSS configurations and things like that.
[01:16:33]
It's hard to like get both under the same. It's hard to do your best for both.
[01:16:39]
But I guess the best starting point is always the readme and the GitHub project.
[01:16:43]
Do you think that an incremental upgrade would work well for people who are already using the regular style of Tailwind with Tailwind classes in their Elm code?
[01:16:53]
Using Elm HTML, you mean?
[01:16:55]
Oh, good point. I guess. Aha. That's interesting. Right.
[01:16:59]
So if you're using Elm HTML, you would first have to switch to Elm CSS, which you could do. But and I mean, it's a drop in replacement. So you basically change your imports to change from Elm HTML to Elm CSS because it's it's a superset of the API.
[01:17:17]
Yeah, that's a good point. I think Elm CSS is quite it's quite possible to use to introduce Elm CSS incrementally.
[01:17:25]
So there's like from unstyled and to unstyled. So you can go from the Elm CSS world into the normal Elm slash HTML world and the other way around.
[01:17:37]
So I guess the best way for people who have lots of Elm slash HTML code would be to add the drop in replacement module by module.
[01:17:46]
Right. Starting at the leaves.
[01:17:48]
Exactly. Starting at the leaves.
[01:17:50]
In the meantime, you'll have lots more generated code. But in the end, you hopefully have a have the normal amount of code again.
[01:17:58]
Right. And you should be able to take like, you know, an Elm CSS element that is using like the class syntax to do tailwind classes and then take that one HTML element that's in Elm CSS and change that to use Elm Tailwind syntax.
[01:18:17]
Right. Yes.
[01:18:18]
Elm Tailwind modules. Yeah. So incremental upgrades would probably be the way to go.
[01:18:22]
You could probably have some kind of tool maybe using Elm review or Elm HTML to Elm CSS.com.
[01:18:34]
Right.
[01:18:35]
To help with that.
[01:18:36]
I did think about the possibility of having like a transformer that would allow you to turn class names as an Elm review rule into tailwind using the parsing logic that I have.
[01:18:48]
The tricky thing is if you have a mixture of classes that don't represent classes that don't represent tailwind and classes that do represent tailwind, then it can get a little bit messy because you don't know how to separate them.
[01:19:00]
But that's maybe a topic for another day.
[01:19:03]
But great. So try out the default package, which is published on the Package Repository, Elm default tailwind modules.
[01:19:11]
Find the link in the show notes. Check out the readme. Check out HTML to Elm.com perhaps. Give that a try. And, Philipp, thanks again for coming on the show.
[01:19:21]
Thanks again for having me. It was lots of fun.
[01:19:23]
Likewise. And Jeroen, I'll talk to you next time.
[01:19:26]
See you next time.