Tavotto
AI drew your research figure. Tavotto makes it publication-ready: edit matplotlib figures on a canvas, lay out in mm, check journal rules. Free, open source.
KSK Design System — フリーランス向けマルチテーマ対応デザインシステム
Translation pending
DesignTypeScript · JavaScriptGitHub @ekusiek716OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Loading the full README. The saved excerpt is shown below.
複数クライアント案件を 1つのデザインシステム で高速に回すために設計された、React 製のマルチテーマ対応 UI ライブラリ。BtoB / SaaS の信頼感と EC / BtoC の購買体験、2つのドメインの設計思想を統合しています。 133 コンポーネントを実際に操作・確認できます。Read the full README ↗
複数クライアント案件を 1つのデザインシステム で高速に回すために設計された、React 製のマルチテーマ対応 UI ライブラリ。BtoB / SaaS の信頼感と EC / BtoC の購買体験、2つのドメインの設計思想を統合しています。
🔗 ライブ Storybook → https://ksk-design-system.vercel.app
133 コンポーネントを実際に操作・確認できます。
@storybook/addon-a11y で a11y を担保@theme ベースのトークン設計native/ui 公開 export 134GlassView + Button variant="glass"、Web 側 .glass CSS マテリアルBrand 色を差し替えるだけで業種に合わせた配色に切り替わります。
| テーマ | Brand-500 | 想定業種 |
|---|---|---|
| Default (Blue) | #3B82F6 | 汎用・SaaS・BtoB |
| Orange | #F97316 | EC・フード・エンタメ |
| Green | #16A34A | ヘルスケア・サステナ・金融 |
| Violet | #7C3AED | プレミアム・クリエイティブ・教育 |
React 19 + TypeScript / Vite / Tailwind CSS v4 / shadcn/ui(Radix UI)/ CVA / iconsax-reactjs / Storybook
Peer dependencies: react 18 or 19, react-dom, tailwindcss ^4.1(preset が同梱する safelist が
Tailwind 4.1 で導入された @source inline() を使うため)
React 19 以上が必須です。 コンポーネントは React 19 の ref-as-prop(関数コンポーネントが
ref を通常の prop として受け取る仕様)を前提に実装しているため、React 18 では
Function components cannot be given refs の警告が出て ref が届きません(issue #502)。
| 依存 | 必要バージョン |
|---|---|
| react / react-dom | ^19.0.0 |
| react-native(Native を使う場合) | >=0.78.0(React 19 を同梱する最初の RN) |
| tailwindcss(Web を使う場合) | ^3.4.17 || ^4.1.0 |
| typescript | >=5.0.0 |
npm install ksk-design-system
AI コーディング(Claude Code / Codex)を使うプロジェクトでは、続けて 1 回だけ実行します。
npx ksk-ds init
プロジェクトルートに CLAUDE.md / AGENTS.md(node_modules 内の DS ルールを指す薄いポインタ)を
設置します。AI エージェントは node_modules 配下を自動では読まないため、このファイルが無いと
DS のルールが適用されません。既存ファイルはスキップされ、更新は --force で上書きします。
v1.60.0 で
postinstallによる自動設置を廃止しました。 install 時にプロジェクトルートへ AI 指示ファイルを書き込む挙動は、サプライチェーン検査で「同意なき AI エージェント制御面の設置」 として Critical 判定されるためです(LPM Firewall が 1.49.2 / 1.51.1 をこの理由でブロック判定)。 v1.59.0 以前から更新する場合、既に設置済みのファイルはそのまま使えます。
/* src/styles/app.css(アプリのルートで一度だけ読み込む) */
@import "tailwindcss";
@import "ksk-design-system/preset";
@import "ksk-design-system/themes/default";
@source "../../node_modules/ksk-design-system/dist";
独自ブランドのテーマを使う場合は、この後にテーマCSSをimportしてください。
preset を使わずトークン CSS を個別 subpath で import する構成では、角丸トークン
(--Radius-Surface / --Radius-Modal / --Radius-Sheet)は
ksk-design-system/tokens/product-theme に含まれます(issue #553)。
product-theme を読まず Radius だけが欲しい場合は
@import "ksk-design-system/tokens/radius"; を使ってください。
同様にセクション余白トークン(--Space-Section-*)も
ksk-design-system/tokens/product-theme に含まれます(issue #560)。
Section Spacing だけが欲しい場合は
@import "ksk-design-system/tokens/section-spacing"; を使ってください。
同様に重なり順トークン(--Z-*)と影トークン(--shadow-*)も
ksk-design-system/tokens/product-theme に含まれます(issue #563)。
それぞれ単体が欲しい場合は
@import "ksk-design-system/tokens/z-index"; /
@import "ksk-design-system/tokens/shadow"; を使ってください。
Tailwind CSS v4 は node_modules を既定では走査しません。@source がないと、
DS 内部だけで使うレイアウト・サイズ・状態クラスが生成されず、コンポーネントの表示や操作が崩れます。
consumer 側の Tailwind と DS を同じビルドで処理するため、上記の設定をセットで使用してください。
なお preset には DS 内部ユーティリティの safelist
(src/styles/source-safelist.css / 自動生成)が同梱されており、@source を書き忘れても
DS のクラス CSS は生成されます(issue #258)。ただし consumer 自身のコードは
consumer 側の走査対象なので、@source は引き続き推奨構成です。
preset-coresafelist は DS 部品(Button 等)が内部で使う Tailwind クラスを、consumer が 1 つも
部品を使っていなくても生成します(safelist は約 1,150 クラス。consumer のクラスが無い状態の
実測で、出力 CSS が preset 179KB → preset-core 54KB、gzip 25.8KB → 9.8KB)。DS 部品を import しない
consumer(ksk-design-system/class-names とトークンだけで UI を組む公開ページ等)は、
safelist だけを除いた preset-core を使えます。トークン・shadcn 互換の変数・アニメーション・
container 幅・scrollbar-hide・base layer のボーダー色の保険は preset と同じです。
@import "tailwindcss";
@import "ksk-design-system/preset-core";
@import "ksk-design-system/themes/default";
| consumer | 使う入口 |
|---|---|
| DS 部品を 1 つでも使う(どこか 1 ルートでも) | ksk-design-system/preset(従来どおり。preset-core + safelist と同じ) |
DS 部品を使わない(トークン・class-names だけ) | ksk-design-system/preset-core |
Tailwind は CSS を 1 本にまとめるため、DS 部品を使うルートが 1 つでもあるビルドでは
preset を使ってください(preset-core を選ぶと、そのルートで部品の見た目が崩れます)。
preset-core と safelist を分けて読みたい場合は
@import "ksk-design-system/preset-core"; @import "ksk-design-system/safelist"; が
preset と同じ結果になります。
import { Button, Input, Label } from "ksk-design-system"
export function Example() {
return (
<form>
<Label htmlFor="company">会社名</Label>
<Input id="company" name="company" defaultValue="サンプル株式会社" />
<Button type="submit">保存</Button>
</form>
)
}
DSリポジトリの開発者向け検査として、npm run test:consumer:web は、配布READMEの上記例を空consumerで型検査・CSSビルドし、Node SSRからChromiumへのhydrationと操作を検査します。初回は npx playwright install chromium が必要です。
新規クライアント案件では、テーマファイルで --Primitive-Brand-500 などブランドカラーの 10 行を定義するだけで、全コンポーネントがそのブランドカラーで動作します。
既存の手書き CSS で作られた画面を DS へ移すときは、見た目を後回しにして挙動と
アクセシビリティだけ先に統一するのが最も安全です。生タグ(<button> / <input> /
<textarea>)を DS コンポーネントへ置き換え、既存クラスを className へ渡し、
unstyled を付けます。
// before — 生タグ + 手書き CSS
<button className="btn btn-primary" onClick={submit}>ログイン</button>
<textarea className="composer-input" value={text} onChange={onChange} />
// after — 挙動と a11y は DS、見た目は既存 CSS のまま
<Button unstyled className="btn btn-primary" onClick={submit}>ログイン</Button>
<Textarea unstyled className="composer-input" value={text} onChange={onChange} />
なぜ unstyled が要るのか。 手書き CSS は非レイヤー、DS のユーティリティは
@layer utilities なので、宣言が衝突すれば手書き側が勝ちます。しかし手書きクラスの
大半は height / white-space / font-weight / border-radius / background を宣言して
おらず、衝突しないプロパティは DS の base + variant がそのまま効きます。
className を渡すだけでは見た目は保てません。aikoibito web の実測では、
チャット入力欄が --Field-Min-Height: 5rem の流入で 43px → 80px に、
ログイン CTA が 52px → 40px に変わりました(issue #420)。
unstyled を付けたときに DS が出すのは次だけです。
| コンポーネント | 維持されるもの | 出さないもの |
|---|---|---|
Button | type 既定 "button" / disabled・aria-disabled クリックの抑止 / haptic / asChild | inline-flex items-center justify-center gap-* whitespace-nowrap typo-* cursor-pointer と variant・size・layout の全クラス |
Input | showCount(IME 追従)/ adornment の配置土台 | h-* w-full border-* bg-* px-* typo-* placeholder:* |
Textarea | autoGrow / showCount | min-h-* w-full border-* bg-* px-* py-* typo-* placeholder:* |
Label | Radix Label の挙動(クリックで対象コントロールへフォーカス移動) | typo-label-md text-[var(--Text-High-Emphasis)] peer-disabled:cursor-not-allowed peer-disabled:opacity-50 |
キーボード操作時の focus-visible リングだけは a11y のため既定で残ります(Label は
フォーカス可能要素ではないため対象外)。手書き CSS が自前のフォーカス表現を持つ場合は
className="focus-visible:ring-0" で消せます。
移行の順番は次のとおりです。
unstyled + 既存クラスへ置き換える(見た目は 1px も変わらない)npx ksk-ds lint src の P001〜P006 を通すunstyled を外すunstyled は移行の足場であって最終形ではありません。3 が済んだ画面から順に外してください。
consumer 側のローカル grep script が古くならないよう、DS 本体から contracts/rules.json を読む lint CLI を同梱しています。
npx ksk-ds lint src
npx ksk-ds lint src --format json
npx ksk-ds lint --changed
npx ksk-ds lint src --platform native
npx ksk-ds lint src --strict # warning も exit 1
npx ksk-ds lint src --max-warnings=0 # warning が N 件を超えたら exit 1
既定では severity: "error" のルールだけが exit code に影響します。a11y 系を含む
warning ルールも CI で締めたい場合は --strict か --max-warnings N を使ってください。
未知のオプションはエラー終了します(黙って捨てないので、CI に書いたフラグが
無言で効かないことはありません)。
ルールはファイルごとに capability(そのファイルがどの記法を持ちうるか) を判定して
出し分けます(contracts/rules.json の appliesTo)。
| ファイル | capability | 当たるルール |
|---|---|---|
| web(native シグナルなし) | dom tailwind web | 全部 |
React Native + className あり(NativeWind) | native tailwind | Tailwind 系は当たる/DOM 生タグ系と CSS 変数系は外れる |
React Native + className なし(StyleSheet) | native | Tailwind 系も外れる |
native の判定シグナルは *.native.tsx というファイル名、react-native /
ksk-design-system/native の import 文、StyleSheet.create の使用です。
自動判定が合わない場合は --platform web / --platform native で上書きできます
(ファイル判定より優先)。ただし --platform native を付けても Tailwind を持つかどうかは
ソースの className の有無で決まるので、NativeWind の consumer が Tailwind 系ルールを
失うことはありません。
appliesTo を持たないルール(#hex 直書きの P008 等)は従来どおり全ファイルに適用され、
native では RN 向けの fix 文言を表示します。
出力は file:line rule severity fix を含みます。severity の語彙は error / warning で、
contracts/rules.json と JSON 出力・text 出力すべてで一致しています。
どうしても DS で表現できない箇所は、ルール単位・理由付きの escape コメントを置きます
(理由は 5 文字以上が必須。空虚な理由は ESCAPE002 として報告され、ignore は効きません)。
// ksk-ds-lint-ignore P008 -- ブランドロゴの規定色のため
const BRAND = "#06C755"
| 書き方 | スコープ |
|---|---|
// ksk-ds-lint-ignore P008 -- 理由 | そのコメント行から下 2 行以内・指定ルールのみ |
// ksk-ds-lint-ignore-file P008 -- 理由 | そのファイル全体・指定ルールのみ |
// ksk-ds-allow-custom-ui: 理由 | そのファイル全体・全ルール(非推奨・後方互換) |
ksk-ds-allow-custom-ui はスコープが広すぎて将来の違反まで隠すため、新規では使わず
ルール単位の ksk-ds-lint-ignore を使ってください。escape マーカーはコメントの中でのみ
有効で、文字列リテラル(ドキュメント URL 等)の中では発火しません。
理由は 5 文字以上が必須です。また理由文字列に * は使えません
(*/ を誤って飲み込まないための制約で、* 以降は理由として認識されません)。
AUTO-GENERATED / DO NOT EDIT / 自動生成 等)を持つ
ファイルは全ルールを skip します。直す先は生成物ではなく生成元です。src/styles/*.css / src/themes/*.css / src/preset.css と、
それを再梱包したベンダリングコピー)は P049 / P050 の対象外です。トークンを定義している側で
あって、consumer による上書き・並行実装ではないためです。P049 は「DS の名前空間の変数(--Primitive-Brand-500 等)に触れている CSS」しか見ないため、DS を
一切使わない独自パレット(--bg / --accent / --surface 等)を持つ consumer には
無言でした。P050 は :root / .dark / [data-theme=...] のようなルート的セレクタの中で、
DS 名前空間に属さないカスタムプロパティへ色値(#hex / rgb() / hsl() / oklch())を
5 個以上定義していたら「並行パレットの疑い」を warn します。値が var(--Primitive-...) の
ように DS トークンを参照しているだけのものはカウントしません。意図的な独立パレット
(LINE 風チャット画面のライトなど)は // ksk-ds-lint-ignore P050 -- 理由 で抑制できます。
padding={false} 付け忘れ(issue #619)preset="plain" / side="float" / side="float-glass" の ResponsiveOverlayFrame は、
既定で p-6 と safe-area 込みの下余白を持ちます。ResponsiveOverlayFooter も自前で
safe-area 込みの下余白を持つため、併用するときは Frame に padding={false} が必要です。
付け忘れると下の余白が二重になるので、P051 が warn します。
誤検知を出さない方を優先し、side / preset が文字列リテラルで確定していて、Frame に
spread props({...props})が無く、padding が未指定・値なし・{true} で、Footer が
Frame の JSX の中に直接書かれているときだけ報告します。padding={cond} のような式、
Footer を別コンポーネントに切り出した構成、preset 経路(mobile-form 等。余白を持たない)は
対象外です。P046 と同じく TypeScript の構文解析を使うため、typescript が無いプロジェクトでは
P051 だけ skip して案内を1行出します。
className で形(角丸・高さ・左右 padding)を上書きしないButton / Input / Textarea / SelectTrigger / DateField / Chip / Skeleton などの角丸・高さ・
左右 padding は DS が token(--Control-Radius / --Field-Radius / --Field-Height-* /
--Field-Padding-X-*)と prop(size / rounded / unstyled / startAdornment)で持っています。
部品ごとに rounded-2xl / h-12 / px-4 を被せると、同じ画面の他の部品と形が揃わなくなるため
P052 が warn します(belle-todo で、ボタンが pill と角丸長方形で混在し、入力欄も欄ごとに角丸・高さが
ずれていた事例が由来)。
// ❌ 部品単位で形を被せる
<Button className="w-full rounded-2xl">保存</Button>
<Input className="h-12 rounded-2xl pl-8" /> // ¥ を自前で絶対配置
// ✅ 既定に任せ、変えたいときは product theme で 1 箇所だけ
<Button className="w-full">保存</Button>
<Input startAdornment="¥" />
アプリ全体でボタンや入力欄の角丸を変えたいときは、--Control-Radius / --Field-Radius を
product theme(P049 の許可リスト)で上書きします。枠なしのインライン編集欄は unstyled、
Skeleton の角丸は rounded prop を使います。
"ksk-design-system" から import した部品の className に書かれた文字列リテラル(cn() / 三項 /
テンプレートの固定部分を含む)だけを見ます。unstyled を付けた部品、h-auto / h-full / min-h-0
等のレイアウト用の高さ、変数経由のクラスは対象外です。ボタンをカード状のタイルとして使う等の
正当な例外は // ksk-ds-lint-ignore P052 -- 理由 で外します。P046 / P051 と同じく
typescript が無いプロジェクトでは skip します。
@layer の外のグローバル CSS で DS 部品の形を変えないCSS の仕様上、@layer の外に書いた宣言は @layer の中の宣言(Tailwind / DS のユーティリティは
@layer utilities)に詳細度と無関係に勝ちます。消費側の globals.css に layer 外で
タグ / role / 汎用属性セレクタの寸法・余白・角丸・文字サイズ・outline を書くと、DS 部品の
className では打ち消せず、DS 部品の形が壊れます。belle-todo では次がすべてこれでした。
| layer 外のルール | 起きたこと |
|---|---|
[role="radio"] { min-height: var(--touch-min) } | DS Chip(32px)が 44〜52px に膨らむ |
:focus-visible { border-radius: 8px } | pill のボタンがフォーカス中だけ角ばる / DS 部品のフォーカス枠が二重 |
input:focus-visible { border-radius: 16px } | 入力欄の角丸がフォーカス中だけ変わる |
nav { padding-bottom: env(safe-area-inset-bottom) } | 浮いたタブバーに safe-area が二重 |
/* ❌ layer 外 */
:focus-visible { outline: 2px solid var(--Brand-Primary); }
/* ✅ base に置けば DS 部品は DS のフォーカスリングが勝ち、素の要素だけにこの outline が当たる */
@layer base {
:focus-visible { outline: 2px solid var(--Brand-Primary); }
}
/* ✅ layer 外に残すなら DS 部品を外す */
a:not([data-slot]) { min-height: 44px; }
@layer / @theme の中、クラスで始まるセレクタ(消費側の独自クラス)、html / body / :root /
*、keyframes、data-slot を含むセレクタ(DS の公開 data-slot を明示的に狙う・外す)は対象外です。
DS 自身 / ベンダリングされた DS の CSS も P049 と同じく対象外。例外は
/* ksk-ds-lint-ignore P053 -- 理由 */ で外します。
ring-[var(--Focus-High-Emphasis)]/50 のようにフォーカスリングの色へ不透明度を付けると、実効コントラストが
WCAG 1.4.11(非テキスト 3:1)を割ります(旧実装は白の上で 1.34〜1.67:1、Gray-900 の上でも
2.43〜3.44:1)。DS はトークンをライト Brand-600 / ダーク Brand-400 に直し、リングを不透明で使う形に
揃えました。旧い書き方をコピーした consumer のクラスを P054(warning)が検出します。
/* ❌ 半透明 */
className="focus-visible:ring-[3px] focus-visible:ring-[var(--Focus-High-Emphasis)]/50"
/* ✅ 不透明 */
className="focus-visible:ring-[3px] focus-visible:ring-[var(--Focus-High-Emphasis)]"
/* ✅ Brand 塗りの面の上では、面と同色にならないよう 2px 離す */
className="bg-[var(--Brand-Primary)] focus-visible:ring-[3px] focus-visible:ring-[var(--Focus-High-Emphasis)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--Surface-Primary)]"
/数字 と /[任意値] の両方を検出します。shadcn 既定の ring-ring/50・ring-sidebar-ring/50(--ring / --sidebar-ring は Focus-High-Emphasis を指す)、Tailwind v4 の省略記法 ring-(--Focus-High-Emphasis)/50、フォールバック付きの ring-[var(--Focus-High-Emphasis,#3B82F6)]/50 も同じです。別トークン(ring-[var(--Caution-Base)]/20 等)は対象外です。
contracts/rules.json の除外指定は、当てる対象ごとに 3 つに分かれています。
| フィールド | 当てる対象 | 効く範囲 |
|---|---|---|
excludePaths | ファイルパス | 全リポジトリ(.stories. 等) |
excludeDsPaths | ファイルパス | DS パッケージ配下のファイルだけ(components/ui/ 等) |
excludeLines | 行の内容 | 全リポジトリ(data-slot / asChild 等) |
excludeDsPaths は「DS 自身の実装だから生タグを書いてよい」という意味の除外なので、
consumer が shadcn 既定の src/components/ui/ に部品を置いても中核ルールは無効化されません
(v1.63.x までは単一の excludes をパスと行内容の両方に OR で当てていたため、
このディレクトリ名だけで 13 ルールが同時に黙っていました)。
excludeLines の各値は literal 文字列として line.includes(value) で判定されます
(正規表現ではありません)。shadow-\[ のように正規表現エスケープした値を書くと、実ソースの
shadow-[var(--shadow-md)] と一致せず除外が一度も効きません(issue #463)。
scripts/check-rules-contract.mjs(npm run check に組み込み済み)が excludeLines /
excludePaths / excludeDsPaths 内のバックスラッシュ混入を検出します。
className={\...${...}...`} の補間部(${...})が**文字列リテラルのみで構成される式** (単純三項 cond ? "a" : "b"/&&` / ネスト三項)なら、Tailwind の静的クラス抽出を壊さないため
P029 の対象外です。CLAUDE.md の実装前セルフチェックにある「クラス名は完全な文字列で書く。分岐は
三項演算子か cva variant で」という方針と一致させています。
識別子・関数呼び出し・メンバーアクセスの補間(`bg-${color}` 等)や、テンプレートリテラルの
入れ子は静的抽出できないため引き続き検出します。cn() / clsx() / CVA を使ってください。
&& は左辺が条件で出力されないため右辺だけを見ますが、|| は左辺が truthy ならその値が
そのまま出力されるので両辺を見ます(issue #468)。したがって
`base ${props.className || ""}` は `base ${props.className ?? ""}` と同じく検出対象です。
このパッケージは ESM-only です。CJS との dual build は配布せず、Jest
側で DS とその ESM 依存を babel-jest の変換対象にします。Vitest など
ESM をネイティブに扱うランナーでは、以下の設定は不要です。
Jest 29 では Babel 7 系を使います(Babel 8 は babel-jest@29 の peer
範囲外です)。
npm install --save-dev \
[email protected] [email protected] [email protected] \
@babel/core@^7.28.0 @babel/preset-env@^7.28.0 \
@babel/preset-react@^7.28.0 @babel/preset-typescript@^7.28.0
// babel.config.cjs
module.exports = {
presets: [
["@babel/preset-env", { targets: { node: "current" } }],
["@babel/preset-react", { runtime: "automatic" }],
["@babel/preset-typescript", { allExtensions: true, isTSX: true }],
],
}
// jest.config.cjs
module.exports = {
testEnvironment: "jsdom",
transform: {
"^.+\\.m?[jt]sx?$": "babel-jest",
},
transformIgnorePatterns: [
"<rootDir>/node_modules/.pnpm/(?!(ksk-design-system|radix-ui|iconsax-reactjs|@radix-ui\\+[^@]+)@)",
"node_modules/(?!.pnpm|ksk-design-system|radix-ui|@radix-ui|iconsax-reactjs)",
],
moduleNameMapper: {
"\\.(css|less|sass|scss)$": "<rootDir>/test/style-mock.cjs",
"^ksk-design-system/(preset(?:-core)?|safelist|styles(?:\\.css)?|glass|tokens/(?:primitive|semantic|typography|categorical|motion)|themes/(?:default|blue|orange|green|violet|cobalt))$":
"<rootDir>/test/style-mock.cjs",
},
}
// test/style-mock.cjs
module.exports = {}
この構成なら ksk-design-system をコンポーネント単位で mock せず、そのまま
render できます。リポジトリ内では、実際に npm pack した tgz を空の Jest
プロジェクトへインストールする再現テストを実行できます。
npm run test:jest-consumer
React Native / Expo の jest-expo でも考え方は同じです。既存の Expo preset
は維持し、transformIgnorePatterns の除外対象へ
ksk-design-system と利用する ESM peer を加えてください。
DS に存在する部品を consumer 側で再実装しないよう、コンポーネント名の重複検査を同梱しています。
npx ksk-ds check-duplicates
npx ksk-ds check-duplicates ./src --strict
既定は助言モードで終了コード 0、--strict は重複候補があると終了コード 1 です。正本は同梱の contracts/components.json であり、consumer 側に別の「昇格候補台帳」を作らないでください。
DS を import して委譲するだけの薄いラッパー(段階移行の型)は、DS と同名でも重複報告から除外され、ℹ ラッパー(DS 委譲済み)として除外 として件数のみ表示されます。
さらに、contracts/components.json と完全一致しない名前でも、DS 部品を連想させる命名パターン(PrimaryButton → Button / AppTextInput → Input / BottomSheet → Sheet / CandidateOverflowMenu → DropdownMenu / CrisisBanner → Banner / AvatarView → Avatar 等)は「重複の疑い」として ⚠ 付きで warn 表示されます。これは名前一致のみによる推測(AST は使いません)のため確度は下がり、--strict でも exit code には影響しません。誤検知の場合は無視して構いませんが、恒常的に抑制したい場合はそのファイル内のコメントに ksk-ds-local-fallback を含めてください。
ただし、ファイルが ksk-design-system(/native 等のサブパス含む)から named import し、その import した識別子を JSX またはコールとして実際に使っている場合は、名前が DS と異なっていても「DS 委譲ラッパー」とみなし疑い検出の対象から除外します(例: CandidateOverflowMenu が DropdownMenu を import して <DropdownMenu> として使うケース)。DS を import しているだけで実際には使っていない場合はこの除外の対象外です。
consumer アプリが DS の lint を外すときは、理由付きのコメントを残します(ksk-ds-lint-ignore / ksk-ds-lint-ignore-file / ksk-ds-allow-custom-ui / ksk-ds-local-fallback)。複数のアプリが同じ理由で DS を迂回していれば、それは DS に足りない部品・API の候補です。report-ignores はその理由コメントを複数リポ横断で集計します(読み取り専用)。
# 引数の各ディレクトリを 1 リポ(プロダクト)として集計
npx ksk-ds report-ignores ../app-a ../app-b ../app-c
npx ksk-ds report-ignores ../app-a ../app-b --min-repos 3 --rule P008
npx ksk-ds report-ignores ../app-a ../app-b --json
--min-repos(既定 2)以上のリポで同じルールが外されている」DS issue 候補を並べます。ksk-ds-local-fallback は理由中の issue #N も抽出します。--rule は P008 のようなルール ID のほか、ksk-ds-allow-custom-ui / ksk-ds-local-fallback を指定できます。--json は { repos, byRule, candidates, invalid } を出力します。.ts .tsx .js .jsx .mjs .css。node_modules / dist / build / .git / .expo / storybook-static 等は除外し、文字列リテラル中のマーカーは数えません。候補になったルールは、consumer 側に別の台帳を作らず、DS 側に issue を登録してください(DS依頼の登録を参照)。
動画・写真の上に文字や操作を置く場合は、--Text-on-Media と .text-on-media / .text-on-media-secondary、上下の .media-scrim-top / .media-scrim-bottom を使います。TikTok / Reels 型の操作群は MediaActionCluster が glass ボタン、ラベル、safe-area anchor、idle auto-hide をまとめて扱います。
import { MediaActionCluster } from "ksk-design-system"
<div className="relative">
<div className="absolute inset-x-0 top-0 h-32 media-scrim-top" />
<h1 className="text-on-media">メディア上のタイトル</h1>
<MediaActionCluster
anchor="bottom-right"
items={[
{ label: "いいね", icon: <HeartIcon />, active: true },
{ label: "シェア", icon: <ShareIcon /> },
]}
/>
</div>
フルスクリーン画面は Screen で固定高 root、本文の内部スクロール、下部 CTA の safe-area 余白をまとめて扱います。写真背景の入口画面やオンボーディングスライドは PhotoHero と typo-on-image を組み合わせます。
import { Screen, PhotoHero, Button } from "ksk-design-system"
<Screen scroll={false} padding="none">
<PhotoHero src="/onboarding/slide-1.jpg" overlay="dark">
<PhotoHero.Eyebrow>さあ、始めよう</PhotoHero.Eyebrow>
<PhotoHero.Title>ふたりの準備、ここから。</PhotoHero.Title>
<PhotoHero.Actions>
<Button variant="glass-inverse" className="w-full">はじめる</Button>
</PhotoHero.Actions>
</PhotoHero>
</Screen>
Web のグローバルナビで iOS 26 風の Liquid Glass を使う場合は、BottomTabBar variant="pill" を使います。実アプリの中央 CTA は centerAction、ラベル付き構成は showLabels、暗い写真・動画・gradient 上では tone="inverse" を指定します。
import { BottomTabBar } from "ksk-design-system"
<BottomTabBar
variant="pill"
items={[
{ label: "トーク", icon: <TalkIcon />, href: "/talk", isActive: true },
{ label: "ギャラリー", icon: <GalleryIcon />, href: "/gallery" },
]}
centerAction={{ label: "作成", icon: <PlusIcon />, href: "/create" }}
tone="inverse"
maxWidth={430}
/>
pillPosition は実アプリでは既定の fixed、Storybook や mobile shell 内のデモでは absolute を使います。safe-area は内部で env(safe-area-inset-bottom) を見ます。入力フォーム画面では keyboard 表示時に被らないよう、画面側で nav を隠すか bottom action に切り替えてください。
React Native は 0.78 以上(React 19 を同梱する最初の RN)が必要です。
import { useReduceMotion } from "ksk-design-system/native/ui"
const { reduceMotion, resolved } = useReduceMotion()
const suppressMotion = !resolved || reduceMotion
resolved は OS 設定の初回取得または設定変更イベントで true になります。
取得前・取得失敗時は false のため、suppressMotion の間は演出を開始せず、
実行中の演出は停止して元の表示へ戻してください。設定変更は即時反映され、
遅れて届いた初回取得結果では上書きしません。アンマウント時には購読を解除します。
この hook は native/ui の公開 API です。トークン専用の native エントリには含みません。
ksk-design-system/native/ui から直接 RN 用コンポーネント export(134 個)を import できます。iOS 26 の Liquid Glass にも対応:
import { ThemeProvider, Button, Card, GlassView } from "ksk-design-system/native/ui"
# Liquid Glass を本物の UIVisualEffectView で出したい場合
npx expo install expo-glass-effect expo-blur
Web は backdrop-filter で擬似、Android は半透明 surface でフォールバックします。
既存コンポーネントの確認は src/native/COMPONENT_LOOKUP.md、Expo Router / React Navigation の tab bar、native GlassView、Button loading、fullscreen/media/settings/attachment/mobile shell recipes は NATIVE_RECIPES.md を参照してください。
npx ksk-design-system demo
cd ksk-ds-demo
npm run dev # → http://localhost:5173 で全プロトタイプ
# or
npm run storybook # → http://localhost:6010 で全コンポーネントカタログ
npx ksk-design-system demo my-trial のように引数で名前指定もできます。
中身は git clone + npm install を自動でやるだけなので、もちろん手動でもOK:
git clone https://github.com/ekusiek716/ksk-design-system.git
cd ksk-design-system && npm install
Claude Code をこのリポで開いて:
/mock https://notion.so/your-spec
# or
/mock 「ECサイトの商品詳細画面、カート追加と画像ギャラリー付き」
DS コンポーネントを最大限活用したモックが src/prototypes/ に生成され、http://localhost:5173 のプレビューに即反映されます。スクラッチで UI を組まず、既存コンポーネントを組み合わせて作るのでブレません。
mcp-server/(ksk-ds-mcp)は DS の正本を MCP で配信する。リポジトリを clone した環境で npm run mcp:build → npm run mcp:start(stdio)。
| 種別 | 名前 | 内容 |
|---|---|---|
| tool | get_component / get_token / check_rule / search | コンポーネント・トークン・禁止ルールの照会と横断検索 |
| tool | list_contracts | 配信している正本の名前・説明・URI の一覧 |
| tool | get_contract | 正本を名前で取得。JSON は path(例: meta.counts)、Markdown は section(見出しの部分一致)で部分取得。大きい契約は目次のみ返し、full: true で全文 |
| resource | ksk-ds://contracts/<name> | contracts/*.json・tokens.json(components / rules / tokens / composition / screen-patterns 等) |
| resource | ksk-ds://docs/<name> | DESIGN.md・COMPONENT_LOOKUP.md(web / native) |
get_contract で同じ内容を取得できる。package.json の files)に同梱されるファイルだけ。新しい contracts/*.json を足したら mcp-server/src/tools/get-contract.ts の一覧にも追加する(mcp-server/__tests__/get-contract.test.ts が登録漏れと同梱漏れを検出する)。npm ci --prefix mcp-server が要る。CLAUDE.md / DESIGN.md を参照UPDATING.md を参照npm の公開統計から、インストール数を確認できます。
npm run metrics # 直近の DL 数・推移・バージョン別内訳を表示
npm run metrics -- --json # 生データを JSON で出力
npm run metrics -- --range last-year
npm は匿名集計のため、CI やミラーの重複を含む install 回数の目安 です(ユニークユーザー数ではありません)。
DSへ依頼するときは、DSの通常issueとappの取り込み待ち通常issueを作成する。DS正本の作業ブランチで node bin/init.js register-consumer-request --ds-issue N --consumer-issue URL を実行し、台帳変更をPRに含める。未公開CLIで古いnpm版を起動しないようsourceのコマンドを使う。公開済みCLIでは npx ksk-ds register-consumer-request も同じ引数で使える。
登録CLIは既存のgh認証で両issueを確認し、app issueへ ds:waiting を自動付与する。closedまたは ds:released のissueは降格させない。修正PR作成後は同じコマンドに --fix-pr N を付けて紐付ける。省略時は既存fixPrを維持し、累積台帳を削除しない。--dry-run は通信・書き込みなしのローカルプレビューで、issue実在や権限は確認しない。失敗は非zero。台帳保存後のラベル失敗は同じコマンドで再試行する。
公開通知はDSの一括bump(scripts/update-consumers.sh VERSION)時だけ実行する。公開版に修正が含まれることを確認し、bump PR付きの通知と ds:released を付ける。6時間pollingとapp側workflow/scriptは廃止し、init-release-notices は移行案内を出して停止する。app側の取り込み・動作確認が完了するまで追従issueは閉じない。詳しくは 公開通知手順 を参照。
同じapp issueに複数のDS依頼がある場合、公開台帳内の該当依頼がすべて公開・通知済みになるまで ds:waiting を残す(ds:released と併存する場合あり)。通知はrepo owner本人 ekusiek716 のPAT認証が前提。通知失敗時は既存bump PRを保持して同じversion・対象repoの一括bumpを再実行し、通知runtime単独実行・並列実行はしない。
MIT © 奥野圭祐 (Keisuke Okuno)