mikeneko-ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +199 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +123 -0
- package/package.json +27 -0
- package/templates/CLAUDE.md +306 -0
- package/templates/components.json +451 -0
- package/templates/prohibited.md +196 -0
- package/templates/theme.css +118 -0
- package/templates/tokens.json +159 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
3
|
+
import { resolve, dirname } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { createInterface } from "readline";
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const TEMPLATES = resolve(__dirname, "../templates");
|
|
8
|
+
const CWD = process.cwd();
|
|
9
|
+
// ── Colors ──
|
|
10
|
+
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
11
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
12
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
13
|
+
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
14
|
+
const red = (s) => `\x1b[31m${s}\x1b[0m`;
|
|
15
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
16
|
+
function log(icon, msg) {
|
|
17
|
+
console.log(` ${icon} ${msg}`);
|
|
18
|
+
}
|
|
19
|
+
// ── Prompt ──
|
|
20
|
+
function ask(question) {
|
|
21
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
rl.question(question, (answer) => {
|
|
24
|
+
rl.close();
|
|
25
|
+
resolve(answer.trim());
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function detectFramework() {
|
|
30
|
+
const pkgPath = resolve(CWD, "package.json");
|
|
31
|
+
if (!existsSync(pkgPath))
|
|
32
|
+
return null;
|
|
33
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
34
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
35
|
+
if (deps["next"])
|
|
36
|
+
return "nextjs";
|
|
37
|
+
if (deps["react"])
|
|
38
|
+
return "react";
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function detectShadcn() {
|
|
42
|
+
return existsSync(resolve(CWD, "components.json"));
|
|
43
|
+
}
|
|
44
|
+
// ── CSS file location ──
|
|
45
|
+
function findCssFile(framework) {
|
|
46
|
+
const candidates = framework === "nextjs"
|
|
47
|
+
? [
|
|
48
|
+
"src/app/globals.css",
|
|
49
|
+
"app/globals.css",
|
|
50
|
+
"src/globals.css",
|
|
51
|
+
"styles/globals.css",
|
|
52
|
+
]
|
|
53
|
+
: [
|
|
54
|
+
"src/index.css",
|
|
55
|
+
"src/globals.css",
|
|
56
|
+
"src/app.css",
|
|
57
|
+
"index.css",
|
|
58
|
+
];
|
|
59
|
+
for (const c of candidates) {
|
|
60
|
+
if (existsSync(resolve(CWD, c)))
|
|
61
|
+
return c;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
// ── Theme injection ──
|
|
66
|
+
function injectTheme(cssPath) {
|
|
67
|
+
const fullPath = resolve(CWD, cssPath);
|
|
68
|
+
const original = readFileSync(fullPath, "utf-8");
|
|
69
|
+
const themeBlock = readFileSync(resolve(TEMPLATES, "theme.css"), "utf-8");
|
|
70
|
+
// Check if already injected
|
|
71
|
+
if (original.includes("mikeneko UI Theme")) {
|
|
72
|
+
log(yellow("●"), `Theme already exists in ${cssPath}`);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
// Find :root block and replace, or append
|
|
76
|
+
if (original.includes(":root {")) {
|
|
77
|
+
// Replace existing :root and .dark blocks with melta theme
|
|
78
|
+
const cleaned = original
|
|
79
|
+
.replace(/:root\s*\{[^}]*\}/s, "/* :root replaced by mikeneko-ui */")
|
|
80
|
+
.replace(/\.dark\s*\{[^}]*\}/s, "/* .dark replaced by mikeneko-ui */");
|
|
81
|
+
writeFileSync(fullPath, cleaned + "\n" + themeBlock);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
writeFileSync(fullPath, original + "\n" + themeBlock);
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
// ── File writers ──
|
|
89
|
+
function writeIfNotExists(relativePath, content) {
|
|
90
|
+
const fullPath = resolve(CWD, relativePath);
|
|
91
|
+
if (existsSync(fullPath)) {
|
|
92
|
+
log(yellow("●"), `${relativePath} already exists, skipping`);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const dir = dirname(fullPath);
|
|
96
|
+
if (!existsSync(dir))
|
|
97
|
+
mkdirSync(dir, { recursive: true });
|
|
98
|
+
writeFileSync(fullPath, content);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
function copyTemplate(templateName, destPath) {
|
|
102
|
+
const content = readFileSync(resolve(TEMPLATES, templateName), "utf-8");
|
|
103
|
+
return writeIfNotExists(destPath, content);
|
|
104
|
+
}
|
|
105
|
+
function writeMcpJson() {
|
|
106
|
+
const mcpPath = resolve(CWD, ".mcp.json");
|
|
107
|
+
const mcpConfig = {
|
|
108
|
+
mcpServers: {
|
|
109
|
+
"mikeneko-ui": {
|
|
110
|
+
command: "npx",
|
|
111
|
+
args: ["-y", "mikeneko-ui", "mcp"],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
if (existsSync(mcpPath)) {
|
|
116
|
+
// Merge into existing
|
|
117
|
+
const existing = JSON.parse(readFileSync(mcpPath, "utf-8"));
|
|
118
|
+
if (existing.mcpServers?.["mikeneko-ui"]) {
|
|
119
|
+
log(yellow("●"), ".mcp.json already has mikeneko-ui entry");
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
existing.mcpServers = existing.mcpServers || {};
|
|
123
|
+
existing.mcpServers["mikeneko-ui"] = mcpConfig.mcpServers["mikeneko-ui"];
|
|
124
|
+
writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + "\n");
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
// ── Main ──
|
|
131
|
+
async function main() {
|
|
132
|
+
const args = process.argv.slice(2);
|
|
133
|
+
// Sub-command: mcp server mode
|
|
134
|
+
if (args[0] === "mcp") {
|
|
135
|
+
const { startMcp } = await import("./mcp.js");
|
|
136
|
+
await startMcp();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
console.log();
|
|
140
|
+
console.log(bold(" mikeneko UI") + dim(" — AI-ready design system"));
|
|
141
|
+
console.log();
|
|
142
|
+
// Step 1: Detect or ask framework
|
|
143
|
+
let framework = detectFramework();
|
|
144
|
+
if (framework) {
|
|
145
|
+
log(green("✔"), `Detected: ${bold(framework === "nextjs" ? "Next.js" : "React (Vite)")}`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.log(" Which framework?");
|
|
149
|
+
console.log(` ${bold("1")} React (Vite)`);
|
|
150
|
+
console.log(` ${bold("2")} Next.js`);
|
|
151
|
+
console.log();
|
|
152
|
+
const choice = await ask(" Enter (1/2): ");
|
|
153
|
+
framework = choice === "2" ? "nextjs" : "react";
|
|
154
|
+
}
|
|
155
|
+
// Step 2: Check shadcn/ui
|
|
156
|
+
if (!detectShadcn()) {
|
|
157
|
+
console.log();
|
|
158
|
+
log(red("✖"), "shadcn/ui not found (no components.json)");
|
|
159
|
+
console.log();
|
|
160
|
+
console.log(` Run this first:`);
|
|
161
|
+
console.log(cyan(` npx shadcn@latest init`));
|
|
162
|
+
console.log();
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
log(green("✔"), "shadcn/ui detected");
|
|
166
|
+
// Step 3: Find CSS file
|
|
167
|
+
const cssFile = findCssFile(framework);
|
|
168
|
+
if (!cssFile) {
|
|
169
|
+
log(red("✖"), "Could not find globals.css / index.css");
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
console.log();
|
|
173
|
+
// Step 4: Inject theme
|
|
174
|
+
if (injectTheme(cssFile)) {
|
|
175
|
+
log(green("✔"), `Injected mikeneko-ui theme into ${bold(cssFile)}`);
|
|
176
|
+
}
|
|
177
|
+
// Step 5: Copy CLAUDE.md
|
|
178
|
+
if (copyTemplate("CLAUDE.md", "CLAUDE.md")) {
|
|
179
|
+
log(green("✔"), `Created ${bold("CLAUDE.md")}`);
|
|
180
|
+
}
|
|
181
|
+
// Step 6: Copy prohibited.md
|
|
182
|
+
if (copyTemplate("prohibited.md", "foundations/prohibited.md")) {
|
|
183
|
+
log(green("✔"), `Created ${bold("foundations/prohibited.md")}`);
|
|
184
|
+
}
|
|
185
|
+
// Step 7: Write .mcp.json
|
|
186
|
+
if (writeMcpJson()) {
|
|
187
|
+
log(green("✔"), `Added mikeneko-ui to ${bold(".mcp.json")}`);
|
|
188
|
+
}
|
|
189
|
+
console.log();
|
|
190
|
+
log("🎨", bold("Done!") + " Your project now uses mikeneko-ui design tokens.");
|
|
191
|
+
console.log();
|
|
192
|
+
console.log(dim(" AI tools will read CLAUDE.md and use MCP for design rules."));
|
|
193
|
+
console.log(dim(" Run `npx shadcn@latest add button` to add components."));
|
|
194
|
+
console.log();
|
|
195
|
+
}
|
|
196
|
+
main().catch((err) => {
|
|
197
|
+
console.error(red("Error:"), err.message);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
});
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startMcp(): Promise<void>;
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
// Lazy-load MCP SDK only when mcp subcommand is used
|
|
4
|
+
export async function startMcp() {
|
|
5
|
+
const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
|
|
6
|
+
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
7
|
+
const { CallToolRequestSchema, ListToolsRequestSchema, } = await import("@modelcontextprotocol/sdk/types.js");
|
|
8
|
+
const CWD = process.cwd();
|
|
9
|
+
function loadFile(path) {
|
|
10
|
+
const full = resolve(CWD, path);
|
|
11
|
+
if (!existsSync(full))
|
|
12
|
+
return null;
|
|
13
|
+
return readFileSync(full, "utf-8");
|
|
14
|
+
}
|
|
15
|
+
function loadJSON(path) {
|
|
16
|
+
const content = loadFile(path);
|
|
17
|
+
return content ? JSON.parse(content) : null;
|
|
18
|
+
}
|
|
19
|
+
// Try to find templates dir (installed via npm or local dev)
|
|
20
|
+
const { dirname } = await import("path");
|
|
21
|
+
const { fileURLToPath } = await import("url");
|
|
22
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const TEMPLATES = resolve(__dirname, "../templates");
|
|
24
|
+
function loadTemplate(name) {
|
|
25
|
+
const full = resolve(TEMPLATES, name);
|
|
26
|
+
if (!existsSync(full))
|
|
27
|
+
return null;
|
|
28
|
+
return readFileSync(full, "utf-8");
|
|
29
|
+
}
|
|
30
|
+
const server = new Server({ name: "mikeneko-ui-mcp", version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
31
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
32
|
+
tools: [
|
|
33
|
+
{
|
|
34
|
+
name: "get_token",
|
|
35
|
+
description: "mikeneko UI デザイントークンを取得。category: color, typography, spacing, elevation, radius, motion, zIndex, wireframe",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
category: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "トークンカテゴリ",
|
|
42
|
+
enum: ["color", "typography", "spacing", "elevation", "radius", "motion", "zIndex", "wireframe"],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
required: ["category"],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "get_component",
|
|
50
|
+
description: "shadcn/ui コンポーネント仕様を取得。name省略で全一覧。",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: {
|
|
54
|
+
name: { type: "string", description: "コンポーネント名 (例: Button, Card)" },
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "get_prohibited",
|
|
60
|
+
description: "mikeneko UI 禁止パターンを取得。",
|
|
61
|
+
inputSchema: {
|
|
62
|
+
type: "object",
|
|
63
|
+
properties: {
|
|
64
|
+
section: { type: "string", description: "特定セクションのみ(省略で全量)" },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "get_quick_reference",
|
|
70
|
+
description: "CLAUDE.md (AI Quick Reference) を返す。",
|
|
71
|
+
inputSchema: {
|
|
72
|
+
type: "object",
|
|
73
|
+
properties: {},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
}));
|
|
78
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
79
|
+
const { name, arguments: args } = request.params;
|
|
80
|
+
const a = args;
|
|
81
|
+
const text = (s) => ({ content: [{ type: "text", text: s }] });
|
|
82
|
+
switch (name) {
|
|
83
|
+
case "get_token": {
|
|
84
|
+
// Try project-local tokens first, then bundled template
|
|
85
|
+
const tokens = loadJSON("tokens/tokens.json") ?? loadJSON(resolve(TEMPLATES, "tokens.json"));
|
|
86
|
+
if (!tokens)
|
|
87
|
+
return text("tokens.json not found");
|
|
88
|
+
const result = tokens[a.category];
|
|
89
|
+
return result ? text(JSON.stringify(result, null, 2)) : text(`Unknown category: ${a.category}`);
|
|
90
|
+
}
|
|
91
|
+
case "get_component": {
|
|
92
|
+
const data = loadJSON("metadata/components.json") ?? loadJSON(resolve(TEMPLATES, "components.json"));
|
|
93
|
+
if (!data)
|
|
94
|
+
return text("components.json not found");
|
|
95
|
+
if (!a.name) {
|
|
96
|
+
const list = data.components.map((c) => `${c.name} (${c.category})`);
|
|
97
|
+
return text(`Available (${list.length}):\n${list.join("\n")}`);
|
|
98
|
+
}
|
|
99
|
+
const comp = data.components.find((c) => c.name.toLowerCase() === a.name.toLowerCase());
|
|
100
|
+
return comp ? text(JSON.stringify(comp, null, 2)) : text(`Not found: ${a.name}`);
|
|
101
|
+
}
|
|
102
|
+
case "get_prohibited": {
|
|
103
|
+
const content = loadFile("foundations/prohibited.md") ?? loadTemplate("prohibited.md");
|
|
104
|
+
if (!content)
|
|
105
|
+
return text("prohibited.md not found");
|
|
106
|
+
if (!a.section)
|
|
107
|
+
return text(content);
|
|
108
|
+
const regex = new RegExp(`(##+ .*${a.section}[\\s\\S]*?)(?=\\n##[^#]|\\n---\\n|$)`, "i");
|
|
109
|
+
const match = content.match(regex);
|
|
110
|
+
return text(match ? match[1].trim() : `Section "${a.section}" not found`);
|
|
111
|
+
}
|
|
112
|
+
case "get_quick_reference": {
|
|
113
|
+
const content = loadFile("CLAUDE.md") ?? loadTemplate("CLAUDE.md");
|
|
114
|
+
return text(content ?? "CLAUDE.md not found");
|
|
115
|
+
}
|
|
116
|
+
default:
|
|
117
|
+
return text(`Unknown tool: ${name}`);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
const transport = new StdioServerTransport();
|
|
121
|
+
await server.connect(transport);
|
|
122
|
+
console.error("mikeneko UI MCP Server running on stdio");
|
|
123
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mikeneko-ui",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "mikeneko UI — AI-ready design system CLI. Injects theme + rules into your React/Next.js project.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mikeneko-ui": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsx src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"templates"
|
|
16
|
+
],
|
|
17
|
+
"keywords": ["design-system", "shadcn", "tailwind", "react", "nextjs", "mcp"],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"typescript": "^5.4.0",
|
|
24
|
+
"@types/node": "^20.0.0",
|
|
25
|
+
"tsx": "^4.0.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# mikeneko UI — AI Quick Reference
|
|
2
|
+
|
|
3
|
+
> ShadCN/ui ベースのデザインシステム。Primary: Blue (#2b70ef)、日本語ファーストのタイポグラフィ。
|
|
4
|
+
|
|
5
|
+
## Design Tokens (抜粋)
|
|
6
|
+
|
|
7
|
+
### Colors
|
|
8
|
+
```
|
|
9
|
+
Primary: #2b70ef (500), #f0f5ff (50), #07194e (950)
|
|
10
|
+
Text: #0f172a (foreground/heading), #3d4b5f (body), #64748b (muted)
|
|
11
|
+
Border: #e2e8f0
|
|
12
|
+
Ring: #2b70ef (primary)
|
|
13
|
+
Success: #059669
|
|
14
|
+
Warning: #d97706
|
|
15
|
+
Danger: #ef4444
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Typography
|
|
19
|
+
```
|
|
20
|
+
Font: Inter, Hiragino Sans, Hiragino Kaku Gothic ProN, Noto Sans JP, sans-serif
|
|
21
|
+
H1: 32px/1.4 bold (text-3xl)
|
|
22
|
+
H2: 26px/1.4 bold (text-2xl)
|
|
23
|
+
H3: 22px/1.4 semibold (text-xl)
|
|
24
|
+
Body: 18px/2.0 normal (text-base) letter-spacing: 0.02em
|
|
25
|
+
Small: 15px/1.7 (text-sm)
|
|
26
|
+
XS: 13px/1.4 (text-xs)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Spacing
|
|
30
|
+
4px grid (Tailwind default). Prefer multiples of 8.
|
|
31
|
+
|
|
32
|
+
### Elevation
|
|
33
|
+
none → sm → md → overlay (shadow-xl max)
|
|
34
|
+
|
|
35
|
+
### Radius
|
|
36
|
+
sm(4px), md(8px), lg(12px), full — globals.css: `--radius: 0.75rem`
|
|
37
|
+
|
|
38
|
+
### Motion
|
|
39
|
+
Fast: 150ms, Normal: 200ms, Slow: 300ms (max)
|
|
40
|
+
Easing: ease-in-out (default)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 設計原則(5つ)
|
|
45
|
+
|
|
46
|
+
1. **Layered** — Background → Surface → Text/Object の3層でUIを構成する
|
|
47
|
+
2. **Contrast** — テキストは背景に対してWCAG 2.1準拠(4.5:1以上)
|
|
48
|
+
3. **Semantic** — 色は用途で指定する(`bg-primary` ≠ 生の `bg-blue-500`)
|
|
49
|
+
4. **Minimal** — 1つのViewに使う色は3色まで(背景・アクセント・テキスト)
|
|
50
|
+
5. **Grid** — スペーシングは4の倍数を基本、8の倍数を推奨する
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Components (45種 — shadcn/ui ベース)
|
|
55
|
+
|
|
56
|
+
### Action
|
|
57
|
+
- **Button**: default / destructive / outline / secondary / ghost / link × sm / default / lg / icon
|
|
58
|
+
|
|
59
|
+
### Input
|
|
60
|
+
- **Input**: default / disabled / error
|
|
61
|
+
- **Textarea**: default / disabled
|
|
62
|
+
- **Select**: SelectTrigger + SelectContent + SelectItem
|
|
63
|
+
- **Checkbox**: unchecked / checked / indeterminate / disabled
|
|
64
|
+
- **RadioGroup**: RadioGroupItem で構成
|
|
65
|
+
- **Switch**: on / off / disabled(旧 Toggle switch)
|
|
66
|
+
- **Slider**: single / range
|
|
67
|
+
- **Calendar**: single / range date selection
|
|
68
|
+
- **InputOTP**: verification code input
|
|
69
|
+
- **Toggle** / **ToggleGroup**: pressed state buttons
|
|
70
|
+
- **Label**: フォームラベル
|
|
71
|
+
- **Command**: 検索可能なコマンドパレット
|
|
72
|
+
|
|
73
|
+
### Data Display
|
|
74
|
+
- **Badge**: default / secondary / destructive / outline
|
|
75
|
+
- **Avatar**: AvatarImage / AvatarFallback
|
|
76
|
+
- **Table**: TableHeader + TableBody + TableRow + TableHead + TableCell
|
|
77
|
+
- **Card**: CardHeader + CardTitle + CardDescription + CardContent + CardFooter
|
|
78
|
+
- **Carousel**: horizontal / vertical
|
|
79
|
+
- **Chart**: area / bar / line / pie / radar / radial (Recharts)
|
|
80
|
+
|
|
81
|
+
### Navigation
|
|
82
|
+
- **Breadcrumb**: BreadcrumbList + BreadcrumbItem + BreadcrumbLink + BreadcrumbPage
|
|
83
|
+
- **Tabs**: TabsList + TabsTrigger + TabsContent
|
|
84
|
+
- **Pagination**: PaginationContent + PaginationItem + PaginationLink
|
|
85
|
+
- **NavigationMenu**: horizontal nav
|
|
86
|
+
- **Menubar**: application menubar
|
|
87
|
+
- **Sidebar**: SidebarProvider ベース、collapsible 対応
|
|
88
|
+
|
|
89
|
+
### Feedback
|
|
90
|
+
- **Alert**: default / destructive
|
|
91
|
+
- **Sonner**: toast() で呼び出し(旧 Toast)
|
|
92
|
+
- **Progress**: determinate / indeterminate
|
|
93
|
+
- **Skeleton**: loading placeholder
|
|
94
|
+
|
|
95
|
+
### Overlay
|
|
96
|
+
- **Dialog**: modal dialog(旧 Modal)
|
|
97
|
+
- **AlertDialog**: confirmation dialog
|
|
98
|
+
- **Sheet**: slide-in panel (top/right/bottom/left)
|
|
99
|
+
- **Drawer**: bottom drawer
|
|
100
|
+
- **DropdownMenu**: action menu(旧 Dropdown)
|
|
101
|
+
- **ContextMenu**: right-click menu
|
|
102
|
+
- **HoverCard**: hover popover
|
|
103
|
+
- **Popover**: generic popover
|
|
104
|
+
- **Tooltip**: TooltipProvider でラップ必須
|
|
105
|
+
|
|
106
|
+
### Disclosure
|
|
107
|
+
- **Accordion**: single / multiple collapsible sections
|
|
108
|
+
- **Collapsible**: toggle content visibility
|
|
109
|
+
|
|
110
|
+
### Layout
|
|
111
|
+
- **AspectRatio**: 1:1 / 16:9 / 4:3 / 3:2
|
|
112
|
+
- **Separator**: horizontal / vertical(旧 Divider)
|
|
113
|
+
- **ScrollArea**: custom scrollbar
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Tailwind CSS テーマ適用
|
|
118
|
+
|
|
119
|
+
```css
|
|
120
|
+
/* app/src/app/globals.css — Primary: #2b70ef (oklch) */
|
|
121
|
+
:root {
|
|
122
|
+
--primary: oklch(0.5765 0.2038 261.31); /* #2b70ef */
|
|
123
|
+
--primary-foreground: oklch(1.0000 0 0);
|
|
124
|
+
--background: oklch(0.9846 0.0017 247.84); /* #f9fafb */
|
|
125
|
+
--foreground: oklch(0.2077 0.0398 265.75); /* #0f172a */
|
|
126
|
+
--muted-foreground: oklch(0.5544 0.0407 257.42); /* #64748b */
|
|
127
|
+
--border: oklch(0.9288 0.0126 255.51); /* #e2e8f0 */
|
|
128
|
+
--ring: oklch(0.5765 0.2038 261.31);
|
|
129
|
+
--destructive: oklch(0.6368 0.2078 25.33); /* #ef4444 */
|
|
130
|
+
--radius: 0.75rem;
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 禁止パターン要約
|
|
137
|
+
|
|
138
|
+
| 禁止 | 代替 |
|
|
139
|
+
|------|------|
|
|
140
|
+
| `text-black` | `text-foreground` / `text-slate-900` |
|
|
141
|
+
| `bg-gray-300`以上の背景 | `bg-muted` / `bg-gray-50` 〜 `bg-gray-200` |
|
|
142
|
+
| `rounded-none` on cards | Card コンポーネントのデフォルト radius |
|
|
143
|
+
| `shadow-lg` / `shadow-2xl` | `shadow-sm` 〜 `shadow-md`(オーバーレイ: `shadow-xl`) |
|
|
144
|
+
| `border-gray-100` | `border-border` / `border-slate-200` |
|
|
145
|
+
| `text-gray-400` for body | `text-muted-foreground` |
|
|
146
|
+
| カード/Alert の `border-t-4` / `border-l-4` | shadcn Alert/Card のデフォルトスタイル |
|
|
147
|
+
| `bg-indigo-*` / `bg-blue-*` ハードコード | `bg-primary` / CSS変数を使用 |
|
|
148
|
+
| `tracking-tight` | 見出し 0.01em、本文 0.02em |
|
|
149
|
+
| 色だけで情報伝達 | アイコン/テキストを併用 |
|
|
150
|
+
| 300ms超のアニメーション | 150〜300ms に制限 |
|
|
151
|
+
| `<th>` の `scope` 省略 | Table コンポーネント使用 |
|
|
152
|
+
|
|
153
|
+
> 全禁止パターン(76項目): `foundations/prohibited.md` 参照
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## ファイル構成
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
mikeneko-ui/
|
|
161
|
+
├── CLAUDE.md ← このファイル (AI Quick Reference)
|
|
162
|
+
├── tokens/
|
|
163
|
+
│ └── tokens.json ← デザイントークン (SSOT)
|
|
164
|
+
├── metadata/
|
|
165
|
+
│ └── components.json ← コンポーネント仕様 (shadcn/ui ベース)
|
|
166
|
+
├── foundations/ ← デザイン基盤ドキュメント (10ファイル)
|
|
167
|
+
│ ├── color.md
|
|
168
|
+
│ ├── typography.md
|
|
169
|
+
│ ├── spacing.md
|
|
170
|
+
│ ├── elevation.md
|
|
171
|
+
│ ├── motion.md
|
|
172
|
+
│ ├── radius.md
|
|
173
|
+
│ ├── icons.md
|
|
174
|
+
│ ├── accessibility.md
|
|
175
|
+
│ ├── emotional-feedback.md
|
|
176
|
+
│ ├── design_philosophy.md
|
|
177
|
+
│ └── prohibited.md ← 禁止パターン (76項目)
|
|
178
|
+
├── components/ ← コンポーネント詳細ドキュメント
|
|
179
|
+
├── patterns/ ← パターンドキュメント (5ファイル)
|
|
180
|
+
├── ai/
|
|
181
|
+
│ └── mcp/ ← MCP Server
|
|
182
|
+
│ ├── src/index.ts
|
|
183
|
+
│ └── dist/index.js
|
|
184
|
+
├── app/ ← Next.js + shadcn/ui 実装
|
|
185
|
+
│ ├── src/
|
|
186
|
+
│ │ ├── app/globals.css ← テーマ (CSS変数)
|
|
187
|
+
│ │ ├── components/ui/ ← shadcn/ui コンポーネント (45個)
|
|
188
|
+
│ │ └── lib/utils.ts ← cn() ユーティリティ
|
|
189
|
+
│ └── components.json ← shadcn/ui 設定
|
|
190
|
+
└── docs/ ← ドキュメントサイト
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## MCP Server
|
|
196
|
+
|
|
197
|
+
5つのツールを提供:
|
|
198
|
+
|
|
199
|
+
| ツール | 説明 |
|
|
200
|
+
|--------|------|
|
|
201
|
+
| `get_token` | デザイントークン取得 (color, typography, spacing, etc.) |
|
|
202
|
+
| `get_component` | コンポーネント仕様取得 (shadcn/ui ベース) |
|
|
203
|
+
| `get_foundation` | ファウンデーション MD 取得 |
|
|
204
|
+
| `get_prohibited` | 禁止パターン取得 |
|
|
205
|
+
| `get_quick_reference` | この CLAUDE.md を返す |
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"mcpServers": {
|
|
210
|
+
"mikeneko-ui": {
|
|
211
|
+
"command": "node",
|
|
212
|
+
"args": ["./ai/mcp/dist/index.js"]
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## タスクベース読み込みガイド
|
|
221
|
+
|
|
222
|
+
| タスク | 読み込むファイル(順序) |
|
|
223
|
+
|--------|------------------------|
|
|
224
|
+
| 単体コンポーネント生成 | CLAUDE.md のみ |
|
|
225
|
+
| ページ生成 | + foundations/theme.md → patterns/layout.md → 関連 component md |
|
|
226
|
+
| ダークモード対応 | + foundations/theme.md → foundations/color.md |
|
|
227
|
+
| フォーム画面 | + patterns/form.md → Input / Select / Checkbox / Button |
|
|
228
|
+
| データ一覧 | + Table → Pagination → Badge |
|
|
229
|
+
| ダッシュボード | + Card / Table / Progress / Chart / Badge |
|
|
230
|
+
| 設定画面 | + Tabs → Switch / Select / RadioGroup |
|
|
231
|
+
| モーダル / 確認 | + Dialog / AlertDialog → Button |
|
|
232
|
+
| Loading / 空状態 | + Skeleton → interaction-states.md |
|
|
233
|
+
| 通知フィードバック | + Sonner → Alert → interaction-states.md |
|
|
234
|
+
| サイドバー付きページ | + Sidebar → layout.md |
|
|
235
|
+
| ナビゲーション | + navigation.md → Sidebar → Tabs / Breadcrumb |
|
|
236
|
+
| レスポンシブ対応 | + patterns/responsive.md → layout.md |
|
|
237
|
+
| アクセシビリティ確認 | + foundations/accessibility.md |
|
|
238
|
+
| テーマカスタマイズ | foundations/theme.md + app/src/app/globals.css |
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## テーマ・カラー変数・ダークモード
|
|
243
|
+
|
|
244
|
+
> テーマ設定・CSS変数定義: `app/src/app/globals.css` を参照。
|
|
245
|
+
|
|
246
|
+
| 設定 | 値 |
|
|
247
|
+
|------|-----|
|
|
248
|
+
| **ダークモード** | `OFF` |
|
|
249
|
+
|
|
250
|
+
- `OFF`: ライトモードのみで設計・生成する(デフォルト)
|
|
251
|
+
- `ON`: ダークモード対応を含めて設計・生成する(`.dark` セクション参照)
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Design Context
|
|
256
|
+
|
|
257
|
+
### Users
|
|
258
|
+
- **対象**: B2B / B2C 両方の汎用デザインシステム
|
|
259
|
+
- **エンドユーザー**: 業務SaaSを使うビジネスパーソンから、ECサイト・予約サービスの一般消費者まで
|
|
260
|
+
- **利用コンテキスト**: ダッシュボード、管理画面、EC、予約、学習、医療、行政など幅広いドメイン
|
|
261
|
+
- **DSの消費者**: 人間の開発者・デザイナー、および Claude Code / Cursor 等の AI コード生成エージェント
|
|
262
|
+
|
|
263
|
+
### Brand Personality
|
|
264
|
+
- **3語で表すと**: 静謐・精緻・温もり(Quiet · Precise · Warm)
|
|
265
|
+
- **声のトーン**: 「声を張らずに伝わる」— 主張しすぎない、でも確かに伝わる
|
|
266
|
+
- **コアメタファー**: 「機能的な黒子であり、たまに微笑む」
|
|
267
|
+
- **感情目標**: 心地よい集中 → 洗練された効率 → 穏やかな親しみ(この順で優先)
|
|
268
|
+
|
|
269
|
+
### Aesthetic Direction
|
|
270
|
+
- **ビジュアルトーン**: ミニマルだが冷たくない。フラットな基盤に Background → Surface → Text の3層で奥行きを出す
|
|
271
|
+
- **参考プロダクト**: Linear / Notion / Stripe / Vercel
|
|
272
|
+
- **アンチリファレンス**: 派手なグラデーション・ネオンカラー、Bootstrap 的な没個性テンプレート
|
|
273
|
+
|
|
274
|
+
### Design Principles
|
|
275
|
+
1. **Content First** — UIは黒子。コンテンツが主役であり、装飾ではなく構造で伝える
|
|
276
|
+
2. **Calm Confidence** — 信頼感を静かに醸成する。過剰な演出より、正確なスペーシングとコントラストで品質を示す
|
|
277
|
+
3. **Inclusive by Default** — WCAG 2.1 AA準拠はオプションではなくデフォルト
|
|
278
|
+
4. **Systematic Warmth** — 4px グリッド・セマンティックカラー・制限されたシャドウで一貫性を保ちつつ、人間味を添える
|
|
279
|
+
5. **Machine-Readable** — トークン・メタデータ・セマンティック命名により、AIエージェントが正確にUIを生成できる
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## 参照先
|
|
284
|
+
|
|
285
|
+
- **トークン全量**: `tokens/tokens.json`
|
|
286
|
+
- **コンポーネント全量**: `metadata/components.json`
|
|
287
|
+
- **禁止パターン全量**: `foundations/prohibited.md`
|
|
288
|
+
- **ShadCN/ui docs**: `https://ui.shadcn.com/docs`
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## デプロイ
|
|
293
|
+
|
|
294
|
+
| 項目 | 値 |
|
|
295
|
+
|------|-----|
|
|
296
|
+
| ホスティング | Netlify(手動デプロイ) |
|
|
297
|
+
| 本番URL | https://melta.tsubotax.com |
|
|
298
|
+
| publish ディレクトリ | `.`(リポジトリルート)— `netlify.toml` で設定済み |
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
# ドキュメントサイトデプロイ
|
|
302
|
+
netlify deploy --prod
|
|
303
|
+
|
|
304
|
+
# Next.js アプリ (別途)
|
|
305
|
+
cd app && npm run build
|
|
306
|
+
```
|