Closed jessepinho closed 2 months ago
cc @grod220 per our discussion on the topic
Helpful! A few thoughts:
className
prop is really a kind of inversion-of-control that shadcn/ui offers. It's meant to replace the need to A) wrap the component in a div w/ custom styling (like wrapping a div with position: relative
) or b) add a ton of optional props for one off styling. However, in theory, one could pass a ton of styling that really should live in the UI component. So it could be abused. Worth trying to be stricter, however, if folks are constantly wrapping it, that may be an indication that className props are necessary.@grod220:
There will be some sizable design changes coming down the pipeline...
Yup, makes sense.
The
className
prop is really a kind of inversion-of-control…
The best way to avoid pain here is: anything that affects the internal elements of a component should be handled by the component itself (with the optional input of limited props like variant
). Anything affecting the external relationships between the component and its surroundings should be handled by the container component.
The reason I don’t like e.g., passing position: relative
directly to a component’s root element is that it can interfere with a component’s internal styling logic. Let’s say, for example, that we have a component with rounded corners. And let’s say you want to make one specific instance of that button’s have smaller corner radiuses than the rest of the buttons, since it’s going to be a small button where larger radiuses would look weird. So you pass a rounded-sm
class to it, which gets applied to the root element. Later, we get new brand guidelines that all of our buttons should have square corners, not rounded. We update the component to have squared corners. Now, all our buttons are squared, except for the one you passed rounded-sm
to. No one realizes this, because there’s no type checking or anything that ensures that we no longer round the corners of our buttons.
If we had instead created e.g., a size
prop on the <Button />
component, the <Button />
would have its own internal logic to use smaller corner radiuses when size === ‘small’
. (For that matter, the developer who created the <Button />
component would hopefully have been thoughtful about what exact radius to use for each size, to fit together aesthetically.) Then, when we got updated brand guidelines, we’d remove any use of border radiuses from inside the <Button />
component, and buttons would still look consistent throughout the app. That way, the domain-specific knowledge about how to render a <Button />
stays within the <Button />
, rather than being distributed in random places throughout the app.
The idea is to have clear boundaries between parent and child components, in terms of styling. If a parent can modify a child’s classes, the child then has to be aware of every possible style modification that can be made to it. But if a parent can only pass in a limited set of props, the child can control exactly what it renders for every combination of props.
…in theory, one could pass a ton of styling that really should live in the UI component. So it could be abused.
I would argue that Tailwind is designed to encourage that kind of abuse.*
The industry is moving toward headless component libraries.
Yeah, I can definitely see the benefits of this — especially to your point about accessibility. I just read his post that you linked, and I hadn’t realized that ShadCN is a kind of copy-paste solution. Looks pretty cool.
Deprecating Tailwind is a tough one to swallow.
I'm open to being wrong on this. I'll take some time to study up on Tailwind so that I understand its philosophy from the inside out, rather than just judging it by its cover. (I watched the video of Theo's journey, though: while his skepticism was similar to mine, I didn’t find his conclusions particularly compelling. To be honest, it feels a bit like Tailwind was designed for people who dislike CSS, which, I mean, fair… but it’s what we have to work with, and it’s worth doing right.)
Re: it being a bet against the industry: I wouldn’t necessarily assume that its popularity means that it’s a good or responsible solution for the long-term maintenance of a web app. It’s equally possible that its popularity stems from people wanting to not have to switch between TSX and CSS files when coding, which isn’t necessarily a good reason for its adoption. That said, again, I’ll take some time to study up on Tailwind so I’m more informed on why it’s so popular.
* Big caveat to pretty much all of my arguments above: I think I’m more accustomed to working with meticulously crafted design systems in Figma, and then implementing them in code. In that context, you really don’t want developers to be making design decisions. Since we don’t currently have a super built-out design system in place, allowing more design-by-developers is the only option we have, and Tailwind certainly makes that a lot easier. (Tangentially: I might also be conflating two concerns: 1) deprecating Tailwind, and 2) keeping clearer boundaries between parent and child components. Will reflect on this more…)
"Black box" model was described in #1406, followed by #1411 and implemented later. Closing as done
The future plans of UI package development are moved to #1710
At the moment, our
@penumbra-zone/ui
package contains components whose appearance can be highly customized by the devs using them, particularly through the use of theclassName
prop.Ideally, a component library should be much less customizable. In the same way that an API exposes specific endpoints rather than allow any client to make SQL queries directly against its database, a component library should have a specific set of "endpoints" (in the form of component props) that allow them to be customized in pre-defined, rather than ad-hoc, ways.
The idea is that developers shouldn't generally be making design decisions when working on a given UI ticket, as that leads to poor UX decision-making. Designers are the ones responsible for making sure that an app has visual unity, and that each user flow is optimized to make it as painless and intuitive as possible. Developers, meanwhile, are generally focused on making a feature work, even if it comes at the expense of those two design concerns. Thus, if the design of an app is led by developers, its visual unity will gradually disintegrate over time, as each developer tweaks designs to their own tastes. And, common workflows will often suffer without a designer's research and expertise.
Thus, I am suggesting the following:
@penumbra-zone/ui
.primary
,secondary
, andicon
— each of which have different background colors and sizes — these can be specified via the<Button />
component'svariant
prop, NOT via aclassName
prop that allows customization of colors and sizes.