0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【shadcn/lint】デザインシステムのルールをlinterで守らせる話

0
Posted at

はじめに

私はプロダクト開発で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の負荷をかけたくないなら、一度試してみてはいかがでしょうか。


参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?