はじめに
私はプロダクト開発でAIエージェントにUI実装を任せています。デザインシステムから逸脱していないかのチェックもAIにさせていましたが、次のような不便を感じるようになりました。
- チェックのたびにルールをAIに読ませる必要があり、時間もコストもかかる
- 指摘の粒度がブレて、見落としも起きうる
そんな折、2026年9月にshadcn-uiから @shadcn/lint がリリースされました。うたい文句は「agent-first linter for Tailwind design systems」。本記事では 何ができて何ができないのか を、公式ドキュメントとローカルでの動作検証をもとに整理します。
@shadcn/lintとは
Tailwindのクラス使いをデザインシステムのルールで縛る、ESLint / Oxlint用プラグインです。
- 2026-09-14にv0.1.0、2026-09-22にv0.2.0がリリース(本記事時点の最新)
- Tailwind v4で動作。shadcn/uiを使っていなくても導入できる
- ESLint 9.30以降 / Oxlint 1.80以降。React / Vue / Svelte、Node.js 20.19以降
style propなら Pick<React.CSSProperties, "margin" | "width"> のように型で縛れますが、className はただの文字列で、中のクラスまで型で縛るのは現実的ではありません。@shadcn/lintは、その className の中身を検査して直し方まで教えるのが特徴です。公式READMEの紹介例(抜粋):
"p-4" is not allowed on <Button>: <Button> owns its spacing.
Use a size (sm, lg), or margin here or gap on the parent for space around it.
なにで直すべきかまで指示が出る。これが「agent-first」の意味です。
✅ できること
1. 逸脱を6つのルールで検出する
「paddingは触らせない」「生色は禁止」など、デザインシステムあるあるのルールが最初から用意されています。
| ルール | 検出するもの |
|---|---|
no-restyle |
className による再スタイル |
no-raw-colors |
bg-pink-500 のような生のパレット色 |
no-arbitrary-values |
p-[13px] のような任意値 |
no-inline-styles |
インラインstyleと <style> 要素 |
no-unknown-classes |
rounded-huge のようなTailwindが生成できないクラス |
require-static-classes |
`bg-${color}` のような解析不能なクラス |
2. contractsで運用に合わせる
コンポーネントごとの契約(contracts)で「CardTitleはタイポグラフィ変更可、CardContentは余白のみ」といった粒度の指定ができ、エラーメッセージも差し替えられます。{{sizes}} のようなプレースホルダーは、コンポーネントの定義から実際の値に展開されます。
"shadcn/no-restyle": ["error", {
allow: ["layout"],
contracts: [{ pattern: "^CardTitle$", allow: ["layout", "typography"] }],
}]
3. 既存のESLintに数行で載る
npm install -D @shadcn/lint eslint @typescript-eslint/parser で入れて、設定にpluginとruleを足すだけです。WSL2 (Ubuntu 24.04) / Node.js v24.21.0 / @shadcn/lint 0.2.0 / ESLint 10.11.0 で実際に動かしました。
size に sm / lg を持つButtonに対して意図的に違反させると、次の出力が得られます。
<Button className="p-4">Delete</Button>
<Button className="bg-pink-500 p-[13px]">Subscribe</Button>
Use a Button size: sm, lg.
"p-[13px]" hardcodes an off-token value. Use "p-3.25" instead (same value, on the scale).
p-[13px] には p-3.25 への自動修正提案まで付く のが芸が細かいです。
⚠️ できないこと・制限
1. 静的解析の守備範囲外は判断しない
あくまで コード上のクラス文字列の検査 です。レンダリング結果の見た目、アクセシビリティ、ふさわしいコンポーネント選択は対象外。プレーンなCSSも別途CSS linterの領域です。
2. 解析できない書き方は「検証不能」で扱われる
`bg-${color}-500` のようなテンプレートリテラルで組み立てたクラスは中身を追えないため、require-static-classes が「チェックできない」と報告するだけでした。検証はされない、という正確な実態です。
また no-restyle は認識済みコンポーネントにのみ効きます。shadcn/uiなら components.json で自動認識されますが、自作構成では settings での認識設定(componentImports など)が必要でした(未指定だと発火しません)。Oxlint + Vue / Svelte はscriptブロックのみの対応です。
3. まだv0.x
公開2週間足らずで7リリースと開発が速く、APIや挙動が変わる可能性があります。
本記事の検証はv0.2.0時点です。最新のドキュメントも確認してください。
結論:AIチェックとの使い分けを考える
一番知りたかった「AIにチェックさせる代わりになるか」ですが、クラスの逸脱検出に限れば、AIチェックより速く・確実に回せる道具 でした。LLMを呼ばない静的解析なのでAPI往復も不要で、同じコードには常に同じ診断が出ます。READMEはAGENTS.mdに「変更したらlintを実行して全エラーを直せ」と書く運用も推奨しています。
| 観点 | AIにレビューさせる | @shadcn/lint |
|---|---|---|
| 速度・コスト | コンテキスト積載とAPI往復が必要 | 静的解析のみで一瞬 |
| 見落とし | 文脈や調子でブレうる | 決定的。同じコードには同じ診断 |
| 指摘の質 | ルール外の品質まで語れる | 直し方を語彙で指示 |
公式はコーディングエージェント150以上のタスクでの評価も公開しており、対照実験ではルールのみを渡すより診断を渡す方が安く収束したとしています。
✅ 向いていると思えるケース
- Tailwind v4で、className経由のスタイリングが主体
- AIエージェントにUI実装を任せており、逸脱チェックを決定的に回したい
- ルールが「許可/禁止リスト」で表現できる
❌ 向いていないと思えるケース
- Tailwind v3以前、またはCSS-in-JS / プレーンCSSが主体
- 守りたいのが「見た目の品質」そのもの(デザインレビューの自動化)
判断の軸は「チェックの内容がクラスの許可/禁止で表現できるか」だと思います。
まとめ
- @shadcn/lintは、クラス使いの逸脱を決定的に検出できるESLint / Oxlintプラグイン。エラーが直し方まで教えてくれる
- 見た目の品質やa11yは守備範囲外。AIレビューの置き換えではなく、「確実に検出できる部分」の切り出し先
- まだv0.x。固定バージョンで試すのが無難
デザインシステムの逸脱チェックにAIの負荷をかけたくないなら、一度試してみてはいかがでしょうか。