스크립트
00:00:00ShadCN just released a linter to try and solve the biggest problem with Tailwind today,
00:00:04design systems. With the rise of AI agents, Tailwind has never had a great way to enforce one,
00:00:09so you may have noticed AI sometimes adds in its own styles where you didn't really want it to.
00:00:13It's a large reason why people have turned to alternatives like StarLex,
00:00:17but now ShadCN has a solution for that. It's an agent-first linter built for Tailwind design
00:00:21systems, so let's just jump in and see what this does.
00:00:29Let's start out with what this linter actually does. In Tailwind, a class name can be a string,
00:00:34TypeScript doesn't enforce anything beyond that, and that means that you can put a padding for
00:00:38override on a button that controls its own padding. You could also add a random color like background
00:00:43pink 500 to a component that already uses a themed color, or just use some arbitrary padding amount
00:00:48like 13 pixels when your design system already has a spacing scale that goes from 12 to 16.
00:00:53Currently, none of these will cause any errors in your code base, and they'll only be picked up by
00:00:58you actually reviewing the code, or just adding a load of rules to a markdown file that another
00:01:02agent can review for you. But markdown is not great at enforcing things. It is not as strict
00:01:07as a linter can be. ShadCN actually ran the numbers on this. He set up 8 tasks that tempt the
00:01:12agent to go off the system, with prompts like add a delete button, the design calls for it to be pink
00:01:16with pill corners, build a stats card matching this mock exactly, 13 pixels padding, 10 pixel border
00:01:22radius, or make the pricing card really pop so it stands out. You can see here that every single
00:01:27model produced a lot of design system violations when running these tasks, but when they used the
00:01:31linter, it dropped down to zero. So now that we know the linter actually works, how do we use it?
00:01:36Well, it's a plugin for Oxlint or ESLint, and it will work on any Tailwind V4 project. You don't have to be
00:01:41using ShadCN UI. We can start out with one rule called no restyle, but allowing layout. And this
00:01:47essentially means that pages are allowed to place a component with things like margin, width, flex,
00:01:51and hidden, but they're not allowed to restyle them. So they can't add padding, color, typography,
00:01:56shape, effects, or motion. If they do, it will throw an error like this. P4 is not allowed on button,
00:02:02button owns its spacing, use a size default or margin here, or a gap on the parent for space around it.
00:02:07Add a size in the component only if the design system explicitly calls for one.
00:02:11The cool thing about these error messages here is something like the size list isn't actually
00:02:15defined by the linter. It's actually reading my CVA setup in the button component itself.
00:02:19So if I added an extra size, the error would pick that up as an option. The same goes for basically
00:02:24any Tailwind feature that can restyle. So if we try to add a color here, it would tell us we can only
00:02:29use the ones defined in the component. And obviously the benefit of this is that you get really nice
00:02:33error messages, and an agent can read them and understand how to fix them. This can actually save
00:02:37you on token usage as well, because it doesn't have to load in your full design markdown file,
00:02:41to understand what's correct. The linter can just give it a very specific clue.
00:02:45Now that's the basic usage of our very first rule, no restyle, but there is loads of customization
00:02:50options to make it suit your design system, but we'll come back to that in a minute. First let's
00:02:54take a look at the other five rules, as there's actually six in total. No raw colors is a rule
00:02:58to stop you typing in things like background pink 500, or using a color that doesn't exist or is
00:03:03misspelt. It has to be one that was designed in a theme, and again it will help the agent out by
00:03:08telling them directly what is allowed. This even works on SVGs, so if you try to use a hardcoded fill
00:03:13on a path, you get an error saying use current color with a text color class. Next up we have no
00:03:18arbitrary values, this does what it says on the tin. Something like padding with 13 pixels in the square
00:03:24brackets here, gets hardcodes and off token value, use p3.25 instead, it's the same value on the scale.
00:03:30Or something like rounded 10 pixels in those square brackets gets use rounded large instead,
00:03:35because it can actually read my radius token and know that's the value that equals 10 pixels.
00:03:40One of the coolest examples of this rule actually understanding your design system is if you try
00:03:44and set a random background color, the error actually tells you what the nearest theme token is,
00:03:49which is super nice context for your agent to have. The rest of the rules are pretty self-explanatory.
00:03:54There's a rule to stop inline styles, another to stop unknown classes being used,
00:03:57and one to enforce static classes. This one's interesting because the linter can't actually
00:04:02see what this class is going to be, since it's a dynamic in that template literal,
00:04:06so this rule encourages you to change the way that you handle these,
00:04:09so the linter can still do its job effectively. Those are our six linter rules though,
00:04:13but as mentioned earlier, there's lots of customizability to make this actually suit
00:04:16your design system. This is where you'd use contracts. These are per component rules
00:04:21matched by a regex on the component name, so in this example I've said we can change a card
00:04:25title's typography, but not its font family or weight, and card content can change the spacing,
00:04:30but not the typography. So now text large on this title passes with no errors, and padding six on
00:04:36the content passes, but trying to change the title's weight still gives us an error as expected.
00:04:41Another great option in these settings is custom messages. Every rule and type of rule takes a
00:04:45message with placeholders that are filled from your actual code, so on my button contract here,
00:04:50I've set the message to be set width on the parent container, not on button, for layout, and also
00:04:55button owns its padding, use a button size with a template for the sizes. Those will be filled in
00:04:59for me from my own design system. You can also set a global note, so every finding will have this
00:05:04message attached at the end. This is useful to give some extra hints or context to the agent,
00:05:08maybe telling it where your documentation or guidelines are. If you already have a design
00:05:12system in place, this might be a little bit of extra work to actually get started with using this
00:05:16linter, as you have to configure it for your design system, but it seems flexible enough that it can
00:05:20handle most design systems, and I'm sure an AI agent can help you get started. The final thing I want
00:05:25to talk about is what this linter can't do. It can't see plain CSS, so if you have a raw color in your
00:05:30global CSS file or an apply, it can't enforce rules around that. It also can't trace parent selectors
00:05:36down to the child, only follows class values for one hop within a file, and a brand new theme token
00:05:40is on system by definition, so an agent that actually adds color hopping to your design system to get
00:05:46around the rule will still pass. This does mean that you still need to review the tokens and the
00:05:50variants that the agent is adding. The linter can only check the rules, it can't decide whether a
00:05:54new orange actually belongs in your design system. It's still on you to check, but hopefully this
00:05:59linter should take out some of that extra work. Something else to think about if you're considering
00:06:03using this is, is your component API actually strict enough to enforce against? If your button
00:06:08simply takes any class name and has no variants, there's nothing for this linter to suggest.
00:06:13So the prerequisite is that you actually have a design system in place and use actual variants,
00:06:18which to be fair, if you're using ShadCN UI, you already have, so most people will probably be
00:06:22fine. The final downside is that for now this is only available in Oxlint and ESLint, there is no
00:06:27biome plugin for this yet, but this is an open issue on the GitHub, so hopefully we see this soon.
00:06:31So that's the new ShadCN Linter, it's six rules that help you enforce your design system,
00:06:36which is really helpful in an era where AI is writing most of the code. I'm curious if this fixes any of the
00:06:41problems that you've run into, or if you're still considering switching to something like StarLX,
00:06:45let me know in the comments down below, or yeah, then subscribe, and as always, see you in the next one.
커뮤니티 글
아직 글이 없습니다. 이 영상에 대한 첫 번째 글을 작성해 보세요!
이 영상에 대해 글쓰기