@layoutdesign/context 0.5.0 → 0.6.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/bin/cli.js +27 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/src/cli/diff.d.ts +4 -0
- package/dist/src/cli/diff.d.ts.map +1 -0
- package/dist/src/cli/diff.js +53 -0
- package/dist/src/cli/diff.js.map +1 -0
- package/dist/src/cli/import-tokens-json.d.ts +5 -0
- package/dist/src/cli/import-tokens-json.d.ts.map +1 -0
- package/dist/src/cli/import-tokens-json.js +174 -0
- package/dist/src/cli/import-tokens-json.js.map +1 -0
- package/dist/src/cli/lint.d.ts +7 -0
- package/dist/src/cli/lint.d.ts.map +1 -0
- package/dist/src/cli/lint.js +80 -0
- package/dist/src/cli/lint.js.map +1 -0
- package/dist/src/kit/stage.d.ts +19 -0
- package/dist/src/kit/stage.d.ts.map +1 -0
- package/dist/src/kit/stage.js +30 -0
- package/dist/src/kit/stage.js.map +1 -0
- package/dist/src/lint/diff.d.ts +23 -0
- package/dist/src/lint/diff.d.ts.map +1 -0
- package/dist/src/lint/diff.js +62 -0
- package/dist/src/lint/diff.js.map +1 -0
- package/dist/src/lint/layout-md.d.ts +21 -0
- package/dist/src/lint/layout-md.d.ts.map +1 -0
- package/dist/src/lint/layout-md.js +376 -0
- package/dist/src/lint/layout-md.js.map +1 -0
- package/package.json +2 -1
- package/skills/layout-md/SKILL.md +135 -0
package/dist/bin/cli.js
CHANGED
|
@@ -10,6 +10,9 @@ import { installCommand } from "../src/cli/install.js";
|
|
|
10
10
|
import { doctorCommand } from "../src/cli/doctor.js";
|
|
11
11
|
import { serveLocalCommand } from "../src/cli/serve-local.js";
|
|
12
12
|
import { scanCommand } from "../src/cli/scan.js";
|
|
13
|
+
import { lintCommand } from "../src/cli/lint.js";
|
|
14
|
+
import { diffCommand } from "../src/cli/diff.js";
|
|
15
|
+
import { importTokensJsonCommand } from "../src/cli/import-tokens-json.js";
|
|
13
16
|
const require = createRequire(import.meta.url);
|
|
14
17
|
const pkg = require("../../package.json");
|
|
15
18
|
const program = new Command();
|
|
@@ -85,5 +88,29 @@ program
|
|
|
85
88
|
.action(async (targetPath, options) => {
|
|
86
89
|
await scanCommand(targetPath, options);
|
|
87
90
|
});
|
|
91
|
+
program
|
|
92
|
+
.command("lint")
|
|
93
|
+
.description("Validate layout.md, tokens.css, and tokens.json against Layout's 7 linting rules")
|
|
94
|
+
.option("--json", "Emit structured JSON (for CI / agent consumption)")
|
|
95
|
+
.option("--quiet", "Suppress info-level findings")
|
|
96
|
+
.option("--path <dir>", "Directory containing .layout/ (default: cwd)")
|
|
97
|
+
.action(async (options) => {
|
|
98
|
+
await lintCommand(options);
|
|
99
|
+
});
|
|
100
|
+
program
|
|
101
|
+
.command("diff <base> <head>")
|
|
102
|
+
.description("Compare two layout kits (paths to .layout/ dirs, or bundled kit names) and report token-level changes")
|
|
103
|
+
.option("--json", "Emit structured JSON (for CI / agent consumption)")
|
|
104
|
+
.action(async (base, head, options) => {
|
|
105
|
+
await diffCommand(base, head, options);
|
|
106
|
+
});
|
|
107
|
+
program
|
|
108
|
+
.command("import-tokens <tokens-json>")
|
|
109
|
+
.description("Seed a new .layout/ directory from a W3C DTCG tokens.json file")
|
|
110
|
+
.option("--name <name>", "Project name written into the generated kit.json", "Imported Kit")
|
|
111
|
+
.option("--path <dir>", "Target directory containing .layout/ (default: cwd)")
|
|
112
|
+
.action(async (tokensJson, options) => {
|
|
113
|
+
await importTokensJsonCommand(tokensJson, options);
|
|
114
|
+
});
|
|
88
115
|
program.parse();
|
|
89
116
|
//# sourceMappingURL=cli.js.map
|
package/dist/bin/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/cli/diff.ts"],"names":[],"mappings":"AAWA,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/B,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { loadKit } from "../kit/loader.js";
|
|
2
|
+
import { stageKitReference } from "../kit/stage.js";
|
|
3
|
+
import { diffKits } from "../lint/diff.js";
|
|
4
|
+
const GREEN = "\x1b[32m";
|
|
5
|
+
const RED = "\x1b[31m";
|
|
6
|
+
const YELLOW = "\x1b[33m";
|
|
7
|
+
const DIM = "\x1b[2m";
|
|
8
|
+
const BOLD = "\x1b[1m";
|
|
9
|
+
const RESET = "\x1b[0m";
|
|
10
|
+
export async function diffCommand(base, head, options = {}) {
|
|
11
|
+
const baseStaged = stageKitReference(base);
|
|
12
|
+
const headStaged = stageKitReference(head);
|
|
13
|
+
const cleanup = () => {
|
|
14
|
+
baseStaged.cleanup();
|
|
15
|
+
headStaged.cleanup();
|
|
16
|
+
};
|
|
17
|
+
const baseKit = loadKit(baseStaged.path);
|
|
18
|
+
const headKit = loadKit(headStaged.path);
|
|
19
|
+
if (!baseKit || !headKit) {
|
|
20
|
+
cleanup();
|
|
21
|
+
process.stderr.write(`${RED}Failed to load one or both kits (checked path, .layout/ subfolder, and bundled-kit name).${RESET}\n`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const diff = diffKits(baseKit, headKit);
|
|
25
|
+
cleanup();
|
|
26
|
+
if (options.json) {
|
|
27
|
+
process.stdout.write(JSON.stringify(diff, null, 2) + "\n");
|
|
28
|
+
process.exit(diff.summary.breakingChanges > 0 ? 1 : 0);
|
|
29
|
+
}
|
|
30
|
+
printHuman(diff, base, head);
|
|
31
|
+
process.exit(diff.summary.breakingChanges > 0 ? 1 : 0);
|
|
32
|
+
}
|
|
33
|
+
function printHuman(diff, baseLabel, headLabel) {
|
|
34
|
+
process.stdout.write(`${BOLD}layout diff${RESET} ${DIM}${baseLabel} → ${headLabel}${RESET}\n`);
|
|
35
|
+
if (diff.summary.totalChanges === 0) {
|
|
36
|
+
process.stdout.write(`\n${GREEN}No changes.${RESET}\n`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const section = (title, rows) => {
|
|
40
|
+
if (rows.length === 0)
|
|
41
|
+
return;
|
|
42
|
+
process.stdout.write(`\n${BOLD}${title}${RESET}\n`);
|
|
43
|
+
for (const row of rows)
|
|
44
|
+
process.stdout.write(` ${row}\n`);
|
|
45
|
+
};
|
|
46
|
+
section(`Tokens added (${diff.tokens.added.length})`, diff.tokens.added.map((t) => `${GREEN}+${RESET} ${t.name}: ${DIM}${t.after}${RESET}`));
|
|
47
|
+
section(`Tokens removed (${diff.tokens.removed.length})`, diff.tokens.removed.map((t) => `${RED}-${RESET} ${t.name}: ${DIM}${t.before}${RESET}`));
|
|
48
|
+
section(`Tokens modified (${diff.tokens.modified.length})`, diff.tokens.modified.map((t) => `${YELLOW}~${RESET} ${t.name}: ${DIM}${t.before ?? "∅"} → ${t.after ?? "∅"}${RESET}`));
|
|
49
|
+
section(`Sections added (${diff.sections.added.length})`, diff.sections.added.map((s) => `${GREEN}+${RESET} ${s}`));
|
|
50
|
+
section(`Sections removed (${diff.sections.removed.length})`, diff.sections.removed.map((s) => `${RED}-${RESET} ${s}`));
|
|
51
|
+
process.stdout.write(`\n${BOLD}${diff.summary.totalChanges} changes${RESET} ${diff.summary.breakingChanges} breaking\n`);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../src/cli/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-tokens-json.d.ts","sourceRoot":"","sources":["../../../src/cli/import-tokens-json.ts"],"names":[],"mappings":"AA6EA,wBAAsB,uBAAuB,CAC3C,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7C,OAAO,CAAC,IAAI,CAAC,CAuEf"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { LAYOUT_DIR, KIT_MANIFEST_FILE, LAYOUT_MD_FILE, TOKENS_CSS_FILE, TOKENS_JSON_FILE } from "../kit/types.js";
|
|
4
|
+
const GREEN = "\x1b[32m";
|
|
5
|
+
const RED = "\x1b[31m";
|
|
6
|
+
const DIM = "\x1b[2m";
|
|
7
|
+
const BOLD = "\x1b[1m";
|
|
8
|
+
const RESET = "\x1b[0m";
|
|
9
|
+
// Flatten a DTCG tokens.json tree into CSS custom properties.
|
|
10
|
+
// The output matches what generateTokensCss produces in Studio, so round-trips
|
|
11
|
+
// through export → import stay structurally stable.
|
|
12
|
+
function flattenToCss(tree, prefix = "") {
|
|
13
|
+
const out = [];
|
|
14
|
+
for (const [key, rawValue] of Object.entries(tree)) {
|
|
15
|
+
if (!rawValue || typeof rawValue !== "object")
|
|
16
|
+
continue;
|
|
17
|
+
const value = rawValue;
|
|
18
|
+
if ("$value" in value) {
|
|
19
|
+
const token = value;
|
|
20
|
+
const cssName = prefix ? `--${prefix}-${key}`.toLowerCase().replace(/[^a-z0-9-]+/g, "-") : `--${key}`.toLowerCase();
|
|
21
|
+
const cssValue = stringifyValue(token.$value);
|
|
22
|
+
if (cssValue !== null) {
|
|
23
|
+
out.push({
|
|
24
|
+
name: cssName,
|
|
25
|
+
value: cssValue,
|
|
26
|
+
category: topLevelCategory(prefix),
|
|
27
|
+
description: token.$description,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
out.push(...flattenToCss(value, prefix ? `${prefix}-${key}` : key));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
function topLevelCategory(prefix) {
|
|
38
|
+
const first = prefix.split("-")[0] || "other";
|
|
39
|
+
if (first === "color" || first === "colors")
|
|
40
|
+
return "Colours";
|
|
41
|
+
if (first === "typography")
|
|
42
|
+
return "Typography";
|
|
43
|
+
if (first === "spacing" || first === "space" || first === "dimension")
|
|
44
|
+
return "Spacing";
|
|
45
|
+
if (first === "radius" || first === "rounded")
|
|
46
|
+
return "Radius";
|
|
47
|
+
if (first === "shadow" || first === "effect" || first === "elevation")
|
|
48
|
+
return "Effects";
|
|
49
|
+
if (first === "motion" || first === "duration")
|
|
50
|
+
return "Motion";
|
|
51
|
+
return first.charAt(0).toUpperCase() + first.slice(1);
|
|
52
|
+
}
|
|
53
|
+
function stringifyValue(v) {
|
|
54
|
+
if (v === null || v === undefined)
|
|
55
|
+
return null;
|
|
56
|
+
if (typeof v === "string")
|
|
57
|
+
return v;
|
|
58
|
+
if (typeof v === "number")
|
|
59
|
+
return `${v}`;
|
|
60
|
+
if (typeof v === "object") {
|
|
61
|
+
// Typography composite — flatten to shorthand if possible.
|
|
62
|
+
const obj = v;
|
|
63
|
+
if ("fontFamily" in obj && "fontSize" in obj) {
|
|
64
|
+
const family = obj.fontFamily;
|
|
65
|
+
const size = obj.fontSize;
|
|
66
|
+
const weight = obj.fontWeight ?? 400;
|
|
67
|
+
const lh = obj.lineHeight ?? "1.5";
|
|
68
|
+
return `${weight} ${size}/${lh} ${family}`;
|
|
69
|
+
}
|
|
70
|
+
// Fall back to JSON-encoded value so it at least survives.
|
|
71
|
+
return JSON.stringify(v);
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
export async function importTokensJsonCommand(tokensJsonPath, options = {}) {
|
|
76
|
+
const input = resolve(tokensJsonPath);
|
|
77
|
+
if (!existsSync(input)) {
|
|
78
|
+
process.stderr.write(`${RED}tokens.json not found at ${input}${RESET}\n`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
let parsed;
|
|
82
|
+
try {
|
|
83
|
+
parsed = JSON.parse(readFileSync(input, "utf-8"));
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
process.stderr.write(`${RED}Failed to parse tokens.json: ${err instanceof Error ? err.message : "unknown"}${RESET}\n`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
const projectName = options.name ?? "Imported Kit";
|
|
90
|
+
const targetRoot = resolve(options.path ?? process.cwd());
|
|
91
|
+
const layoutDir = join(targetRoot, LAYOUT_DIR);
|
|
92
|
+
if (!existsSync(layoutDir))
|
|
93
|
+
mkdirSync(layoutDir, { recursive: true });
|
|
94
|
+
const cssTokens = flattenToCss(parsed);
|
|
95
|
+
const grouped = new Map();
|
|
96
|
+
for (const t of cssTokens) {
|
|
97
|
+
const arr = grouped.get(t.category) ?? [];
|
|
98
|
+
arr.push(t);
|
|
99
|
+
grouped.set(t.category, arr);
|
|
100
|
+
}
|
|
101
|
+
const cssLines = [":root {"];
|
|
102
|
+
for (const [category, tokens] of grouped) {
|
|
103
|
+
cssLines.push(` /* === ${category} === */`);
|
|
104
|
+
for (const t of tokens) {
|
|
105
|
+
const comment = t.description ? ` /* ${t.description} */` : "";
|
|
106
|
+
cssLines.push(` ${t.name}: ${t.value};${comment}`);
|
|
107
|
+
}
|
|
108
|
+
cssLines.push("");
|
|
109
|
+
}
|
|
110
|
+
cssLines.push("}");
|
|
111
|
+
const tokensCss = cssLines.join("\n") + "\n";
|
|
112
|
+
// Pass tokens.json through as-is so downstream tooling still sees the
|
|
113
|
+
// canonical DTCG shape.
|
|
114
|
+
const tokensJson = JSON.stringify(parsed, null, 2) + "\n";
|
|
115
|
+
const layoutMd = buildLayoutMdSkeleton(projectName, grouped);
|
|
116
|
+
const kitJson = JSON.stringify({
|
|
117
|
+
name: slugify(projectName),
|
|
118
|
+
version: "0.1.0",
|
|
119
|
+
displayName: projectName,
|
|
120
|
+
description: "Imported from DTCG tokens.json",
|
|
121
|
+
source: "tokens-json",
|
|
122
|
+
tier: "free",
|
|
123
|
+
tokenCount: cssTokens.length,
|
|
124
|
+
componentCount: 0,
|
|
125
|
+
aesthetic: "Imported",
|
|
126
|
+
}, null, 2) + "\n";
|
|
127
|
+
writeFileSync(join(layoutDir, LAYOUT_MD_FILE), layoutMd);
|
|
128
|
+
writeFileSync(join(layoutDir, TOKENS_CSS_FILE), tokensCss);
|
|
129
|
+
writeFileSync(join(layoutDir, TOKENS_JSON_FILE), tokensJson);
|
|
130
|
+
writeFileSync(join(layoutDir, KIT_MANIFEST_FILE), kitJson);
|
|
131
|
+
process.stdout.write(`${GREEN}✔${RESET} Imported ${cssTokens.length} tokens into ${BOLD}${LAYOUT_DIR}/${RESET}\n` +
|
|
132
|
+
` ${DIM}${LAYOUT_MD_FILE}${RESET} ${DIM}${TOKENS_CSS_FILE}${RESET} ${DIM}${TOKENS_JSON_FILE}${RESET} ${DIM}${KIT_MANIFEST_FILE}${RESET}\n` +
|
|
133
|
+
`\nRun ${BOLD}layout-context lint${RESET} to validate the imported kit.\n`);
|
|
134
|
+
}
|
|
135
|
+
function buildLayoutMdSkeleton(projectName, grouped) {
|
|
136
|
+
const lines = [];
|
|
137
|
+
lines.push(`# ${projectName}`);
|
|
138
|
+
lines.push("");
|
|
139
|
+
lines.push("_Generated from tokens.json. Fill in the prose sections and delete this line._");
|
|
140
|
+
lines.push("");
|
|
141
|
+
lines.push("## Quick Reference");
|
|
142
|
+
lines.push("");
|
|
143
|
+
lines.push("```css");
|
|
144
|
+
lines.push(":root {");
|
|
145
|
+
for (const [, tokens] of grouped) {
|
|
146
|
+
for (const t of tokens.slice(0, 4))
|
|
147
|
+
lines.push(` ${t.name}: ${t.value};`);
|
|
148
|
+
}
|
|
149
|
+
lines.push("}");
|
|
150
|
+
lines.push("```");
|
|
151
|
+
lines.push("");
|
|
152
|
+
for (const [category, tokens] of grouped) {
|
|
153
|
+
lines.push(`## ${category}`);
|
|
154
|
+
lines.push("");
|
|
155
|
+
lines.push("```css");
|
|
156
|
+
for (const t of tokens)
|
|
157
|
+
lines.push(`${t.name}: ${t.value};`);
|
|
158
|
+
lines.push("```");
|
|
159
|
+
lines.push("");
|
|
160
|
+
}
|
|
161
|
+
lines.push("## Components");
|
|
162
|
+
lines.push("");
|
|
163
|
+
lines.push("_Add component examples here. Keep each under 40 lines of TSX._");
|
|
164
|
+
lines.push("");
|
|
165
|
+
lines.push("## Anti-Patterns");
|
|
166
|
+
lines.push("");
|
|
167
|
+
lines.push("_List rules that AI agents must not violate when generating code._");
|
|
168
|
+
lines.push("");
|
|
169
|
+
return lines.join("\n");
|
|
170
|
+
}
|
|
171
|
+
function slugify(raw) {
|
|
172
|
+
return raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "imported-kit";
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=import-tokens-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-tokens-json.js","sourceRoot":"","sources":["../../../src/cli/import-tokens-json.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../../src/cli/lint.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiCjF"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { loadKit } from "../kit/loader.js";
|
|
2
|
+
import { stageKitReference } from "../kit/stage.js";
|
|
3
|
+
import { lintKit } from "../lint/layout-md.js";
|
|
4
|
+
// ANSI colour helpers kept inline so the CLI stays dependency-free.
|
|
5
|
+
const RED = "\x1b[31m";
|
|
6
|
+
const YELLOW = "\x1b[33m";
|
|
7
|
+
const BLUE = "\x1b[34m";
|
|
8
|
+
const DIM = "\x1b[2m";
|
|
9
|
+
const RESET = "\x1b[0m";
|
|
10
|
+
const BOLD = "\x1b[1m";
|
|
11
|
+
function colourForSeverity(severity) {
|
|
12
|
+
if (severity === "error")
|
|
13
|
+
return RED;
|
|
14
|
+
if (severity === "warning")
|
|
15
|
+
return YELLOW;
|
|
16
|
+
return BLUE;
|
|
17
|
+
}
|
|
18
|
+
function symbolFor(severity) {
|
|
19
|
+
if (severity === "error")
|
|
20
|
+
return "✖";
|
|
21
|
+
if (severity === "warning")
|
|
22
|
+
return "⚠";
|
|
23
|
+
return "ℹ";
|
|
24
|
+
}
|
|
25
|
+
export async function lintCommand(options = {}) {
|
|
26
|
+
const prepared = stageKitReference(options.path);
|
|
27
|
+
const kit = loadKit(prepared.path);
|
|
28
|
+
const cleanup = prepared.cleanup;
|
|
29
|
+
if (!kit) {
|
|
30
|
+
cleanup();
|
|
31
|
+
const message = options.path
|
|
32
|
+
? `No .layout/ directory found at ${options.path}`
|
|
33
|
+
: "No .layout/ directory found. Run `layout-context init` first.";
|
|
34
|
+
if (options.json) {
|
|
35
|
+
process.stdout.write(JSON.stringify({ error: "kit-not-found", message, issues: [], summary: { errors: 1, warnings: 0, info: 0, passed: false } }, null, 2) + "\n");
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
process.stderr.write(`${RED}${message}${RESET}\n`);
|
|
39
|
+
}
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const result = lintKit(kit);
|
|
43
|
+
cleanup();
|
|
44
|
+
if (options.json) {
|
|
45
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
printHuman(result, options.quiet ?? false);
|
|
49
|
+
}
|
|
50
|
+
process.exit(result.summary.passed ? 0 : 1);
|
|
51
|
+
}
|
|
52
|
+
function printHuman(result, quiet) {
|
|
53
|
+
const { issues, summary } = result;
|
|
54
|
+
const filtered = quiet ? issues.filter((i) => i.severity !== "info") : issues;
|
|
55
|
+
if (filtered.length === 0) {
|
|
56
|
+
process.stdout.write(`${BOLD}layout lint${RESET} ${BLUE}✔${RESET} no issues\n`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const byFile = new Map();
|
|
60
|
+
for (const issue of filtered) {
|
|
61
|
+
const key = issue.file ?? "(kit)";
|
|
62
|
+
const arr = byFile.get(key) ?? [];
|
|
63
|
+
arr.push(issue);
|
|
64
|
+
byFile.set(key, arr);
|
|
65
|
+
}
|
|
66
|
+
for (const [file, fileIssues] of byFile) {
|
|
67
|
+
process.stdout.write(`\n${BOLD}${file}${RESET}\n`);
|
|
68
|
+
for (const issue of fileIssues) {
|
|
69
|
+
const colour = colourForSeverity(issue.severity);
|
|
70
|
+
const sym = symbolFor(issue.severity);
|
|
71
|
+
const loc = issue.line ? `${DIM}L${issue.line}${RESET} ` : "";
|
|
72
|
+
process.stdout.write(` ${colour}${sym}${RESET} ${loc}${issue.message} ${DIM}(${issue.ruleId})${RESET}\n`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
process.stdout.write(`\n${BOLD}${summary.errors} errors${RESET} ${summary.warnings} warnings ${summary.info} info\n`);
|
|
76
|
+
if (!summary.passed) {
|
|
77
|
+
process.stdout.write(`${RED}layout lint failed${RESET}\n`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=lint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../../../src/cli/lint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a user-supplied kit reference into a directory that the loader can
|
|
3
|
+
* read. Accepts:
|
|
4
|
+
* 1. A path to a directory containing a .layout/ subfolder (default for
|
|
5
|
+
* consumer projects).
|
|
6
|
+
* 2. A path to a directory with layout.md at its root (a bundled kit or
|
|
7
|
+
* an unzipped export). Staged into a temp dir that wraps it in .layout/.
|
|
8
|
+
* 3. A bundled kit name (linear-lite, stripe-lite, notion-lite). Likewise
|
|
9
|
+
* staged.
|
|
10
|
+
*
|
|
11
|
+
* The caller MUST invoke the returned cleanup() when done to remove any temp
|
|
12
|
+
* directory the function created.
|
|
13
|
+
*/
|
|
14
|
+
export interface StagedKit {
|
|
15
|
+
path: string;
|
|
16
|
+
cleanup: () => void;
|
|
17
|
+
}
|
|
18
|
+
export declare function stageKitReference(input: string | undefined): StagedKit;
|
|
19
|
+
//# sourceMappingURL=stage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../../src/kit/stage.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAUtE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { existsSync, mkdtempSync, writeFileSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { LAYOUT_DIR, LAYOUT_MD_FILE, TOKENS_CSS_FILE, TOKENS_JSON_FILE, KIT_MANIFEST_FILE, } from "./types.js";
|
|
5
|
+
import { getBundledKitPath } from "./loader.js";
|
|
6
|
+
export function stageKitReference(input) {
|
|
7
|
+
if (!input)
|
|
8
|
+
return { path: process.cwd(), cleanup: () => { } };
|
|
9
|
+
const abs = resolve(input);
|
|
10
|
+
if (existsSync(join(abs, LAYOUT_DIR)))
|
|
11
|
+
return { path: abs, cleanup: () => { } };
|
|
12
|
+
if (existsSync(join(abs, LAYOUT_MD_FILE)))
|
|
13
|
+
return stageDirAsKit(abs);
|
|
14
|
+
const bundled = getBundledKitPath(input);
|
|
15
|
+
if (bundled)
|
|
16
|
+
return stageDirAsKit(bundled);
|
|
17
|
+
return { path: abs, cleanup: () => { } };
|
|
18
|
+
}
|
|
19
|
+
function stageDirAsKit(kitDir) {
|
|
20
|
+
const tmp = mkdtempSync(join(tmpdir(), "layout-stage-"));
|
|
21
|
+
const layoutDir = join(tmp, LAYOUT_DIR);
|
|
22
|
+
mkdirSync(layoutDir, { recursive: true });
|
|
23
|
+
for (const f of [LAYOUT_MD_FILE, TOKENS_CSS_FILE, TOKENS_JSON_FILE, KIT_MANIFEST_FILE]) {
|
|
24
|
+
const src = join(kitDir, f);
|
|
25
|
+
if (existsSync(src))
|
|
26
|
+
writeFileSync(join(layoutDir, f), readFileSync(src));
|
|
27
|
+
}
|
|
28
|
+
return { path: tmp, cleanup: () => rmSync(tmp, { recursive: true, force: true }) };
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=stage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage.js","sourceRoot":"","sources":["../../../src/kit/stage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAClG,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,UAAU,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAoBhD,MAAM,UAAU,iBAAiB,CAAC,KAAyB;IACzD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC;IAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC;IAC/E,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;IAErE,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,OAAO;QAAE,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC;IAE3C,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC;AAC1C,CAAC;AAED,SAAS,aAAa,CAAC,MAAc;IACnC,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACxC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,CAAC,EAAE,CAAC;QACvF,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC5B,IAAI,UAAU,CAAC,GAAG,CAAC;YAAE,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACrF,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Kit } from "../kit/types.js";
|
|
2
|
+
export interface TokenChange {
|
|
3
|
+
name: string;
|
|
4
|
+
before?: string;
|
|
5
|
+
after?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface KitDiff {
|
|
8
|
+
tokens: {
|
|
9
|
+
added: TokenChange[];
|
|
10
|
+
removed: TokenChange[];
|
|
11
|
+
modified: TokenChange[];
|
|
12
|
+
};
|
|
13
|
+
sections: {
|
|
14
|
+
added: string[];
|
|
15
|
+
removed: string[];
|
|
16
|
+
};
|
|
17
|
+
summary: {
|
|
18
|
+
totalChanges: number;
|
|
19
|
+
breakingChanges: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare function diffKits(base: Kit, head: Kit): KitDiff;
|
|
23
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/lint/diff.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE;QACN,KAAK,EAAE,WAAW,EAAE,CAAC;QACrB,OAAO,EAAE,WAAW,EAAE,CAAC;QACvB,QAAQ,EAAE,WAAW,EAAE,CAAC;KACzB,CAAC;IACF,QAAQ,EAAE;QACR,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,OAAO,EAAE;QACP,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAqBD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAoCtD"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Token-level diff engine for two layout kits. Mirrors the shape of the
|
|
2
|
+
// extraction-diff UI in Studio so outputs are recognisable to anyone who
|
|
3
|
+
// has used re-extract.
|
|
4
|
+
function parseCssTokens(css) {
|
|
5
|
+
const map = new Map();
|
|
6
|
+
if (!css)
|
|
7
|
+
return map;
|
|
8
|
+
for (const match of css.matchAll(/^\s*(--[a-zA-Z0-9_-]+)\s*:\s*([^;\n]+);/gm)) {
|
|
9
|
+
const name = match[1];
|
|
10
|
+
const value = match[2];
|
|
11
|
+
if (name && value)
|
|
12
|
+
map.set(name, value.trim());
|
|
13
|
+
}
|
|
14
|
+
return map;
|
|
15
|
+
}
|
|
16
|
+
function parseSections(layoutMd) {
|
|
17
|
+
const out = new Set();
|
|
18
|
+
for (const match of layoutMd.matchAll(/^##\s+(.+)$/gm)) {
|
|
19
|
+
if (match[1])
|
|
20
|
+
out.add(match[1].trim());
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
export function diffKits(base, head) {
|
|
25
|
+
const baseTokens = parseCssTokens(base.tokensCss);
|
|
26
|
+
const headTokens = parseCssTokens(head.tokensCss);
|
|
27
|
+
const added = [];
|
|
28
|
+
const removed = [];
|
|
29
|
+
const modified = [];
|
|
30
|
+
for (const [name, value] of headTokens) {
|
|
31
|
+
if (!baseTokens.has(name)) {
|
|
32
|
+
added.push({ name, after: value });
|
|
33
|
+
}
|
|
34
|
+
else if (baseTokens.get(name) !== value) {
|
|
35
|
+
modified.push({ name, before: baseTokens.get(name), after: value });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
for (const [name, value] of baseTokens) {
|
|
39
|
+
if (!headTokens.has(name))
|
|
40
|
+
removed.push({ name, before: value });
|
|
41
|
+
}
|
|
42
|
+
const baseSections = parseSections(base.layoutMd);
|
|
43
|
+
const headSections = parseSections(head.layoutMd);
|
|
44
|
+
const sectionsAdded = [];
|
|
45
|
+
const sectionsRemoved = [];
|
|
46
|
+
for (const s of headSections)
|
|
47
|
+
if (!baseSections.has(s))
|
|
48
|
+
sectionsAdded.push(s);
|
|
49
|
+
for (const s of baseSections)
|
|
50
|
+
if (!headSections.has(s))
|
|
51
|
+
sectionsRemoved.push(s);
|
|
52
|
+
const totalChanges = added.length + removed.length + modified.length + sectionsAdded.length + sectionsRemoved.length;
|
|
53
|
+
// Breaking: removed tokens + removed sections (references in downstream code
|
|
54
|
+
// may now fail).
|
|
55
|
+
const breakingChanges = removed.length + sectionsRemoved.length;
|
|
56
|
+
return {
|
|
57
|
+
tokens: { added, removed, modified },
|
|
58
|
+
sections: { added: sectionsAdded, removed: sectionsRemoved },
|
|
59
|
+
summary: { totalChanges, breakingChanges },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../src/lint/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Kit } from "../kit/types.js";
|
|
2
|
+
export type LintSeverity = "error" | "warning" | "info";
|
|
3
|
+
export interface LintIssue {
|
|
4
|
+
ruleId: string;
|
|
5
|
+
severity: LintSeverity;
|
|
6
|
+
message: string;
|
|
7
|
+
file?: "layout.md" | "tokens.css" | "tokens.json";
|
|
8
|
+
line?: number;
|
|
9
|
+
detail?: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
export interface LintResult {
|
|
12
|
+
issues: LintIssue[];
|
|
13
|
+
summary: {
|
|
14
|
+
errors: number;
|
|
15
|
+
warnings: number;
|
|
16
|
+
info: number;
|
|
17
|
+
passed: boolean;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export declare function lintKit(kit: Kit): LintResult;
|
|
21
|
+
//# sourceMappingURL=layout-md.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-md.d.ts","sourceRoot":"","sources":["../../../src/lint/layout-md.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,GAAG,YAAY,GAAG,aAAa,CAAC;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;CACH;AAqWD,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,UAAU,CAkB5C"}
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
// Linter for the layout.md format. Runs over a loaded Kit (layout.md + the
|
|
2
|
+
// companion tokens.css / tokens.json) and reports issues. Callers get either
|
|
3
|
+
// a structured array (for JSON output + CI integration) or a human-readable
|
|
4
|
+
// pretty-printed summary.
|
|
5
|
+
//
|
|
6
|
+
// The seven rules mirror the checks Google's design.md CLI ships with, plus
|
|
7
|
+
// the specifics our three-tier token system wants to enforce.
|
|
8
|
+
const CANONICAL_SECTIONS = [
|
|
9
|
+
"Quick Reference",
|
|
10
|
+
"Design Direction",
|
|
11
|
+
"Colour System",
|
|
12
|
+
"Typography System",
|
|
13
|
+
"Spacing",
|
|
14
|
+
"Components",
|
|
15
|
+
"Elevation",
|
|
16
|
+
"Motion",
|
|
17
|
+
"Anti-Patterns",
|
|
18
|
+
];
|
|
19
|
+
// Parses `--name: value;` declarations out of a CSS string using matchAll
|
|
20
|
+
// rather than stateful regex iteration.
|
|
21
|
+
function parseCssDeclarations(css) {
|
|
22
|
+
const pattern = /^\s*(--[a-zA-Z0-9_-]+)\s*:\s*([^;\n]+);/gm;
|
|
23
|
+
const out = [];
|
|
24
|
+
for (const match of css.matchAll(pattern)) {
|
|
25
|
+
const name = match[1];
|
|
26
|
+
const value = match[2];
|
|
27
|
+
if (name && value)
|
|
28
|
+
out.push({ name, value: value.trim(), index: match.index ?? 0 });
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
// ── Rule 1 ─────────────────────────────────────────────────────────────────
|
|
33
|
+
// broken-token-ref: every var(--name) in layout.md must resolve to a token
|
|
34
|
+
// defined in tokens.css.
|
|
35
|
+
function ruleBrokenTokenRef(kit) {
|
|
36
|
+
const issues = [];
|
|
37
|
+
const defined = new Set();
|
|
38
|
+
if (kit.tokensCss) {
|
|
39
|
+
for (const decl of parseCssDeclarations(kit.tokensCss))
|
|
40
|
+
defined.add(decl.name);
|
|
41
|
+
}
|
|
42
|
+
const pattern = /var\((--[a-zA-Z0-9_-]+)\)/g;
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
for (const match of kit.layoutMd.matchAll(pattern)) {
|
|
45
|
+
const name = match[1];
|
|
46
|
+
if (!name)
|
|
47
|
+
continue;
|
|
48
|
+
if (defined.size > 0 && !defined.has(name) && !seen.has(name)) {
|
|
49
|
+
seen.add(name);
|
|
50
|
+
issues.push({
|
|
51
|
+
ruleId: "broken-token-ref",
|
|
52
|
+
severity: "warning",
|
|
53
|
+
message: `layout.md references var(${name}) but no definition exists in tokens.css.`,
|
|
54
|
+
file: "layout.md",
|
|
55
|
+
line: lineNumberOf(kit.layoutMd, match.index ?? 0),
|
|
56
|
+
detail: { token: name },
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return issues;
|
|
61
|
+
}
|
|
62
|
+
// ── Rule 2 ─────────────────────────────────────────────────────────────────
|
|
63
|
+
// orphaned-tokens: tokens defined in tokens.css but never referenced anywhere
|
|
64
|
+
// in layout.md.
|
|
65
|
+
function ruleOrphanedTokens(kit) {
|
|
66
|
+
if (!kit.tokensCss)
|
|
67
|
+
return [];
|
|
68
|
+
const issues = [];
|
|
69
|
+
const declarations = parseCssDeclarations(kit.tokensCss);
|
|
70
|
+
const referenced = new Set();
|
|
71
|
+
for (const match of kit.layoutMd.matchAll(/var\((--[a-zA-Z0-9_-]+)\)/g)) {
|
|
72
|
+
if (match[1])
|
|
73
|
+
referenced.add(match[1]);
|
|
74
|
+
}
|
|
75
|
+
for (const decl of declarations) {
|
|
76
|
+
if (!referenced.has(decl.name)) {
|
|
77
|
+
issues.push({
|
|
78
|
+
ruleId: "orphaned-tokens",
|
|
79
|
+
severity: "info",
|
|
80
|
+
message: `Token ${decl.name} is defined in tokens.css but never referenced in layout.md.`,
|
|
81
|
+
file: "tokens.css",
|
|
82
|
+
detail: { token: decl.name },
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return issues;
|
|
87
|
+
}
|
|
88
|
+
// ── Rule 3 ─────────────────────────────────────────────────────────────────
|
|
89
|
+
// wcag-aa-contrast: detectable colour pairs must clear 4.5:1 contrast.
|
|
90
|
+
function ruleWcagAaContrast(kit) {
|
|
91
|
+
if (!kit.tokensCss)
|
|
92
|
+
return [];
|
|
93
|
+
const issues = [];
|
|
94
|
+
const colours = parseCssColourTokens(kit.tokensCss);
|
|
95
|
+
const pairs = [];
|
|
96
|
+
for (const [name] of colours) {
|
|
97
|
+
if (name === "--color-primary") {
|
|
98
|
+
const onName = "--color-on-primary";
|
|
99
|
+
if (colours.has(onName))
|
|
100
|
+
pairs.push({ fg: onName, bg: name, context: "on-primary" });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (const [txtName] of colours) {
|
|
104
|
+
if (!txtName.startsWith("--text-") && !txtName.startsWith("--color-on-"))
|
|
105
|
+
continue;
|
|
106
|
+
for (const [bgName] of colours) {
|
|
107
|
+
if (!bgName.startsWith("--bg-") && !bgName.startsWith("--color-bg-"))
|
|
108
|
+
continue;
|
|
109
|
+
if (txtName === "--text-primary" && bgName === "--bg-app") {
|
|
110
|
+
pairs.push({ fg: txtName, bg: bgName, context: "body text on app background" });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const pair of pairs) {
|
|
115
|
+
const fg = colours.get(pair.fg);
|
|
116
|
+
const bg = colours.get(pair.bg);
|
|
117
|
+
if (!fg || !bg)
|
|
118
|
+
continue;
|
|
119
|
+
const ratio = contrastRatio(fg, bg);
|
|
120
|
+
if (ratio === null)
|
|
121
|
+
continue;
|
|
122
|
+
if (ratio < 4.5) {
|
|
123
|
+
issues.push({
|
|
124
|
+
ruleId: "wcag-aa-contrast",
|
|
125
|
+
severity: "warning",
|
|
126
|
+
message: `Contrast between ${pair.fg} and ${pair.bg} is ${ratio.toFixed(2)}:1, below WCAG AA threshold (4.5:1) for ${pair.context}.`,
|
|
127
|
+
file: "tokens.css",
|
|
128
|
+
detail: { fg: pair.fg, bg: pair.bg, ratio },
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return issues;
|
|
133
|
+
}
|
|
134
|
+
// ── Rule 4 ─────────────────────────────────────────────────────────────────
|
|
135
|
+
// section-ordering: canonical order enforced when sections are present.
|
|
136
|
+
function ruleSectionOrdering(kit) {
|
|
137
|
+
const positions = [];
|
|
138
|
+
for (const canonical of CANONICAL_SECTIONS) {
|
|
139
|
+
const pattern = new RegExp(`^##\\s+.*${escapeRegex(canonical)}`, "mi");
|
|
140
|
+
const match = kit.layoutMd.match(pattern);
|
|
141
|
+
if (match && match.index !== undefined)
|
|
142
|
+
positions.push({ name: canonical, idx: match.index });
|
|
143
|
+
}
|
|
144
|
+
const issues = [];
|
|
145
|
+
for (let i = 1; i < positions.length; i++) {
|
|
146
|
+
const current = positions[i];
|
|
147
|
+
const previous = positions[i - 1];
|
|
148
|
+
if (current.idx < previous.idx) {
|
|
149
|
+
issues.push({
|
|
150
|
+
ruleId: "section-ordering",
|
|
151
|
+
severity: "info",
|
|
152
|
+
message: `Section "${current.name}" appears before "${previous.name}" in layout.md. Canonical order: Quick Reference, Design Direction, Colour System, Typography, Spacing, Components, Elevation, Motion, Anti-Patterns.`,
|
|
153
|
+
file: "layout.md",
|
|
154
|
+
line: lineNumberOf(kit.layoutMd, current.idx),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return issues;
|
|
159
|
+
}
|
|
160
|
+
// ── Rule 5 ─────────────────────────────────────────────────────────────────
|
|
161
|
+
// missing-primary: layout.md carries at least one primary/accent colour.
|
|
162
|
+
function ruleMissingPrimary(kit) {
|
|
163
|
+
if (!kit.tokensCss)
|
|
164
|
+
return [];
|
|
165
|
+
const defined = parseCssDeclarations(kit.tokensCss).map((d) => d.name);
|
|
166
|
+
// Accept any token whose basename ends in -primary, -accent, -brand, or
|
|
167
|
+
// matches the bare names. This tolerates prefixed kits like --linear-accent,
|
|
168
|
+
// --stripe-primary, --acme-brand.
|
|
169
|
+
const hasAny = defined.some((name) => /(?:^|-)(primary|accent|brand)$/.test(name));
|
|
170
|
+
if (hasAny)
|
|
171
|
+
return [];
|
|
172
|
+
return [
|
|
173
|
+
{
|
|
174
|
+
ruleId: "missing-primary",
|
|
175
|
+
severity: "error",
|
|
176
|
+
message: `No primary/accent/brand colour token found. Expected any token whose name ends in -primary, -accent, or -brand (e.g. --color-primary, --linear-accent, --acme-brand).`,
|
|
177
|
+
file: "tokens.css",
|
|
178
|
+
},
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
// ── Rule 6 ─────────────────────────────────────────────────────────────────
|
|
182
|
+
// circular-alias: --a: var(--b); --b: var(--a).
|
|
183
|
+
function ruleCircularAlias(kit) {
|
|
184
|
+
if (!kit.tokensCss)
|
|
185
|
+
return [];
|
|
186
|
+
const issues = [];
|
|
187
|
+
const aliases = new Map();
|
|
188
|
+
for (const match of kit.tokensCss.matchAll(/^\s*(--[a-zA-Z0-9_-]+)\s*:\s*var\((--[a-zA-Z0-9_-]+)\)\s*;/gm)) {
|
|
189
|
+
const from = match[1];
|
|
190
|
+
const to = match[2];
|
|
191
|
+
if (from && to)
|
|
192
|
+
aliases.set(from, to);
|
|
193
|
+
}
|
|
194
|
+
const seen = new Set();
|
|
195
|
+
for (const [from] of aliases) {
|
|
196
|
+
if (seen.has(from))
|
|
197
|
+
continue;
|
|
198
|
+
const chain = [from];
|
|
199
|
+
let current = from;
|
|
200
|
+
while (aliases.has(current)) {
|
|
201
|
+
const next = aliases.get(current);
|
|
202
|
+
if (!next)
|
|
203
|
+
break;
|
|
204
|
+
if (chain.includes(next)) {
|
|
205
|
+
chain.push(next);
|
|
206
|
+
issues.push({
|
|
207
|
+
ruleId: "circular-alias",
|
|
208
|
+
severity: "error",
|
|
209
|
+
message: `Circular token alias: ${chain.join(" -> ")}.`,
|
|
210
|
+
file: "tokens.css",
|
|
211
|
+
detail: { chain },
|
|
212
|
+
});
|
|
213
|
+
chain.forEach((c) => seen.add(c));
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
chain.push(next);
|
|
217
|
+
current = next;
|
|
218
|
+
if (chain.length > 32)
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
seen.add(from);
|
|
222
|
+
}
|
|
223
|
+
return issues;
|
|
224
|
+
}
|
|
225
|
+
// ── Rule 7 ─────────────────────────────────────────────────────────────────
|
|
226
|
+
// unknown-property: tokens.json entries whose $type is outside the W3C DTCG
|
|
227
|
+
// set Layout's exporters produce.
|
|
228
|
+
function ruleUnknownProperty(kit) {
|
|
229
|
+
if (!kit.tokensJson)
|
|
230
|
+
return [];
|
|
231
|
+
const issues = [];
|
|
232
|
+
let parsed;
|
|
233
|
+
try {
|
|
234
|
+
parsed = JSON.parse(kit.tokensJson);
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return [
|
|
238
|
+
{
|
|
239
|
+
ruleId: "unknown-property",
|
|
240
|
+
severity: "error",
|
|
241
|
+
message: "tokens.json is not valid JSON.",
|
|
242
|
+
file: "tokens.json",
|
|
243
|
+
},
|
|
244
|
+
];
|
|
245
|
+
}
|
|
246
|
+
const allowed = new Set([
|
|
247
|
+
"color",
|
|
248
|
+
"dimension",
|
|
249
|
+
"fontFamily",
|
|
250
|
+
"fontWeight",
|
|
251
|
+
"fontSize",
|
|
252
|
+
"lineHeight",
|
|
253
|
+
"letterSpacing",
|
|
254
|
+
"typography",
|
|
255
|
+
"shadow",
|
|
256
|
+
"border",
|
|
257
|
+
"duration",
|
|
258
|
+
"cubicBezier",
|
|
259
|
+
"motion",
|
|
260
|
+
"number",
|
|
261
|
+
"string",
|
|
262
|
+
]);
|
|
263
|
+
walkTokens(parsed, (path, value) => {
|
|
264
|
+
if (value && typeof value === "object" && "$type" in value) {
|
|
265
|
+
const t = value.$type;
|
|
266
|
+
if (typeof t === "string" && !allowed.has(t)) {
|
|
267
|
+
issues.push({
|
|
268
|
+
ruleId: "unknown-property",
|
|
269
|
+
severity: "info",
|
|
270
|
+
message: `Token ${path} declares $type "${t}" which is not in the W3C DTCG vocabulary Layout emits.`,
|
|
271
|
+
file: "tokens.json",
|
|
272
|
+
detail: { path, type: t },
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
return issues;
|
|
278
|
+
}
|
|
279
|
+
// Helpers ────────────────────────────────────────────────────────────────
|
|
280
|
+
function escapeRegex(s) {
|
|
281
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
282
|
+
}
|
|
283
|
+
function lineNumberOf(text, index) {
|
|
284
|
+
return text.slice(0, index).split("\n").length;
|
|
285
|
+
}
|
|
286
|
+
function walkTokens(obj, visit, prefix = "") {
|
|
287
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
288
|
+
const next = prefix ? `${prefix}.${key}` : key;
|
|
289
|
+
if (value && typeof value === "object") {
|
|
290
|
+
if ("$value" in value) {
|
|
291
|
+
visit(next, value);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
walkTokens(value, visit, next);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function parseCssColourTokens(css) {
|
|
300
|
+
const map = new Map();
|
|
301
|
+
for (const decl of parseCssDeclarations(css)) {
|
|
302
|
+
if (isColourValue(decl.value))
|
|
303
|
+
map.set(decl.name, decl.value);
|
|
304
|
+
}
|
|
305
|
+
return map;
|
|
306
|
+
}
|
|
307
|
+
function isColourValue(v) {
|
|
308
|
+
return /^#([0-9a-fA-F]{3,8})$/.test(v) || /^rgb\(/i.test(v) || /^rgba\(/i.test(v);
|
|
309
|
+
}
|
|
310
|
+
function parseHex(value) {
|
|
311
|
+
const match = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.exec(value.trim());
|
|
312
|
+
if (!match || !match[1])
|
|
313
|
+
return null;
|
|
314
|
+
let hex = match[1];
|
|
315
|
+
if (hex.length === 3)
|
|
316
|
+
hex = hex.split("").map((c) => c + c).join("");
|
|
317
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
318
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
319
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
320
|
+
const a = hex.length >= 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1;
|
|
321
|
+
return [r, g, b, a];
|
|
322
|
+
}
|
|
323
|
+
function parseRgb(value) {
|
|
324
|
+
const match = /^rgba?\(\s*([^)]+)\)\s*$/i.exec(value.trim());
|
|
325
|
+
if (!match || !match[1])
|
|
326
|
+
return null;
|
|
327
|
+
const parts = match[1].split(",").map((p) => p.trim());
|
|
328
|
+
if (parts.length < 3)
|
|
329
|
+
return null;
|
|
330
|
+
const r = Number.parseFloat(parts[0]);
|
|
331
|
+
const g = Number.parseFloat(parts[1]);
|
|
332
|
+
const b = Number.parseFloat(parts[2]);
|
|
333
|
+
const a = parts[3] !== undefined ? Number.parseFloat(parts[3]) : 1;
|
|
334
|
+
if ([r, g, b].some((n) => Number.isNaN(n)))
|
|
335
|
+
return null;
|
|
336
|
+
return [r, g, b, a];
|
|
337
|
+
}
|
|
338
|
+
function channel(c) {
|
|
339
|
+
const v = c / 255;
|
|
340
|
+
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
341
|
+
}
|
|
342
|
+
function relativeLuminance(rgb) {
|
|
343
|
+
const [r, g, b] = rgb;
|
|
344
|
+
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
|
345
|
+
}
|
|
346
|
+
function contrastRatio(fg, bg) {
|
|
347
|
+
const fgRgb = parseHex(fg) ?? parseRgb(fg);
|
|
348
|
+
const bgRgb = parseHex(bg) ?? parseRgb(bg);
|
|
349
|
+
if (!fgRgb || !bgRgb)
|
|
350
|
+
return null;
|
|
351
|
+
const l1 = relativeLuminance([fgRgb[0], fgRgb[1], fgRgb[2]]);
|
|
352
|
+
const l2 = relativeLuminance([bgRgb[0], bgRgb[1], bgRgb[2]]);
|
|
353
|
+
const [lighter, darker] = l1 > l2 ? [l1, l2] : [l2, l1];
|
|
354
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
355
|
+
}
|
|
356
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
357
|
+
export function lintKit(kit) {
|
|
358
|
+
const rules = [
|
|
359
|
+
ruleBrokenTokenRef,
|
|
360
|
+
ruleOrphanedTokens,
|
|
361
|
+
ruleWcagAaContrast,
|
|
362
|
+
ruleSectionOrdering,
|
|
363
|
+
ruleMissingPrimary,
|
|
364
|
+
ruleCircularAlias,
|
|
365
|
+
ruleUnknownProperty,
|
|
366
|
+
];
|
|
367
|
+
const issues = rules.flatMap((r) => r(kit));
|
|
368
|
+
const errors = issues.filter((i) => i.severity === "error").length;
|
|
369
|
+
const warnings = issues.filter((i) => i.severity === "warning").length;
|
|
370
|
+
const info = issues.filter((i) => i.severity === "info").length;
|
|
371
|
+
return {
|
|
372
|
+
issues,
|
|
373
|
+
summary: { errors, warnings, info, passed: errors === 0 },
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
//# sourceMappingURL=layout-md.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-md.js","sourceRoot":"","sources":["../../../src/lint/layout-md.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@layoutdesign/context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Design system context for AI coding agents — MCP server + CLI + live preview",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
21
|
"kits",
|
|
22
|
+
"skills",
|
|
22
23
|
"README.md",
|
|
23
24
|
"LICENSE"
|
|
24
25
|
],
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: layout-md
|
|
3
|
+
description: Use this skill when writing UI code against a Layout design system (`.layout/` directory with layout.md, tokens.css, tokens.json). Reads the design system, generates on-brand components, and validates output against the project's tokens and anti-patterns.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Layout design system skill
|
|
7
|
+
|
|
8
|
+
Use this skill whenever the repo contains a `.layout/` directory (or an opened
|
|
9
|
+
`layout.md` file). It gives you the project's tokens, component patterns, and
|
|
10
|
+
rules so the code you generate matches the design system.
|
|
11
|
+
|
|
12
|
+
## When to use
|
|
13
|
+
|
|
14
|
+
Invoke this skill for any task that produces UI, including:
|
|
15
|
+
|
|
16
|
+
- Creating new components (buttons, cards, forms, modals, pages)
|
|
17
|
+
- Editing existing components' styling
|
|
18
|
+
- Generating page layouts or sections
|
|
19
|
+
- Reviewing UI code for design-system compliance
|
|
20
|
+
- Answering questions like "what's our accent colour" or "how should I style X"
|
|
21
|
+
|
|
22
|
+
If the user opens a file in a folder with `.layout/`, assume this skill
|
|
23
|
+
applies even without explicit invocation.
|
|
24
|
+
|
|
25
|
+
## What's available
|
|
26
|
+
|
|
27
|
+
Layout ships a local MCP server (`@layoutdesign/context`). If the user has
|
|
28
|
+
installed it via `claude mcp add layout npx -- -y @layoutdesign/context serve`,
|
|
29
|
+
the following MCP tools are available:
|
|
30
|
+
|
|
31
|
+
- `get_design_system` — full layout.md content
|
|
32
|
+
- `get_design_section` — one section (colours, typography, spacing, etc.)
|
|
33
|
+
- `get_tokens` — token values by category
|
|
34
|
+
- `get_component` — specific component's spec + code
|
|
35
|
+
- `list_components` — every component with tokens used
|
|
36
|
+
- `check_compliance` — validate generated code against design system rules
|
|
37
|
+
|
|
38
|
+
If the MCP server is **not** installed, read the files directly:
|
|
39
|
+
|
|
40
|
+
- `.layout/layout.md` — full specification
|
|
41
|
+
- `.layout/tokens.css` — CSS custom properties
|
|
42
|
+
- `.layout/tokens.json` — W3C DTCG token format
|
|
43
|
+
- `.layout/kit.json` — manifest (name, version, tier)
|
|
44
|
+
|
|
45
|
+
## Workflow for generating UI
|
|
46
|
+
|
|
47
|
+
Follow these steps in order:
|
|
48
|
+
|
|
49
|
+
### 1. Load context before writing code
|
|
50
|
+
|
|
51
|
+
Call `get_design_system` (or read `.layout/layout.md`) once per task. Pay
|
|
52
|
+
attention to:
|
|
53
|
+
|
|
54
|
+
- The "Quick Reference" section at the top: core tokens, primary component
|
|
55
|
+
example, NEVER rules.
|
|
56
|
+
- The "Colour System" three tiers: primitive, semantic, component. Use
|
|
57
|
+
semantic or component tokens in your code. Never reference primitive
|
|
58
|
+
tokens directly.
|
|
59
|
+
- The "Components" section: the anatomy and states expected (default, hover,
|
|
60
|
+
focus, active, disabled, loading, error).
|
|
61
|
+
- The "Anti-Patterns" section: rules you must not violate.
|
|
62
|
+
|
|
63
|
+
### 2. Write code using tokens, not literal values
|
|
64
|
+
|
|
65
|
+
**Do:**
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<button style={{ background: 'var(--color-primary)', color: 'var(--color-on-primary)' }} />
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Don't:**
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<button style={{ background: '#6366F1', color: '#fff' }} />
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Hardcoded values drift from the design system and are flagged by
|
|
78
|
+
`check_compliance`.
|
|
79
|
+
|
|
80
|
+
### 3. Validate before finishing
|
|
81
|
+
|
|
82
|
+
If the MCP server is available, call `check_compliance` with your generated
|
|
83
|
+
code. Fix any errors (hardcoded colours, missing required props, unknown
|
|
84
|
+
components, arbitrary spacing) before presenting the result.
|
|
85
|
+
|
|
86
|
+
If the user has also installed `@layoutdesign/context` globally, suggest they
|
|
87
|
+
run `npx @layoutdesign/context lint` in their project root to validate the
|
|
88
|
+
entire `.layout/` directory against the seven built-in rules.
|
|
89
|
+
|
|
90
|
+
## Multi-mode (light/dark)
|
|
91
|
+
|
|
92
|
+
Layout design systems can define tokens per mode:
|
|
93
|
+
|
|
94
|
+
```css
|
|
95
|
+
:root {
|
|
96
|
+
--color-bg: #FFFFFF;
|
|
97
|
+
}
|
|
98
|
+
[data-theme="dark"] {
|
|
99
|
+
--color-bg: #0C0C0E;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
When you see `[data-theme="dark"]` blocks in tokens.css, the design system
|
|
104
|
+
supports both modes. Use plain `var(--color-bg)` in components. The theme
|
|
105
|
+
toggle on the body/html element will resolve correctly.
|
|
106
|
+
|
|
107
|
+
## Kit Gallery
|
|
108
|
+
|
|
109
|
+
If the user asks "is there a kit for X?" point them at the community Kit
|
|
110
|
+
Gallery at https://layout.design/gallery. They can:
|
|
111
|
+
|
|
112
|
+
- Browse kits by tag, sort by Featured / Top / New
|
|
113
|
+
- Install from the CLI: `npx @layoutdesign/context install <slug>`
|
|
114
|
+
- Import into Layout Studio with one click
|
|
115
|
+
|
|
116
|
+
## If you cannot find a design system
|
|
117
|
+
|
|
118
|
+
If `.layout/` does not exist and no layout.md is readable:
|
|
119
|
+
|
|
120
|
+
1. Ask the user whether a design system has been set up.
|
|
121
|
+
2. Offer to scaffold one with `npx @layoutdesign/context init` (starts with a
|
|
122
|
+
bundled kit: linear-lite, stripe-lite, or notion-lite).
|
|
123
|
+
3. Offer to import from an existing `tokens.json` with
|
|
124
|
+
`npx @layoutdesign/context import-tokens path/to/tokens.json`.
|
|
125
|
+
4. Offer to extract from a Figma file or live website in Layout Studio
|
|
126
|
+
(https://layout.design).
|
|
127
|
+
|
|
128
|
+
Do not invent tokens or make up a design system.
|
|
129
|
+
|
|
130
|
+
## Related
|
|
131
|
+
|
|
132
|
+
- Spec: https://layout.design/spec
|
|
133
|
+
- Comparison to Google's design.md: https://layout.design/vs/design-md
|
|
134
|
+
- MCP tools: `serve` from `@layoutdesign/context`
|
|
135
|
+
- CLI: `init`, `install`, `lint`, `diff`, `import-tokens`, `scan`, `serve`
|