Webサイトの配色や余白を、開発者ツールで1つずつ拾って回った経験は誰にでもあるはずだ。designlang(リポジトリ名 Manavarya09/design-extract、★3,405・MIT)は、その採寸作業をheadlessブラウザに任せてしまうOSSである。URLを1つ渡すと、Playwrightが実際にそのページを開き、ライブDOMから配色・タイポグラフィ・余白・角丸・モーションを読み取って、W3C DTCG準拠のデザイントークンやTailwind設定、Figma変数として書き出す。
ただし、この記事でいちばん伝えたいのは機能一覧ではない。READMEどおりにコマンドを打つと失敗するという、配布版と開発版の乖離のほうだ。実際に両方を動かして確かめたので、順を追って書く。
dna を npx で叩いた実録。npm配布版は 12.21.0 で dna を持たないため、dna をホスト名と解釈して https://dna/ へ接続し ERR_NAME_NOT_RESOLVED で失敗する(2026-08-26 実測)- ・正体:URLにheadlessブラウザを向けてライブDOMからデザインシステムを読み取るCLI。npm配布名は
designlang、リポジトリ名はdesign-extract - ・何ができる:DTCGトークン・CSS変数・Tailwind設定・shadcn/uiテーマ・Figma変数・モーショントークン・コンポーネント構造の.tsxスタブを一括出力
- ・何を代替できる:目視での採寸と手作業のトークン化。既存デザインシステムとの差分検出や、AIコーディングへ渡す設計コンテキストの生成
- ・実測:stripe.com で 10.51秒・41ファイル(READMEの「17+ files」より多い)。linear.app でも41ファイル・10.17秒
- ・最大の注意:npm配布版は 12.21.0(2026-06-14公開) で止まっており、READMEが見出しに掲げる
dna/site/fidelity/galleryは配布版に存在しない
デザイントークンという考え方そのものや、デザインシステムを構成する要素の整理については、デザインシステムとは?仕組み・構成要素・有名事例をエンジニア向けに整理する【2026年版】にまとめてある。本記事はそこで扱った概念を、実在するサイトから機械的に取り出す道具の話である。
designlangとは——ライブDOMから読むという設計判断
デザイントークンを扱うツールは多いが、入力に何を取るかで性格が大きく変わる。Figmaのファイルを入力に取るもの、CSSファイルを静的解析するもの、そしてdesignlangのようにレンダリング後のDOMを入力に取るものがある。
designlangはPlaywrightでChromiumを起動し、対象ページを実際に描画してから getComputedStyle 相当の情報を集める。この設計には明確な利点がある。CSS-in-JSでもTailwindでもCSS Modulesでも、最終的にブラウザが解決した値を読むので、実装技術に依存しない。ビルド後にしか決まらない値、メディアクエリで切り替わった値、JavaScriptが実行時に注入した値も、そのまま観測できる。
代償もある。ページが描画されなければ何も取れない。ログインの背後にある画面、ボット遮断が掛かっているサイト、レンダリングに極端に時間がかかるページは苦手だ。またDOMから読める値は「使われている値」であって「設計上の意図」ではない。ある色が意図的なアクセントなのか、たまたま1箇所だけ違う値が混ざっているのかは、DOMだけでは判別できない。
この弱点に対して、designlangは site サブコマンドでカバレッジによる多数決を導入している。サイト内の代表的なページを複数クロールし、各トークンが何割のページで使われているかを数え、サイト全体で使われている値とページ固有の値を分離する仕組みだ。ただし後述するとおり、このコマンドは現時点でnpm配布版に含まれていない。
Chromium で実描画"] B --> C["ライブDOMを計測
色 / 文字 / 余白 / 角丸 / モーション"] C --> D{"近い値を
まとめるか?"} D -- "OKLab上で近接" --> E["1つのトークンへ統合"] D -- "離れている" --> F["別トークンとして保持"] E --> G["DTCG トークンへ正規化"] F --> G G --> H["多形式で書き出し
Tailwind / Figma / shadcn / CSS変数"]
① 何ができる:URLを渡すだけで、そのページのデザインシステムを機械可読なトークン群として取り出せる。
② 何を解決する:開発者ツールで色や余白を1つずつ拾って表に写す、という手作業をまるごと消す。
③ 何を代替できる:手作業の採寸と、リニューアル前の現状棚卸し。デザイナーとの認識合わせに使う「現状の実測値」を数分で用意できる。
designlangを実際に動かす:出力41ファイルの中身を数える
まず素直に動かしてみる。Node.js 20以上が必要で、初回はPlaywrightがChromiumを取得するぶん時間がかかる。
# 配布版をそのまま実行(インストール不要)
npx designlang https://stripe.com
# 出力は ./design-extract-output/ に書かれる
ls design-extract-output/ | head -20
READMEには「Each run writes 17+ files」とある。実際に数えたところ、2026-08-26時点で 41ファイルが出力された。stripe.com と linear.app の両方で同じ41ファイルだったので、サイトによる揺れではない。READMEの数字は実態より控えめということになる。
出力の主なものを整理すると次のようになる。
| 出力ファイル | 中身 | 主な使い道 |
|---|---|---|
*-design-tokens.json |
W3C DTCG準拠トークン(primitive / semantic / composite の3層) | 他ツールへの受け渡し、トークン管理の起点 |
*-variables.css |
CSSカスタムプロパティ | 既存CSSへ直接取り込む |
*-tailwind.config.js / *-tailwind-v4.css |
Tailwind設定(v3形式とv4形式の両方) | Tailwindプロジェクトへ差し込む |
*-shadcn-theme.css |
shadcn/ui の globals.css 相当の変数 |
shadcn/uiベースのUIへ適用 |
*-figma-variables.json |
Figma Variables のインポート形式(light / dark) | デザインツール側へ戻す |
*-motion-tokens.json ほかモーション6種 |
duration・easing・spring、GSAP/WAAPI/Tailwind向けプリセット | アニメーションの再現 |
*-anatomy.tsx |
検出したコンポーネントの型付きスタブ | 実装の出発点 |
*-voice.json |
文体・人称・CTA動詞などのブランドボイス | コピーライティングの指針 |
*-design-language.md |
19セクションのMarkdown要約 | LLMへ丸ごと渡す設計コンテキスト |
*-mcp.json |
MCPサーバー用ペイロード | MCPクライアントへの供給 |
注目すべきは *-design-language.md と *-mcp.json の存在だ。designlangは出力を人間向けだけでなく、LLMに読ませる前提で整形している。デザインシステムの説明文を1ファイルにまとめておき、それをコーディングエージェントの入力にする、という使い方を最初から想定した設計になっている。
実際に抽出されたトークンを読む
出力の質を判断するには、中身を見るのが早い。stripe.com に対する実行で生成された stripe-com-design-tokens.json を開くと、DTCGの仕様どおり primitive(原始値)と semantic(役割)の2層に分かれている。
primitive.color は4つのグループに分類され、合計24個の色トークンが入っていた。ブランド色は brand.primary が #533afd、brand.secondary が #e5edf5 として抽出されている。この他に neutral が7色、background が5色、text が10色である。
semantic 層はDTCGのエイリアス記法で primitive を参照する。
{
"semantic": {
"action": { "primary": { "$value": "{primitive.color.brand.primary}", "$type": "color" } },
"surface": { "default": { "$value": "{primitive.color.background.bg0}", "$type": "color" } },
"text": { "body": { "$value": "{primitive.color.text.text0}", "$type": "color" } }
}
}
参照が文字列リテラルではなく {...} のエイリアスになっているので、原始値を1箇所変えれば役割側も追随する。仕様に沿った素直な実装だ。
ここで、単一URL実行の限界がはっきり見える形で現れる。stripe.com と linear.app の抽出結果を並べると次のようになった。
| 項目 | stripe.com | linear.app |
|---|---|---|
| 色トークン(合計) | 24 | 21 |
| 余白ステップ | 18 | 29 |
| 角丸ステップ | 6 | 8 |
| 影 | 4 | 3 |
| フォントファミリ | 1(sohne-var) |
2(Inter Variable / Berkeley Mono) |
余白が18ステップ、29ステップというのは、設計されたスケールの姿ではない。実際の値は 1px, 20px, 22px, 24px, 28px… と並んでおり、4の倍数で刻んだ設計上のスケールではなく、そのページで観測された実測値の集合である。designlangが返しているのは「意図されたデザインシステム」ではなく「そのURLで実際に使われている値」だ、という点は必ず押さえておきたい。
この差を埋めるのが site サブコマンドのカバレッジ機能である。サイト内の代表ページを複数クロールし、各トークンが何割のページに登場するかを数えて、サイト全体の値(🟢)・セクション固有(🟡)・ページ限定(🔴)に色分けする。設計上のスケールに近づけたいなら、単一URLではなく site を使う必要がある。ただしこのコマンドは、後述するとおり現在のnpm配布版には含まれていない。
なお $metadata に記録されるスキーマ版(実行時点で 7.0.0)は、CLI本体のバージョン(13.1.0)とは別系統で管理されている。出力の互換性を気にする場合はCLI版ではなくこちらを見る。
dna サブコマンドはやや毛色が違う。デザインを30個の特徴量に還元し、参照コーパスの中でどこに位置するかを百分位で返す。実際に走らせると、READMEの掲載例と同じ数値が返ってきた。
Design DNA · https://raycast.com
30 features, 100% measurable
vs 8 systems (default)
color 65th percentile
type 56th percentile
space 42th percentile
shape 79th percentile
motion 67th percentile
Nearest
0.14 https://railway.app
0.15 https://linear.app
0.19 https://notion.so
READMEの出力例が実行結果と一致するというのは、当たり前のようで実は珍しい。README掲載の「出力例」が実際にはプログラムの出力ではなく手書きのイメージ図だった、という例をこれまで何度も見てきた。designlangの dna は「fully deterministic」を謳っており、その主張どおりの結果になった。
dna の出力例は、実行すると数値まで完全に再現できた。決定論的という主張は本当だった。npx版と最新版の乖離——READMEどおりに打つと失敗する
ここからが本題である。上の dna の実行結果には、ひとつ条件が付く。リポジトリをcloneしてmainブランチを直接実行した場合の結果だ。READMEのクイックスタートが案内する npx designlang では、この結果は得られない。
理由は単純で、npmに公開されている最新版が古いからだ。
# 配布版のバージョンと最終公開日を確認する
npm view designlang version time.modified
# → 12.21.0
# → 2026-06-14T09:34:43.132Z
一方リポジトリの package.json は 13.1.0 である。READMEはこの13.1.0を前提に書かれており、クイックスタートの2行目から npx designlang dna stripe.com のように、配布版に存在しないコマンドが並んでいる。
・
dna(v13.1として紹介)・
dna-corpus(v13.1)・
site(v12.23)・
fidelity(v12.24)・
gallery(v12.24)いずれも
npx designlang <cmd> --help がトップレベルのusageを返すだけで、サブコマンドとして認識されない。
厄介なのは失敗の仕方だ。存在しないサブコマンドを打っても「そんなコマンドはない」とは言われない。CLIの引数解析がサブコマンド名をURL引数として受け取ってしまうため、次のようになる。
$ npx designlang dna stripe.com
designlang
https://dna
- Launching browser...
✖ Extraction failed
page.goto: net::ERR_NAME_NOT_RESOLVED at https://dna/
dna という文字列をホスト名だと解釈して https://dna/ に接続しにいき、名前解決に失敗している。初見でこれを見ると、自分のネットワーク設定やプロキシを疑ってしまう。実際にはコマンドが存在しないだけである。
さらに、配布版には正常系でも問題がある。存在するコマンド(引数なしの通常抽出)を実行すると、41ファイルを正しく書き出したうえで最後に異常終了する。
| 実行対象 | バージョン | 出力ファイル | 終了コード | 所要時間 |
|---|---|---|---|---|
| stripe.com | npm 12.21.0 | 41 | 1(異常終了) | — |
| linear.app | npm 12.21.0 | 41 | 1(異常終了) | — |
| stripe.com | main 13.1.0 | 41 | 0(正常) | 10.51秒 |
| linear.app | main 13.1.0 | 41 | 0(正常) | 10.17秒 |
配布版が吐くエラーはこれだ。
✖ Extraction failed
The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received undefined
ファイル自体は書き終わっているので、成果物は使える。しかし終了コードが1なので、CIに組み込むとジョブが失敗扱いになる。デザイントークンの差分検出をパイプラインに入れたい、という本来の使い方をしようとすると、ここで詰まる。
この不具合は開発側で把握済みだった。Issue #131 が2026-06-01に起票され、2026-06-18にクローズされている。修正コミットは 1944e58 fix(cli): guard contentless file entries in output summary (#131) で、内容も「中身のないファイルエントリを出力サマリでガードする」という、まさにこの症状に対応するものだ。
問題は時系列にある。
Issue #131 起票"] --> B["2026-06-14
npm 12.21.0 公開
(修正前・これが最後の公開)"] B --> C["2026-06-18
main で修正
PR #156 / 1944e58"] C --> D["2026-08-26
npm は 12.21.0 のまま
約2か月 未公開"]
修正がmainに入ったのは、最後のnpm公開の4日後だった。以来2か月以上、公開版は更新されていない。リポジトリのコミット自体は活発で、記事執筆時点の最終pushは2026-08-24である。開発は続いているが配布は止まっているという状態だ。
npx designlang --version の出力が 12.21.0 のままなら、READMEの新しいコマンドは使えない。13.x になっていれば公開が再開されている。
「最新版」がどこを指すかは、見る面によって違う。3つの配布面を並べると次のようになった。
| 面 | 最新バージョン | 日付 |
|---|---|---|
リポジトリ main の package.json |
13.1.0 | 最終push 2026-08-24 |
npm(npx が引く先) |
12.21.0 | 公開 2026-06-14 |
| GitHub Releases | v12.15.0 | 公開 2026-05-21 |
READMEはいちばん新しい main を基準に書かれ、npx はその2つ手前を引き、GitHub Releasesはさらに古い。「どのバージョンの話をしているか」を面ごとに確認しないと噛み合わない。
開発が止まっているわけではない点は付記しておきたい。直近30日のコミットは13件あり、最終pushは2026-08-24である(コントリビュータは4名)。npmの週間ダウンロードは528件(2026-08-18〜24)。動いていないのは開発ではなく公開作業のほう、というのが実態に近い。
対処は2つある。ひとつは配布版に存在する機能だけを使うこと。通常の抽出、clone、grade、battle、remix、pack、theme-swap、brand、pair、verify、studio、mcp は12.21.0にも存在する。終了コード1については、CI側で || true を挟むか、出力ファイルの存在で成否を判定すればよい。
もうひとつは、リポジトリを直接使うことだ。
git clone https://github.com/Manavarya09/design-extract.git
cd design-extract
npm install
node bin/design-extract.js https://stripe.com # main の 13.1.0 が動く
筆者の環境(Apple Silicon / Node v22.13.1)では、この手順で dna を含む全コマンドが動作し、終了コードも0になった。
配布版のままでも使える機能
配布版を悲観しすぎる必要はない。存在するコマンドは正常に動くし、終了コード1の問題も通常抽出に固有のもので、他のサブコマンドには波及していない。たとえば grade は配布版でも終了コード0で完走する。
# 配布版(12.21.0)でも動く。デザインシステムの健全性を採点する
npx designlang grade https://stripe.com
実行すると、8項目の内訳を持つ0〜100のスコアとレターグレードが返る。stripe.com を対象にした結果は次のとおりだった。
| 採点項目 | スコア |
|---|---|
| spacingSystem(余白スケール) | 100 |
| shadowConsistency(影の一貫性) | 100 |
| tokenization(CSS変数化の度合い) | 100 |
| radiusConsistency(角丸の一貫性) | 90 |
| typographyConsistency(文字の一貫性) | 82 |
| colorDiscipline(色の規律) | 80 |
| accessibility(アクセシビリティ) | 73 |
| cssHealth(CSSの健全性) | 65 |
| 総合 | 85(グレードB) |
npx designlang grade https://stripe.com の実測結果。配布版(12.21.0)でも終了コード0で完走する指摘として挙がったのは「WCAGのコントラスト不足が3件」「CSSの54%が未使用のため削減を検討」「重複したCSS宣言が6,620件」の3点だった。あのStripeでもこの数字が出る、という事実は、自社サイトを測るときの心理的な基準線として役に立つ。
この用途——自社サイトを定点観測して、スコアの推移を追う——であれば、配布版のままで十分に成立する。grade は .grade.html / .grade.json / .grade.svg を出力するので、JSONをCIで保存してスコアの回帰を検知する、といった運用も組みやすい。
なおREADME自体にも整合していない箇所がある。Claude Codeプラグインのスラッシュコマンド数について、本文の散文は「eleven slash commands」、その直下の表は14行、後方の別の表では「Five slash commands」と、3か所で異なる数を書いている。リポジトリの commands/ ディレクトリの実ファイル数は14だった。急速に機能追加が続いた結果、ドキュメントの各所が別々の時点で止まっている、という状態に見える。
Claude CodeプラグインとMCPサーバーとしての使い方
designlangはCLIの他に、コーディングエージェントから使うための口をふたつ持っている。
ひとつはClaude Codeプラグインとしての登録だ。リポジトリ自体がプラグインマーケットプレイスとして機能する。
/plugin marketplace add Manavarya09/design-extract
/plugin install designlang@designlang
登録すると /extract <url>、/grade <url>、/battle <a> <b>、/remix <url> --as <vocab>、/pack <url> といったスラッシュコマンドが使えるようになる。ただしこれらは内部でCLIを呼ぶラッパーなので、前節の配布版問題の影響をそのまま受ける。プラグイン経由でも /dna や /site は、CLI側に該当コマンドが無ければ動かない。
もうひとつがMCPサーバーだ。
designlang mcp --output-dir ./design-extract-output
これを起動すると、直近の抽出結果をMCPクライアントに対してリソースおよびツールとして公開する。エージェントが「このサイトのプライマリカラーは何か」「見出しのフォントサイズのスケールは」といった問い合わせを、ファイルを読み込ませることなく行える。エージェント側の設定やプラグインの扱いを含めた全体像はClaude Code|2026年版・インストールからCLAUDE.md・Hooks・本番運用までの実装手引きにまとまっている。
エージェント連携という観点でいちばん実用的なのは、実のところ *-design-language.md を渡すことかもしれない。19セクションのMarkdownにデザインシステムの全体像がまとまっているので、これをコンテキストに入れてUIを書かせる、という単純な使い方が最も摩擦が少ない。スラッシュコマンドやMCPの設定を整える手間に対して、Markdown 1枚を読ませるだけという手軽さは大きい。
designlangと類似ツールの比較——使ってよい場面・避けるべき場面
デザイン情報を機械的に扱うOSSは他にもあるが、入力と出力の組み合わせで住み分けができている。
| ツール | 入力 | 主な出力 | 位置づけ |
|---|---|---|---|
| designlang | 任意のURL(ライブDOM) | DTCGトークン・Tailwind・Figma変数・.tsx・Markdown | 既存サイトの実測と棚卸し |
| tweakcn | GUI操作 | shadcn/ui テーマCSS | テーマを視覚的に作る |
| Astryx | 自前のデザインシステム定義 | エージェント向けの参照形式 | 設計をエージェントに読ませる |
| Open Design | 自然言語の指示 | デザイン案・実装 | エージェントに作らせる |
designlangの立ち位置は「すでに存在するものを測る」である。ゼロから作る道具ではない。したがって向いている場面もはっきりしている。
・自社サイトの棚卸し:何年も継ぎ足してきたCSSに、実際いくつの色が使われているかを数える
・リニューアル前の現状把握:旧サイトのトークンを機械的に取り出し、新デザインとの差分を出す
・デザインシステムの遵守チェック:site のカバレッジ機能で、サイト全体で使われている値とページ固有の外れ値を分離する
・エージェントへの設計コンテキスト供給:*-design-language.md を渡してUI実装の一貫性を上げる
一方、他社サイトのデザインをそのまま自社製品に使うという用途は勧められない。designlangのMITライセンスが許諾しているのはツールの利用であって、抽出対象サイトのデザインの権利ではない。配色やタイポグラフィの組み合わせ、ロゴ、独自の視覚表現は、商標や不正競争防止法、対象サイトの利用規約の対象になりうる。READMEには競合サイトとの比較機能(battle)や2サイトの融合機能(pair)もあるが、成果物の使い方は利用者の責任になる。
自社サイトで試すときの最短手順
最初の1回で判断するなら、次の3ステップで足りる。所要時間は合わせて1分程度だ。
# 1. 配布版のバージョンを確認する(12.21.0 なら新コマンドは使えない)
npx designlang --version
# 2. 自社サイトを採点する(配布版でも終了コード0で完走する)
npx designlang grade https://example.com
# 3. トークン一式を出力して中身を見る
npx designlang https://example.com && ls design-extract-output/
見るべきは3つある。ひとつめは grade の cssHealth と accessibility で、ここが低ければ抽出の精度以前にサイト側の整理余地が大きい。ふたつめは *-design-tokens.json の primitive.spacing の要素数で、これが20を超えるようなら余白の値が散らかっているサインだ。設計上は4〜8段階に収まるはずのスケールが20段以上に膨れているなら、実装が設計から乖離している。
みっつめは primitive.fontFamily である。1〜2個に収まっていれば健全だが、3個以上が検出されるなら、どこかでフォント指定が意図せず混入している可能性が高い。
こうした「実装が設計から離れていないか」を機械的に測れる点が、designlangの最も現実的な価値だと感じた。他社サイトを覗く道具として話題になりがちだが、真価は自社の棚卸しにある。
リニューアル前後、あるいはリリース前後で同じURLに対して
grade を実行し、.grade.json の scores を保存しておけば、デザインの一貫性がいつ劣化したかを後から特定できる。トークンJSON同士を diff に掛ければ、どの色や余白がいつ増えたかも追える。
designlang本体は MIT。これはツールを自由に使い改変し再配布してよい、という意味であって、抽出したデザインの利用を許諾するものではない。他社サイトを対象にする場合は、調査・分析の範囲に留めるのが安全である。
逆に、次のような期待で導入すると外す。デザインシステムを新規に設計してくれる道具ではないし、抽出したトークンをそのまま貼れば同じ見た目になるわけでもない。designlangが読むのは色・文字・余白・角丸・モーションといった測れる属性であり、レイアウトの意図、階層の付け方、余白の意味づけといった設計判断は出力に含まれない。出てくるのは素材であって、設計ではない。
技術的な制約も押さえておきたい。Node.js 20以上が必要で、Playwrightに依存するためChromiumのダウンロードが発生する(初回のみ、数百MB)。ログインの背後にあるページは対象にできない。--smart オプションで分類精度を上げる機能はOpenAI互換APIの鍵を要求するが、これは任意であり、基本の抽出と site のカバレッジ算出はAPIキーなしで動作する。
まとめ
ライブDOMからデザインシステムを実測するという設計は筋が良く、出力の幅も広い。実際に動かすと41ファイルが10秒程度で揃い、
dna の出力はREADMEの掲載例を数値まで再現した。決定論的という主張は本物である。ただし配布が2か月以上止まっている点は、導入前に必ず確認したい。npmの最新は12.21.0で、READMEが前面に出す
dna / site / fidelity / gallery は入っていない。しかも存在しないコマンドを打つと「コマンドが無い」ではなく名前解決エラーとして失敗するため、原因にたどり着きにくい。正常系でも配布版は終了コード1で終わる(Issue #131・main では修正済み)。結論:試すだけなら
npx designlang <url> で十分。CIに組み込む、あるいはREADMEの新機能を使いたいなら、リポジトリをcloneしてmainを直接実行するのが現時点での正解である。
参照ソース
・Manavarya09/design-extract(公式リポジトリ・README) — 機能一覧・スラッシュコマンド表・「17+ files」等の公称値の出典
・designlang — npm パッケージ — 配布版バージョン12.21.0と公開日2026-06-14の確認
・Issue #131 — CLI exits 1 with “string argument … Received undefined” — 終了コード1の不具合が既知であり2026-06-18にクローズされた事実
・PR #156 / コミット 1944e58 fix(cli): guard contentless file entries in output summary — 修正が main に入った日付