One tends to think of parsing/compiling as a fairly esoteric skillset that a relatively small number of programmers ever actually need at their jobs, but it always makes me glad to see examples like this where a little DSL was a genuine boon to productivity in a real product. Once you know how to do it, you start to notice more and more little opportunities where it could be useful.
Now as a self thought dev I haven’t really been shown in school what those were and always thought of them as some wizardly magic that only compiler writers dabble in.
But after that tweet I looked around and discovered https://github.com/dmaevsky/rd-parse which is a 100-ish line library, and using it you can do https://github.com/dmaevsky/rd-parse-jsexpr/blob/master/src/... which is a 200-ish lib that parses all of freakin JS expression syntax.
Now whenever we have various custom unwieldy regexes to parse some input, I can refactor that into a clean and easily understandable parser.
I guess thats why you need at least a little bit of formal education in CS.
It seemed bizarre to me that it was considered an appropriate solution. The end users were network engineers so they were _able_ to write the regexes but it seemed obvious to me that providing the user with a higher level parsing library would be much easier. It was something I wanted to POC but never found time in the end.
But I always dreamed of making a programming language so one of my pandemic projects was to read Crafting Interpreters, and ever since then I've been a little bit obsessed
That's a great point about regexes! I'll have to keep that one in mind
But that said, I had a good usecase of my own a couple months ago where a little s-expression-based DSL was just the ticket to open up some data analysis functionality to internal users without having to prematurely build out a super complex, much more rigid GUI. DSLs are really just small, hyper-flexible user interfaces. Once you frame it that way, there are many applications. (The drawback of course is discoverability/feedback, which GUIs excel at, so you have to balance the two for your usecase)
Parsing is the easy/not so interesting part of a compiler.
So while looking back it may seem like a gentle incline, to a lot of us looking forward it is a steep cliff.
This part from the blog post kind of confused me a bit, though.
"The promise of Tailwind UI is that it's just a code snippet"
Am I the only one who never looks at the code snippets/tab for Tailwind UI, and instead jumps straight into inspect element? As blueprints the components are great, but I almost always want to make some small tweaks or changes to fit my application. Are there other people out there actually copying/pasting the snippets as they are?
<Button className="bg-gray-100 rounded-full flex" ...
How do you remember the names of all your classes? Should your IDE know about these classes? Can you autocomplete class names? Where's the import statement for these classes so you know where they're defined and what they do. There's a better way:
Use site-wide themes (Objects with keys like colors.heading.primary having value '#000') and withStyles[1] and React.PureComponent[2]. Having your React component render the same result given the same props and state makes things so much easier to work with. Also, the compiler knows about your theme so your IDE helps auto-complete and makes sure the style you're using is actually defined.
[1] https://github.com/airbnb/react-with-styles
[2] https://reactjs.org/docs/react-api.html#reactpurecomponent
Yes! (https://marketplace.visualstudio.com/items?itemName=bradlc.v...)
Although I wouldn't mind an array instead so I can use the typescript typechecker.
theme: {
button: {
primary: {
color: 'blue',
padding: '2px',
},
},
}<Button {...css(theme.button.primary)}>
vs. tailwind classes:
<Button className="text-blue p-2">
Having IDE autocompletion (or even TypeScript integration) would be really nice. I'm sure there's plugins for many IDEs or editors that can do that, though.
Use withStyles [1] and vscode autocompletes without needing a plugin.
I currently have a tailwind project that's around twelve months old. I'd like to add a dark theme.
Normally - in a CSS, SCSS, PostCSS project - this would consist of adding a media query overriding a handful of color variables.
Using tailwind (which has 'inbuilt dark mode support') I have a few thousand colors spread around the project and I have to modify them all. Additionally by having a giant palette of named colors, we have, for example, multiple occurrences of our active color as `blue-400` and `blue-500` and `blue-600` rather than a single `var(--active-color)`.
Everyone stopped using unstructured cryptic class="bg-gray-100" for good reason. Tailwind is backwards progress.
Tailwind is easy to start but hard to maintain and extend.
Its good for a prototype or small landing page.
I would never use it for long term project where you need to maintain and extend your features a lot.
You don't.
I often wonder whether we need yet another UI framework implementation? I touched on this in my post a couple days ago: https://williamhoyle.ca/blog/2021/vue-has-too-many-ui-framew...
To summarize:
We have 20+ UI frameworks written in Vue. Surely there's enough common ground/code to combine the implementation details for all these common components. For example, OP mentioned the complexities of implementing a modal with some corner cases for 'ESC' behaviour. This is a universal concept whether you're using WidgetUI or TailwindUI. How many times are we going to re-implement a modal/button/dialog/menu?
We have to learn another set of APIs, props, component names, etc... Of course, no one is forcing us to use this. But it seems like we're going in circles re-implementing the same UI patterns over and over again.
I wish we could standardize on a library. Imagine just pulling in GenericUI and sprinkling in your CSS of choice (e.g. tailwindCSS, Bulma, Material, etc...).
I'm a Vue guy, but I think my argument still stands for the React ecosystem.
Because the web wasn’t designed for applications UI interfaces (hence the CSS grid which landed only in 2017), so it lacks basic primitives and behaviors that are then re-implemented slightly differently by different people. On the contrary, a UI framework designed for this such as the Windows Forms on Windows desktop is still very usable (and used) to make applications, 20 years after its release; and is only concurrenced by a single newer framework (WPF) which actually brings a lot of improvements on the table.
Actually the web does it half-way: there are a set of common elements (input text, button, etc.) but some useful components are missing, and some of the existing one are hard to style. This support should be added in browsers, then custom code wouldn’t be needed anymore.
If I can avoid writing another autocomplete and solving the Esc corner cases, the effort will have been worthwhile...
So we end up with 20x libs all doing roughly the same thing, sometimes badly.
This one doesn't seem like the typical Munroe +1, because it's not trying to rule them all, just put a visually unopinionated base in place with the tricky, boring stuff handled. It kinda feels like other libs are doing too much in any case.
That's exactly what the tailwind people built in Headless UI: "a library of JS components that abstract away all of the keyboard navigation and accessibility logic without including any design opinions"
I'm always reminded of https://xkcd.com/927/ when another UI framework pops up. Do all libraries do things just differently enough to warrant a completely new library or can the implementation details be abstracted to some core lib (maybe even a web spec).
On the other hand, maybe I'm wrong and HeadlessUI ends up being that GenericUI lib.
Adam's (author) podcast Full Stack Radio is useful like this too [1].
[0] https://www.w3.org/TR/wai-aria-practices/examples/menu-butto...
I even built something like Tailwind UI but for Chakra (link is in my bio if you want to check it out)
Those things alone are reason for never using Chakra for anything serious as these are actual reasons your product/saas/whatever is at a higher risk of failing.
And that's just apart from the fact that the components are not allowing easy overriding, theming is a mess (especially if you want Typescript support again for your custom variants) and it's sometimes buggy with the way DOM events are handled (the checkbox being the biggest offender). At some point the frontend also randomly crashed for me because of the Popover component and it's usage of Popper.js
>> Those things alone are reason for never using Chakra for anything serious as these are actual reasons your product/saas/whatever is at a higher risk of failing.
Emotion and other CSS-in-JS libraries are used extensively and don't raise a risk of the project failing.
>> And that's just apart from the fact that the components are not allowing easy overriding
You can use Styled-system to create and extend the existing theme. This is actually a knock against using Tailwind CSS directly since you will need to write custom CSS to override the theme. I've even seen projects where Tailwind is used with Emotion to extend the existing theme.
>> At some point the frontend also randomly crashed for me because of the Popover component and it's usage of Popper.js
I use Chakra (and the Menu component that uses Popper) on my landing page and SaaS and have never had any issues. If your code is open, I can help you debug this.
Third paragraph of the article
Headless UI is some of the nicest React components I’ve worked with via interop, and to me it’s really valuable that they don’t come with an opinionated way of styling them.
One year ago I had to do something similar and used Web Components (https://www.robinwieruch.de/react-web-components). Did you consider using Web Components or a Web Components library such as lit-html in the first place?
I assumed that I would use Tailwind UI's HTML for these and write vanilla JS for interactivity, but now I am rethinking, especially having read this quote in the article: "If we tried to write it in custom vanilla JS, well we'd be making it harder for literally everyone".
So given this announcement, in my situation would you recommend against vanilla JS, and instead to adopt one of React or Vue?
I’d be interested to know whether Alpine.js support is “coming soon” or “maybe eventually”.
<Menu.Items className="absolute mt-1 right-0">
I'd prefer it to be like this: <Menu.Items absolute mt-1 right-0>
I did some experimenting (I'm the owner of `react-tailwind`, please reach out if you want it!) and it's definitely possible to do that; but it does imply components are purely visual, which I'm not sure it's the way they want to go. It's also not possible to use the colon like `md:...`, but you can do `md="..."` instead, which is a good approximation IMHOI know it's not the right system for everyone, but it's the best styling/layout framework I've ever used. You can have it span as much functionality or as little as you want. In my experience sticking to largely layout based styles is the right move, while occasionally mixing in some slight design-based styling as needed.
<Box
px={4} // padding left & right
width={['95%', '80%', 320]} // correlates to my media query grid
>
<Card ... />
</Box>
or <Flex gap={4} flexDirection="column">
<Card ... />
<Card ... />
</Flex>
or <Grid
gridTemplateColumns="1fr 1fr 320px"
gridGap={4}
>
<Card ... />
<Card ... />
<Sidebar ... />
</Flex>
I can put together nearly any layout I want without ever touching styled-components, raw css/sass, or augmented classNames (ew?). If I need to do something complex, falling back to a normal styled-component is trivial enough. Plus every layout is in your face and immediately visible; no paging and scrolling around files to figure out what this class or custom styled-component does.The only downside is that if you don't organize your layouts effectively and break up your react components, you can end up with Box overloads or abuse, but ultimately its up to the developer or team member to figure out that balance. It hasn't been too much a problem for me.
<Button color="red">Hello</Button>
<Button red>Hello</Button>Personally, I write CSS this way:
1. Select based on cascaded semantic HTML elements; 2. Don't repeat yourself; 3. No unnecessary classnames; 4. No style-descriptions in classnames.
Only when you have troubles selecting an element based on its position in your DOM you should choose a classname. It should not be ".text-gray-500" (like Tailwind does) but it should be "p.author-role".
In my case the classname is semantically descriptive whereas Tailwind is not. In my case you just need to update the CSS and in the case of Tailwind you need to change your HTML and then probably also create a new CSS class.
But still, ideally, you'd simply select it like this:
The author name:
figure.author figcaption h5 { ... }
The author role: figure.author figcaption h6 { ... }
And even the .author selector would be optional, depending on whether that figure elements needs custom styling that is exclusive to the author or not."Shoot, was this 13px padding or 14px padding on the other page?" Don't worry about it, just use "p-4, p-6, etc.". Thinking in these terms becomes really powerful.
Another great thing as that you just simply don't have to think about what to name things! I find that so liberating, to be honest, and I don't have to keep switching back and forth between my markup and my css. I don't have to worry about coming up with semantically descriptive names. I just use "text-gray-500". Was this .authorrole? Or was it .authoremail? Hmm, now I want to display an author nickname. Do I just repeat "authorname" class? I find it so freeing to basically not have to worry about this stuff at all.
Legitimate question. How do you remember not to use "p-4.25" without npm installing tailwind-default-constraints or whatever the fuck?
Use withStyles[1] and have a spacings namespace in your theme. Use the spacing in your component like spacing.button, which applies an Object containing the correct CSS padding, or spacing.padding.medium for the raw number.
That said, I’m surprised by this:
“Only when you have troubles selecting an element based on its position in your DOM you should choose a classname”
Certainly if you have knowledge of the exact DOM structure in advance, and high confidence that it will never be substantially changed, this is workable. But in many cases, having the CSS care about the DOM structure produces way more pain than the ergonomics of tailwind do. At least in the kind of code I’ve worked on where components are reused all over the place and sometimes rearranged by authors directly in a CMS. Or just having DOM reorganized because something is added that requires an extra wrapper & then all the CSS selectors are nested wrong. Or whatever.
Anyhoo. I don’t love tailwind, but I think it’s fine. Far worse things have happened to web dev. It seems like you have a problem with utility CSS, which tailwind takes to the extreme for sure.
Also, side note - having headings in a figcaption like that seems semantically incorrect.
I genuinely wonder what the payoff is for semantic correctness.
Web browsers don't care, screen readers aren't nearly as particular as they used to be, and the web certainly isn't returning to some XML/XSLT universal document ideal.
Tailwind and other functional frameworks use classes for abstraction, which unlocks media queries, pseudo selectors, and many other CSS features.
Plus Tailwind's build system generates styles according to your config, which is sort of what distinguishes it from other functional frameworks.
"Semantically" descriptive could mean any number of things. In this case, it seems to mean: name your class according to what it's supposed to do, which is nice in theory, except that it hides the relationship between the markup and CSS. A well-prepared stylesheet is nice, except when the markup doesn't match what it expects.
In Tailwind, the class names themselves are "semantically" descriptive because they do exactly what they are supposed to do. There are no hidden abstractions.
It's not, because there's a system to it. With Tailwind you pick specific values. Inline styles let you specify any value.
> It should not be ".text-gray-500" (like Tailwind does)
This is fair. I'd never considered that specifying the actual color in the class name would be problematic, but another thread pointed out that it makes theming (such as dark mode) difficult.
> 1. Select based on cascaded semantic HTML elements; 2. Don't repeat yourself; 3. No unnecessary classnames; 4. No style-descriptions in classnames.
I'm sure this works great when there are a small number of people working on styles. When there are many, however, I've found that the cascade often results in styles overriding one another, and needing to introduce more specificity to fight it, in an endless cycle.
Obviously people should write better CSS to avoid that. Which is actually what Tailwind is, I think.
I think what bothers people isn't _what_ it is but _where_ it is. I for one, don't want long sausages in my HTML elements, regardless of whether they are classes or CSS styles.
1. @apply as a JS function that I can use in CSS in JS.
2. Some way to override existing Tailwind classes with new ones (ex: Generic Button => Specialized Button => Unique Button).