@ericrisco/rsc 3.1.4 → 3.3.1

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.
Files changed (98) hide show
  1. package/README.md +11 -7
  2. package/manifest.json +17 -68
  3. package/package.json +6 -1
  4. package/scripts/design-upstream.js +92 -0
  5. package/scripts/doctor.js +0 -24
  6. package/scripts/lib/default-skill-floor.js +5 -1
  7. package/scripts/lib/domains.js +2 -2
  8. package/scripts/lib/recommend.js +1 -3
  9. package/scripts/lib/retired-skills.js +7 -1
  10. package/skills/design/IMPECCABLE-LICENSE.txt +191 -0
  11. package/skills/design/SKILL.md +79 -328
  12. package/skills/design/THIRD_PARTY.md +26 -0
  13. package/skills/design/engine/LICENSE +21 -0
  14. package/skills/design/engine/RUN.md +515 -0
  15. package/skills/design/engine/UPSTREAM.json +4 -0
  16. package/skills/design/engine/assets/app.css +216 -0
  17. package/skills/design/engine/assets/base.css +105 -0
  18. package/skills/design/engine/assets/base.js +37 -0
  19. package/skills/design/engine/assets/charts.js +617 -0
  20. package/skills/design/engine/assets/scenes/stage.css +28 -0
  21. package/skills/design/engine/assets/scenes/stage.js +133 -0
  22. package/skills/design/engine/library/app.json +1327 -0
  23. package/skills/design/engine/library/directions.json +1078 -0
  24. package/skills/design/engine/library/heroes.json +1086 -0
  25. package/skills/design/engine/library/motions.json +92 -0
  26. package/skills/design/engine/library/palettes.json +1485 -0
  27. package/skills/design/engine/library/photos.json +7240 -0
  28. package/skills/design/engine/library/sections.json +4429 -0
  29. package/skills/design/engine/library/sites.json +8350 -0
  30. package/skills/design/engine/library/type.json +2024 -0
  31. package/skills/design/engine/references/app-screens.md +1199 -0
  32. package/skills/design/engine/references/colour.md +141 -0
  33. package/skills/design/engine/references/copy.md +177 -0
  34. package/skills/design/engine/references/critique.md +182 -0
  35. package/skills/design/engine/references/imagery.md +791 -0
  36. package/skills/design/engine/references/layout.md +292 -0
  37. package/skills/design/engine/references/motion.md +980 -0
  38. package/skills/design/engine/references/research.md +356 -0
  39. package/skills/design/engine/references/scenes.md +174 -0
  40. package/skills/design/engine/references/tells.md +115 -0
  41. package/skills/design/engine/references/type.md +170 -0
  42. package/skills/design/engine/scripts/assets.mjs +865 -0
  43. package/skills/design/engine/scripts/draw.mjs +584 -0
  44. package/skills/design/engine/scripts/inspect.mjs +303 -0
  45. package/skills/design/engine/scripts/lib/app-draw.mjs +203 -0
  46. package/skills/design/engine/scripts/lib/cdp.mjs +248 -0
  47. package/skills/design/engine/scripts/lib/probe.mjs +909 -0
  48. package/skills/design/engine/scripts/lib/rules.mjs +243 -0
  49. package/skills/design/engine/scripts/lint.mjs +119 -0
  50. package/skills/design/engine/scripts/scenes.mjs +124 -0
  51. package/skills/design/evals/cases.yaml +35 -46
  52. package/skills/design/references/design-md.md +49 -0
  53. package/skills/design/references/finish-review.md +46 -0
  54. package/skills/design/references/modes.md +51 -0
  55. package/skills/expo/SKILL.md +3 -4
  56. package/skills/marketing/references/landing-copy.md +2 -2
  57. package/skills/react/SKILL.md +2 -2
  58. package/skills/react-native/SKILL.md +3 -3
  59. package/skills/suggest/SKILL.md +12 -8
  60. package/skills/suggest/evals/cases.yaml +6 -0
  61. package/skills/swift-ios/SKILL.md +1 -1
  62. package/skills/ui-engineering/SKILL.md +3 -4
  63. package/skills/ui-engineering/evals/cases.yaml +2 -2
  64. package/skills/variant-explorer/SKILL.md +4 -4
  65. package/skills/variant-explorer/evals/cases.yaml +1 -1
  66. package/targets/clone-bootstrap.mjs +1 -1
  67. package/scripts/lib/design-identity.js +0 -117
  68. package/scripts/lib/starting-point.js +0 -314
  69. package/skills/design/evals/README.md +0 -53
  70. package/skills/design/references/ai-tells.md +0 -75
  71. package/skills/design/references/brand-grounding.md +0 -197
  72. package/skills/design/references/copywriting-frameworks.md +0 -138
  73. package/skills/design/references/design-systems.md +0 -49
  74. package/skills/design/references/inspiration-sources.md +0 -96
  75. package/skills/design/references/landing-anatomy-and-cro.md +0 -202
  76. package/skills/design/references/motion-and-interaction.md +0 -232
  77. package/skills/design/references/research-method.md +0 -154
  78. package/skills/design/references/signature-and-craft.md +0 -148
  79. package/skills/design/references/starting-point.md +0 -75
  80. package/skills/design/references/trends-2026.md +0 -80
  81. package/skills/design/references/visual-system.md +0 -237
  82. package/skills/design/scripts/verify.sh +0 -357
  83. package/skills/design-dna/PROMPT.md +0 -211
  84. package/skills/design-dna/SKILL.md +0 -162
  85. package/skills/design-dna/design-dna.schema.json +0 -230
  86. package/skills/design-dna/evals/README.md +0 -64
  87. package/skills/design-dna/evals/cases.yaml +0 -68
  88. package/skills/design-dna/references/method.md +0 -436
  89. package/skills/design-dna/scripts/emit.py +0 -164
  90. package/skills/design-dna/templates/PROMPT.md.tmpl +0 -42
  91. package/skills/design-dna/templates/SKILL.md.tmpl +0 -42
  92. package/skills/design-dna/templates/check.py.tmpl +0 -143
  93. package/skills/design-loop/SKILL.md +0 -160
  94. package/skills/design-loop/evals/README.md +0 -56
  95. package/skills/design-loop/evals/cases.yaml +0 -60
  96. package/skills/motion-craft/SKILL.md +0 -125
  97. package/skills/motion-craft/evals/cases.yaml +0 -38
  98. package/skills/motion-craft/scripts/verify.sh +0 -75
@@ -1,314 +0,0 @@
1
- // What does this project already own to start from, and does its colour hold up?
2
- //
3
- // The design area declares three times that it will propose a starting point when the user has
4
- // none — `design-loop` phase 1 on `skip`, the closing line of `design`'s brand grounding, and
5
- // `design-dna`'s REUSE mode — and none of the three had anything behind it. An unbacked promise to
6
- // propose falls back to the model's prior, which is the AI-template median the area exists to
7
- // escape. Spec: 02-DOCS/wiki/sdd/specs/design-starting-point.md
8
- //
9
- // This file is the half that can be an algorithm (P1): listing what is installed, and refusing to
10
- // propose a colour that fails contrast. The half that needs judgement — which reference is the right
11
- // bar for what you are building — is prose, in design/references/starting-point.md.
12
- //
13
- // Read-only, always. `02-DOCS` is untracked and has no undo (P9), and a discovery pass has no
14
- // business writing anywhere.
15
- import { existsSync, lstatSync, readFileSync, readdirSync, statSync } from 'node:fs';
16
- import { join } from 'node:path';
17
- import { homedir } from 'node:os';
18
- import { designIdentity, BRAND_DIR } from './design-identity.js';
19
-
20
- // WCAG 2.2 AA. Body text, and the relaxed floor for large text and UI components.
21
- export const TEXT_MIN = 4.5;
22
- export const UI_MIN = 3.0;
23
-
24
- // Which palette roles are held to which floor. `design-dna`'s schema fixes the role vocabulary
25
- // (ground, ink, accent, secondary, hairline); anything outside it is skipped rather than guessed at,
26
- // because inventing a threshold for an unknown role is how a gate starts approving things.
27
- const TEXT_ROLES = new Set(['ink', 'secondary']);
28
- const UI_ROLES = new Set(['accent']);
29
- const GROUND_ROLE = 'ground';
30
-
31
- // A record is only reusable if it carries the fields a style is actually rebuilt from. Half a record
32
- // cannot be reused, so it is reported as unreadable instead of offered as a candidate.
33
- const REQUIRED = ['palette'];
34
- const DIMENSIONS = ['palette', 'type', 'space', 'surface', 'motion', 'signatures', 'voice'];
35
-
36
- /** Where `design-dna` emits, and where rsc installs, in both scopes. */
37
- export function defaultRoots({ home = homedir(), cwd = process.cwd() } = {}) {
38
- const roots = [];
39
- for (const base of home === cwd ? [cwd] : [cwd, home]) {
40
- roots.push(join(base, '.claude', 'skills'));
41
- roots.push(join(base, '.rsc', 'skills'));
42
- }
43
- return roots;
44
- }
45
-
46
- function parseHex(hex) {
47
- if (typeof hex !== 'string') return null;
48
- const m = /^#([0-9a-fA-F]{6})$/.exec(hex.trim());
49
- if (!m) return null;
50
- const n = parseInt(m[1], 16);
51
- return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
52
- }
53
-
54
- // WCAG relative luminance. The sRGB transfer function, not a shortcut: an approximation here is a
55
- // wrong ratio everywhere downstream, and it would pass a test written against this file's own output.
56
- function luminance([r, g, b]) {
57
- const lin = [r, g, b].map((c) => {
58
- const s = c / 255;
59
- return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
60
- });
61
- return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2];
62
- }
63
-
64
- /**
65
- * WCAG 2.x contrast ratio between two colours, 1.0 … 21.0. Symmetric.
66
- * @param {string} a sRGB hex, `#rrggbb`
67
- * @param {string} b sRGB hex, `#rrggbb`
68
- */
69
- export function contrastRatio(a, b) {
70
- const ca = parseHex(a);
71
- const cb = parseHex(b);
72
- if (!ca || !cb) throw new TypeError(`not an sRGB hex colour: ${!ca ? a : b}`);
73
- const la = luminance(ca);
74
- const lb = luminance(cb);
75
- const [hi, lo] = la >= lb ? [la, lb] : [lb, la];
76
- return (hi + 0.05) / (lo + 0.05);
77
- }
78
-
79
- /**
80
- * Does this palette's text hold up against its own ground?
81
- *
82
- * Runs before a measured colour is written as a proposed dimension: a value that fails is not
83
- * proposed at all, rather than proposed and flagged. Three outcomes are kept apart on purpose —
84
- * measured-and-passing, measured-and-failing, and **not measured**. Collapsing the third into the
85
- * first is the vacuous pass this area has already paid for twice.
86
- *
87
- * @param {Array<{role: string, hex: string, name?: string}>} colors — `palette.colors` from a dna.json
88
- * @returns {{ok: boolean, pairs: Array<object>, failures: Array<object>, skipped: Array<object>,
89
- * unparsed: string[], reason: string}}
90
- */
91
- export function checkPairings(colors) {
92
- const list = Array.isArray(colors) ? colors : [];
93
- const pairs = [];
94
- const skipped = [];
95
- const unparsed = [];
96
-
97
- const ground = list.find((c) => c && c.role === GROUND_ROLE);
98
- const groundHex = ground ? parseHex(ground.hex) : null;
99
- if (!ground) {
100
- return { ok: false, pairs, failures: [], skipped, unparsed, reason: 'no ground role in the palette — nothing to measure against' };
101
- }
102
- if (!groundHex) {
103
- unparsed.push(String(ground.hex));
104
- return { ok: false, pairs, failures: [], skipped, unparsed, reason: `ground colour is not an sRGB hex: ${ground.hex}` };
105
- }
106
-
107
- for (const c of list) {
108
- if (!c || c.role === GROUND_ROLE) continue;
109
- const isText = TEXT_ROLES.has(c.role);
110
- const isUi = UI_ROLES.has(c.role);
111
- if (!isText && !isUi) {
112
- // A hairline is not text. Saying so is not the same as saying it passed.
113
- skipped.push({ role: c.role, name: c.name, reason: 'role carries no text, so no text floor applies' });
114
- continue;
115
- }
116
- if (!parseHex(c.hex)) {
117
- unparsed.push(String(c.hex));
118
- continue;
119
- }
120
- const min = isText ? TEXT_MIN : UI_MIN;
121
- const ratio = Math.round(contrastRatio(c.hex, ground.hex) * 100) / 100;
122
- pairs.push({ role: c.role, name: c.name, fg: c.hex, bg: ground.hex, ratio, min, ok: ratio >= min });
123
- }
124
-
125
- const failures = pairs.filter((p) => !p.ok);
126
- // Not measuring anything is not a pass. An empty failure list only means green when at least one
127
- // pair was actually evaluated and nothing was left unparsed.
128
- const ok = failures.length === 0 && unparsed.length === 0 && pairs.length > 0;
129
- const reason = unparsed.length
130
- ? `${unparsed.length} colour(s) could not be parsed, so they were not measured`
131
- : pairs.length === 0
132
- ? 'no text or UI role to measure against the ground'
133
- : failures.length
134
- ? `${failures.length} pair(s) below their floor`
135
- : `${pairs.length} pair(s) measured, all above their floor`;
136
- return { ok, pairs, failures, skipped, unparsed, reason };
137
- }
138
-
139
- function readRecord(dir, slug) {
140
- const file = join(dir, 'dna.json');
141
- let raw;
142
- try {
143
- raw = readFileSync(file, 'utf8');
144
- } catch (e) {
145
- return { error: `cannot read dna.json: ${e.code || e.message}` };
146
- }
147
- let dna;
148
- try {
149
- dna = JSON.parse(raw);
150
- } catch (e) {
151
- return { error: `dna.json is not valid JSON: ${e.message}` };
152
- }
153
- const missing = REQUIRED.filter((k) => !dna || !dna[k]);
154
- if (missing.length) return { error: `record is missing ${missing.join(', ')} — half a record cannot be reused` };
155
-
156
- return {
157
- record: {
158
- slug: (dna.meta && dna.meta.slug) || slug,
159
- name: (dna.meta && dna.meta.name) || slug,
160
- path: file,
161
- covers: DIMENSIONS.filter((d) => dna[d]),
162
- palette: (dna.palette && dna.palette.colors) || [],
163
- },
164
- };
165
- }
166
-
167
- // What the harness's own identity articles mention. A record the project actually used outranks one
168
- // that merely happens to be installed — but the others stay on the list, because choosing for the
169
- // user is exactly what the spec forbids.
170
- function citedSlugs(harnessRoot) {
171
- const dir = join(harnessRoot, BRAND_DIR);
172
- let text = '';
173
- try {
174
- for (const f of readdirSync(dir)) {
175
- if (!f.endsWith('.md')) continue;
176
- try { text += readFileSync(join(dir, f), 'utf8').toLowerCase(); } catch { /* one unreadable article is not the answer */ }
177
- }
178
- } catch { /* no identity here; designIdentity reports that half */ }
179
- return text;
180
- }
181
-
182
- /**
183
- * What this project already owns to start a design from.
184
- *
185
- * @param {string[]} roots — skill directories to scan. Defaults to both scopes, both layouts.
186
- * @param {string} harnessRoot — where `02-DOCS/` lives, for the identity half and the ordering.
187
- * @returns {{state: 'owned'|'none'|'inconclusive', identity: object, records: object[],
188
- * unreadable: Array<{path: string, reason: string}>, reason: string, fix?: string}}
189
- * `owned` — an identity or at least one legible record exists.
190
- * `none` — looked everywhere, found nothing. Carries the way out (P6).
191
- * `inconclusive` — something could not be read. Never reported as `none`, never as green.
192
- */
193
- export function ownedStartingPoints(roots = defaultRoots(), harnessRoot = process.cwd()) {
194
- const identity = designIdentity(harnessRoot);
195
- const records = [];
196
- const unreadable = [];
197
- let blind = 0;
198
-
199
- for (const root of roots) {
200
- // existsSync follows symlinks, so a dangling one answers false and would be filed as "nothing
201
- // installed here". That is unreadability, not absence — the exact conflation designIdentity was
202
- // fixed for. lstat sees the link itself and tells them apart.
203
- if (!existsSync(root)) {
204
- let dangling = false;
205
- try { dangling = lstatSync(root).isSymbolicLink(); } catch { /* genuinely absent */ }
206
- if (dangling) {
207
- blind += 1;
208
- unreadable.push({ path: root, reason: 'skills root is a symlink that does not resolve' });
209
- }
210
- continue;
211
- }
212
- let entries;
213
- try {
214
- if (!statSync(root).isDirectory()) {
215
- blind += 1;
216
- unreadable.push({ path: root, reason: 'skills root exists but is not a directory' });
217
- continue;
218
- }
219
- entries = readdirSync(root, { withFileTypes: true });
220
- } catch (e) {
221
- blind += 1;
222
- unreadable.push({ path: root, reason: `cannot read skills root: ${e.code || e.message}` });
223
- continue;
224
- }
225
- for (const entry of entries) {
226
- // Dirents, not statSync: a symlinked entry could otherwise report on a directory outside the
227
- // root we were asked to scan.
228
- if (!entry.isDirectory()) continue;
229
- const dir = join(root, entry.name);
230
- if (!existsSync(join(dir, 'dna.json'))) continue;
231
- const { record, error } = readRecord(dir, entry.name);
232
- if (error) { unreadable.push({ path: join(dir, 'dna.json'), reason: error }); continue; }
233
- if (!records.some((r) => r.slug === record.slug)) records.push(record);
234
- }
235
- }
236
-
237
- const cited = citedSlugs(harnessRoot);
238
- records.sort((a, b) => {
239
- const ac = cited.includes(a.slug.toLowerCase()) ? 0 : 1;
240
- const bc = cited.includes(b.slug.toLowerCase()) ? 0 : 1;
241
- return ac - bc || a.slug.localeCompare(b.slug);
242
- });
243
-
244
- if (records.length || identity.state === 'present') {
245
- return {
246
- state: 'owned',
247
- identity,
248
- records,
249
- unreadable,
250
- reason: [
251
- identity.state === 'present' ? `identity present (${identity.articles.length} article(s))` : 'no harness identity',
252
- `${records.length} style record(s)`,
253
- unreadable.length ? `${unreadable.length} unreadable` : null,
254
- ].filter(Boolean).join(', '),
255
- };
256
- }
257
-
258
- // Something was there and we could not use it. Saying "nothing to propose" would be a finding we
259
- // did not earn, and saying nothing at all would hide it.
260
- if (unreadable.length || blind || identity.state === 'inconclusive') {
261
- return {
262
- state: 'inconclusive',
263
- identity,
264
- records,
265
- unreadable,
266
- reason: unreadable.length
267
- ? `could not read ${unreadable.length} candidate(s): ${unreadable[0].reason}`
268
- : `identity could not be read: ${identity.reason}`,
269
- fix: 'name what could not be read, and ask for another route to it — never propose past a blind spot',
270
- };
271
- }
272
-
273
- return {
274
- state: 'none',
275
- identity,
276
- records: [],
277
- unreadable,
278
- reason: 'no harness identity and no style record installed in any scope',
279
- fix: 'propose at most three openable references as candidate bars (design/references/starting-point.md), then run `design-loop`',
280
- };
281
- }
282
-
283
- /**
284
- * The compact projection `doctor` reports: one object, not a section (P5 — every byte of a report is
285
- * paid by every user who runs it). Contrast is folded in per record because a record whose own
286
- * palette fails is not a starting point anyone should be offered.
287
- *
288
- * @returns {{state: string, records: string[], contrast: string, unreadable: number, reason: string}}
289
- */
290
- export function startingPointSummary(root = process.cwd(), roots = defaultRoots({ cwd: root })) {
291
- const r = ownedStartingPoints(roots, root);
292
- let failing = 0;
293
- let unmeasured = 0;
294
- for (const rec of r.records) {
295
- const c = checkPairings(rec.palette);
296
- if (c.failures.length) failing += 1;
297
- else if (!c.ok) unmeasured += 1;
298
- }
299
- const contrast = r.records.length === 0
300
- ? 'no record to measure'
301
- : failing
302
- ? `${failing} record(s) with a pair below its floor`
303
- : unmeasured
304
- ? `${unmeasured} record(s) could not be measured`
305
- : 'all records above their floors';
306
- return {
307
- state: r.state,
308
- records: r.records.map((x) => x.slug),
309
- contrast,
310
- unreadable: r.unreadable.length,
311
- reason: r.reason,
312
- ...(r.fix ? { fix: r.fix } : {}),
313
- };
314
- }
@@ -1,53 +0,0 @@
1
- # Eval harness — `design` skill
2
-
3
- This is an **agent-run** eval, not a shell script. The cases in `cases.yaml` are fed to a
4
- Claude Code agent and graded by inspection. There is no `pytest` to run; the "harness" is the
5
- procedure below plus a human (or a judge agent) checking outputs against the rubrics.
6
-
7
- ## What's under test
8
-
9
- 1. **Triggering** — does the `design` skill activate on the right prompts and stay quiet on
10
- near-misses that belong to a sibling (`marketing`, `nextjs`, `flutter`) or to nothing in-repo?
11
- 2. **Capability** — when it does fire, does the guidance measurably beat an unguided answer?
12
-
13
- ## A. Triggering accuracy
14
-
15
- For each prompt in `should_trigger` and `should_not_trigger`:
16
-
17
- 1. Start a fresh agent session with **only the `design` skill discoverable** (its `SKILL.md`
18
- description in the system prompt / skill index). Do not pre-load sibling skills.
19
- 2. Paste the prompt verbatim as the user's first message.
20
- 3. Observe whether the agent **invokes/loads the `design` skill** (announces it, reads
21
- `SKILL.md`, or applies its protocol) before answering.
22
- 4. Run **3–5 independent trials per prompt** (fresh session each) to average out sampling noise.
23
-
24
- Scoring per prompt:
25
- - `should_trigger` → PASS if the skill fires in the **majority** of trials.
26
- - `should_not_trigger` → PASS if the skill **does not** fire in the majority of trials. Bonus:
27
- the agent routes to the `route_to` target (or correctly declines when `route_to: none`).
28
-
29
- **Pass bar:** >= 90% trigger accuracy across all prompts (true-positives + true-negatives over
30
- total). Any `should_not_trigger` that fires in a majority of trials is a hard fail to fix in the
31
- `SKILL.md` description/When-NOT section, even if the aggregate clears 90%.
32
-
33
- ## B. Capability uplift (with vs without)
34
-
35
- For each `capability` scenario:
36
-
37
- 1. **Without:** fresh session, skill NOT available, give the scenario prompt. Save the answer.
38
- 2. **With:** fresh session, `design` skill available and applied, same prompt. Save the answer.
39
- 3. Grade both against the scenario's `must_include` points — one point per item, mark
40
- covered / not covered. Use a second agent as judge or grade by hand.
41
- 4. Compute coverage = covered / total `must_include`, for each condition.
42
-
43
- **Pass bar:** WITH the skill covers **>= 80%** of `must_include`; WITHOUT it covers materially
44
- less (target a >= 30-point absolute gap). If the gap is small, the skill isn't earning its place —
45
- fix the skill, not the rubric. The brand-grounding STOP and the research-first/CWV/WCAG points are
46
- the high-signal items most likely to be missing without the skill; weight attention there.
47
-
48
- ## Notes / honesty
49
-
50
- - Triggering is inherently stochastic; that's why we average trials and report a percentage, not a
51
- binary. Re-run if results straddle the bar.
52
- - Keep grading faithful to `SKILL.md` — if a case drifts from the actual description, fix the case.
53
- - These files document intent and a repeatable procedure; they are **not** wired into CI.
@@ -1,75 +0,0 @@
1
- # AI tells — the ones only a reader can catch
2
-
3
- The mechanical tells are **not here**. `scripts/verify.sh` carries a registry of them and
4
- flags them with file and line, so re-teaching them as prose would be paying twice for the
5
- same rule. This file holds only what a `grep` cannot decide: presences you have to *look* at
6
- the page to see.
7
-
8
- Part of this corpus is adapted from [Leonxlnx/taste-skill](https://github.com/Leonxlnx/taste-skill)
9
- (MIT), whose lists came out of real production tests. The split into checkable-vs-judged, the
10
- thresholds, and the wording are ours.
11
-
12
- Every item below is a **default**, overridable by a brief that explicitly asks for it. What
13
- makes them tells is that they show up *unasked*, as the model's idea of "looking designed".
14
-
15
- ## Fabricated substance
16
-
17
- - **A product UI built out of divs.** A fake task list, fake terminal or fake dashboard
18
- assembled from styled rectangles is the single most reliable signature of generated work.
19
- Use a real screenshot, a real component, a generated image, or show nothing.
20
- - **Hand-rolled decorative SVG.** Abstract squiggles and blobs authored inline to fill space.
21
- Icons come from a maintained set; decoration comes from a real asset or not at all.
22
- - **The Jane Doe cast.** "John Doe", "Sarah Chan", egg avatars, `99.99%`, `50%`, round
23
- thousands, and startup-slop brand names ("Acme", "Nexus", "SmartFlow"). Real work has
24
- specific, slightly awkward names and messy numbers (`47.2%`, not `50%`).
25
- - **Testimonials nobody said.** A quote with no attributable person, or five quotes with the
26
- same cadence, reads as filler. One real quote beats three invented ones.
27
-
28
- ## Decoration wearing the costume of information
29
-
30
- - **Atmospheric locale strips.** A city, a timezone, a temperature in the nav or footer. Only
31
- earns its place for a genuinely distributed studio, a travel brand, or a physical venue.
32
- - **Status dots with no status.** A coloured dot before nav items, list rows or badges. Keep
33
- it only where it reports real state, and then only once per section.
34
- - **Invented photo credits.** `Field study no. 12 · Ines Caetano` under a stock image.
35
- Credit a real photographer or write a functional caption.
36
- - **Pills laid over images.** Tags floated on top of a photo. Either the image speaks alone,
37
- or the caption sits below it, outside the frame.
38
- - **Decoration strips under the hero.** `BRAND. MOTION. SPATIAL.` in small mono caps. Only
39
- legitimate when those words are real navigation or real status.
40
-
41
- ## Labels that say nothing
42
-
43
- - **Poetic section labels.** "Field notes", "From the field", "On our desks", "Currently on
44
- the bench". Use the functional name ("Testimonials", "Latest writing") or no label.
45
- - **Generic step labels.** "Stage 1 / Stage 2", "Phase 01 / 02". The step content *is* the
46
- label: "Install", "Configure", "Ship".
47
- - **Mock-humble asides** and micro-meta sentences under a heading ("The list will stay short
48
- on purpose"). Eyebrow, headline, body. Nothing else.
49
- - **"Quietly trusted by".** Say "Used at", "Customers include", or let the logos speak.
50
-
51
- ## Composition habits
52
-
53
- - **Three identical feature cards.** The default feature row. Reach for a two-column zig-zag,
54
- an asymmetric grid, or a horizontal rail instead — and do not run the same split three
55
- sections in a row.
56
- - **The corner floater.** A giant left-aligned headline with a small explainer paragraph
57
- floating in the section's top-right, aligned to nothing. Stack it, or build a real
58
- two-column header.
59
- - **Rotated vertical text** and crosshair grid lines drawn only to look designed. Both are
60
- agency-portfolio clichés unless they organise real content.
61
- - **A bento with empty cells.** N items means N cells, and at least two cells need real
62
- visual variation — otherwise it is a white card grid with extra steps.
63
-
64
- ## The second default (repetition)
65
-
66
- Obeying every rule produces correct pages that all look the same. Before fixing type and
67
- palette, read the project's recorded design decisions and either reuse them deliberately or
68
- say why you are departing. Two specific defaults to distrust: reaching for the same display
69
- serif again, and answering every premium-consumer brief with the beige/brass/oxblood family.
70
-
71
- ## Surface treatments to distrust by default
72
-
73
- Outer glows and neon; pure `#000000` (use an off-black); oversaturated accents that refuse to
74
- sit with the neutrals; gradient fills on large headings; custom cursors (outdated, hostile to
75
- accessibility and to performance); glass on every surface rather than on floating ones.
@@ -1,197 +0,0 @@
1
- # Brand Grounding — Checklist, Question Script & Persistence (visual)
2
-
3
- The brand study is the source of truth every design decision is grounded in. This file holds the **completeness checklist** (the visual/design dimensions a brand study must cover before you paint a single pixel), the **question script** (how to interview the user, batched), and the **persistence format** (how to write it into `02-DOCS` and link it from `CLAUDE.md`). The runtime mechanism that invokes this — the hard STOP on an incomplete study — lives in `SKILL.md` under "Brand grounding (read this first)".
4
-
5
- This skill **co-owns the brand study with the `marketing` skill**. Marketing owns the *words* dimensions (positioning, audience, value prop, voice & tone, proof, offers, channels, SEO/GEO keywords — see `../../marketing/references/brand-grounding.md`). Design owns the *visual* dimensions below. They write into the **same** `02-DOCS/wiki/brand/` study and the **same** `## Brand & voice` section of root `CLAUDE.md` — never a parallel copy. If marketing already created the study, extend it with the visual articles; do not duplicate the words articles.
6
-
7
- ## Where the brand study lives
8
-
9
- Following the `harness` Karpathy-wiki convention (compiled articles in `wiki/`, immutable raw inputs in `raw/`):
10
-
11
- ```text
12
- 02-DOCS/
13
- ├── raw/brand/ ← immutable: pasted inputs (screenshots, palettes, competitor lists, mood refs)
14
- │ ├── inspiration-linear.png
15
- │ ├── existing-palette.txt
16
- │ └── …
17
- └── wiki/brand/ ← compiled brand study, one article per dimension
18
- ├── index.md ← positioning one-liner + links to every dimension article
19
- ├── visual-identity.md ← color (OKLCH), type pairing & scale, logo, radius/shadow/motion personality [DESIGN]
20
- ├── imagery.md ← photography vs illustration mood, iconography, texture/grain [DESIGN]
21
- ├── layout.md ← density, composition, dark-mode stance, reference/inspiration sites [DESIGN]
22
- ├── constraints.md ← accessibility level, performance budget, existing design system/tokens [DESIGN]
23
- ├── audience.md ← ICP, pains, desires [MARKETING]
24
- ├── value-proposition.md ← value prop, differentiation [MARKETING]
25
- ├── voice.md ← tone, do/don't word lists, voice samples [MARKETING]
26
- ├── proof.md ← credibility, metrics, customers [MARKETING]
27
- ├── offers.md ← offers + primary/secondary CTA [MARKETING]
28
- ├── channels.md ← active channels [MARKETING]
29
- └── seo.md ← target keywords [MARKETING]
30
- ```
31
-
32
- Small projects may collapse the visual dimensions into one `visual-identity.md` with each dimension as a `##` heading — but every dimension below must still be present and filled.
33
-
34
- ## Completeness checklist (visual dimensions)
35
-
36
- The visual side of the study is **COMPLETE** only when every dimension below is filled with real, specific content. Any empty, placeholder, or "TBD" dimension = **INCOMPLETE** = hard STOP, interview the user. (The words dimensions are checked by `marketing/references/brand-grounding.md`; design needs the voice/positioning articles present so copy and design agree, but design only *interviews* for the visual gaps.)
37
-
38
- - [ ] **V1. Color system** — brand hue(s) and neutrals expressed (or convertible) to OKLCH; a target ramp (50→900); the accent's role; the 60-30-10 intent. Capture any existing hex/brand colors so they can be converted to OKLCH tokens (OKLCH is the source of truth; sRGB hex is an approximation — see `visual-system.md`).
39
- - [ ] **V2. Typography** — display face + text face (and mono if code is shown), or a single superfamily with weight contrast; the modular scale ratio (e.g. 1.25); any licensed/owned fonts the brand must use. Never three+ families.
40
- - [ ] **V3. Logo & wordmark** — does one exist? light/dark variants, clear-space, min size, and whether a favicon/app icon is needed. If absent, note it as a gap (`[[NEEDS ASSET]]`) — never invent a logo silently.
41
- - [ ] **V4. Imagery & illustration mood** — photography vs 3D vs illustration vs abstract; realistic vs stylized; iconography style (line vs filled, corner radius); texture/grain/noise stance. This sets whether the hero shows a product screenshot, a render, or an editorial image.
42
- - [ ] **V5. Density & personality** — how the surface should *feel*: airy/editorial vs dense/utilitarian; the radius personality (sharp 0–4px vs soft 12px+); shadow personality (flat vs layered elevation); motion personality (still/quiet vs expressive). These become token values.
43
- - [ ] **V6. Reference / inspiration sites** — 2–5 sites the user loves and *why* (e.g. "Linear for restraint", "Stripe for gradients done right"). These anchor the research-first protocol's archetypes; without them you research blind.
44
- - [ ] **V7. Layout preferences** — composition leanings (centered hero vs asymmetric/bento vs editorial), max-width feel, any required sections or a sitemap, mobile-first vs desktop-first emphasis.
45
- - [ ] **V8. Dark-mode stance** — light-only, dark-only, or both with a token-swap; if both, which is the default. (Dark mode via semantic token swap, not per-component overrides — see `visual-system.md`.)
46
- - [ ] **V9. Accessibility & performance constraints** — target conformance (WCAG 2.2 AA is the floor here; AAA if mandated); Core Web Vitals budget (LCP/INP/CLS); locales/RTL; reduced-motion seriousness; any device/bandwidth constraints.
47
- - [ ] **V10. Existing design system / tokens** — is there a Tailwind `@theme`, a `tokens.json`, a Figma library, or component kit to extend? Reusing the system beats inventing a parallel one.
48
- - [ ] **V11. Voice & positioning present (cross-check)** — the marketing-owned `voice.md` and `value-proposition.md` articles exist and are filled, so copy and design agree. If absent, hand off to the `marketing` sibling skill (or interview for them too) — a premium surface with off-voice copy is not premium.
49
-
50
- ## Question script (ask in batches, never all at once)
51
-
52
- Ask **one batch at a time**. Send the batch, wait for the answer, persist what you learned, then send the next batch. Skip questions a located-but-incomplete study already answers — only fill the gaps. Stop interviewing the moment every dimension is complete.
53
-
54
- ### Batch 1 — references & feel
55
-
56
- ```text
57
- 1. Show me 2–5 sites or apps whose look you love — and for each, what specifically (the
58
- restraint, the type, the color, the motion)? Paste links or screenshots.
59
- 2. In three words, how should this feel? (e.g. calm/editorial, dense/technical,
60
- playful/vivid, refined/luxury)
61
- 3. Airy or dense? Should it breathe with whitespace, or pack information tightly?
62
- 4. Light mode, dark mode, or both? If both, which is the default?
63
- ```
64
-
65
- ### Batch 2 — color & type
66
-
67
- ```text
68
- 5. Do you have brand colors already? Paste any hex/RGB values, a palette, or a brand guide —
69
- I'll convert them to an OKLCH token ramp (I'll save the raw input under 02-DOCS/raw/brand/).
70
- 6. If not: pick a brand hue direction (cool/blue, warm/orange, green, violet, near-neutral)
71
- and how saturated it should read (muted vs vivid).
72
- 7. Any required or owned fonts? If not, do you lean geometric-sans, humanist-sans, serif/
73
- editorial, or mono-forward (dev tool)?
74
- 8. How loud should the type be — understated, or oversized expressive headlines?
75
- ```
76
-
77
- ### Batch 3 — assets, imagery & layout
78
-
79
- ```text
80
- 9. Is there a logo/wordmark? Send light + dark versions if you have them. Need a favicon/app icon?
81
- 10. What imagery direction — product screenshots, photography, 3D renders, illustration, or
82
- abstract? Realistic or stylized? Any reference for the icon style?
83
- 11. Radius/shadow/motion personality: sharp & flat, or soft & elevated? Still & quiet, or
84
- expressive motion? (I'll bake these into tokens.)
85
- 12. Any must-have sections or a required sitemap? Centered hero or asymmetric/bento?
86
- ```
87
-
88
- ### Batch 4 — constraints & system
89
-
90
- ```text
91
- 13. Accessibility target — is WCAG 2.2 AA enough, or is AAA / a specific standard mandated?
92
- Any RTL/locale needs?
93
- 14. Performance budget or device constraints I should design within? (slow networks, low-end
94
- devices, a hard LCP target?)
95
- 15. Is there an existing design system, Tailwind @theme, tokens.json, or Figma library to
96
- extend rather than replace?
97
- ```
98
-
99
- If the user can't answer a question, that dimension stays incomplete — note the gap, keep the STOP in place for that dimension, and offer to propose a researched default they can confirm, rather than silently defaulting to your generic prior. **How to actually propose one: `starting-point.md`.** It is the difference between offering something and improvising something: what the project already owns is checked first, the value comes from a reference you opened and measured, and it is written marked `propuesto`. A proposed-and-unconfirmed dimension does **not** count as complete against the checklist above — the STOP stays up until the user confirms it.
100
-
101
- ## Persistence format
102
-
103
- ### Visual-identity wiki article template
104
-
105
- Each `02-DOCS/wiki/brand/*.md` article follows the harness wiki format. It is an OKF v0.1 article: open it with YAML frontmatter carrying a non-empty `type:` (`brand-study`), then the H1 and body:
106
-
107
- ```markdown
108
- ---
109
- type: brand-study
110
- title: Brand — Visual Identity
111
- description: The visual personality — OKLCH color system, type pairing & scale, and radius/shadow/motion personality.
112
- tags: [brand, visual-identity, design]
113
- timestamp: YYYY-MM-DDTHH:MM:SSZ
114
- topic: brand
115
- status: stable
116
- ---
117
-
118
- # Brand — Visual Identity
119
-
120
- > Sources: {user interview, YYYY-MM-DD}
121
- > Raw: [existing-palette](../../raw/brand/existing-palette.txt); [inspiration-linear](../../raw/brand/inspiration-linear.png)
122
-
123
- ## Overview
124
-
125
- One paragraph: the visual personality and the ONE memorable detail.
126
-
127
- ## Color (OKLCH source of truth)
128
-
129
- | Token | OKLCH | Role |
130
- | --- | --- | --- |
131
- | brand-500 | oklch(0.62 0.19 256) | primary accent (10% allocation) |
132
- | bg | oklch(0.99 0 0) | background (light) |
133
- | fg | oklch(0.21 0.01 256) | foreground |
134
-
135
- (Any sRGB hex the user gave is recorded as an approximation, not the source — see `visual-system.md`.)
136
-
137
- ## Typography
138
-
139
- - Display: Geist Semibold · Text: Inter Regular · Mono: Geist Mono (code)
140
- - Scale ratio: 1.25 (major third), 16px base
141
-
142
- ## Personality
143
-
144
- - Radius: soft (0.875rem cards) · Shadow: layered transparent elevation · Motion: quiet, entry reveals only
145
-
146
- ## See Also
147
-
148
- - [Layout](layout.md)
149
- - [Constraints](constraints.md)
150
- ```
151
-
152
- ### Raw inputs
153
-
154
- Paste each user-provided input (palette text, competitor list, screenshots saved as files) under `02-DOCS/raw/brand/` with a one-line provenance header on text files:
155
-
156
- ```markdown
157
- > Source: user-pasted, YYYY-MM-DD, origin: "current brand guide, page 3 palette"
158
-
159
- <the pasted values, unedited>
160
- ```
161
-
162
- Binary inputs (screenshots, logo files) go in verbatim and are linked from the article's `> Raw:` line; never compiled or edited.
163
-
164
- ### CLAUDE.md link
165
-
166
- Add (or **extend**, never duplicate) the shared `## Brand & voice` section in the root `CLAUDE.md`. Additive only — never delete existing sections. Create `CLAUDE.md` if absent. If `marketing` already created this section, append the visual links to it rather than writing a second section.
167
-
168
- ```markdown
169
- ## Brand & voice
170
-
171
- Design and marketing/landing copy are grounded in the brand study under
172
- `02-DOCS/wiki/brand/`. Read it before any user-facing design or copy:
173
-
174
- - [Positioning](02-DOCS/wiki/brand/index.md)
175
- - [Visual identity (color, type, personality)](02-DOCS/wiki/brand/visual-identity.md)
176
- - [Imagery & illustration](02-DOCS/wiki/brand/imagery.md)
177
- - [Layout & references](02-DOCS/wiki/brand/layout.md)
178
- - [Accessibility & performance constraints](02-DOCS/wiki/brand/constraints.md)
179
- - [Audience](02-DOCS/wiki/brand/audience.md)
180
- - [Value proposition](02-DOCS/wiki/brand/value-proposition.md)
181
- - [Voice & tone](02-DOCS/wiki/brand/voice.md)
182
-
183
- Raw inputs (palettes, screenshots, voice samples): `02-DOCS/raw/brand/`.
184
- The `design` skill owns the visual articles; `marketing` owns the words
185
- articles. Either stops to interview the user if its dimensions are missing.
186
- ```
187
-
188
- Also update `02-DOCS/wiki/index.md` (add the new brand articles) and append a one-line entry to `02-DOCS/wiki/log.md` per the harness wiki protocol.
189
-
190
- ## See Also
191
-
192
- - `../SKILL.md` — the runtime grounding mechanism (hard STOP) that uses this checklist.
193
- - `visual-system.md` — turns the captured color/type/personality into Tailwind v4 `@theme` tokens.
194
- - `research-method.md` — uses the reference sites (V6) as archetypes for the research loop.
195
- - `trends-2026.md` — researched defaults to offer when a visual dimension has no user direction.
196
- - `../../marketing/references/brand-grounding.md` — the co-owned words dimensions (incl. SEO/GEO) of the same study.
197
- - `../../harness/SKILL.md` — the canonical `02-DOCS` wiki protocol and article templates.