@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.
- package/README.md +11 -7
- package/manifest.json +17 -68
- package/package.json +6 -1
- package/scripts/design-upstream.js +92 -0
- package/scripts/doctor.js +0 -24
- package/scripts/lib/default-skill-floor.js +5 -1
- package/scripts/lib/domains.js +2 -2
- package/scripts/lib/recommend.js +1 -3
- package/scripts/lib/retired-skills.js +7 -1
- package/skills/design/IMPECCABLE-LICENSE.txt +191 -0
- package/skills/design/SKILL.md +79 -328
- package/skills/design/THIRD_PARTY.md +26 -0
- package/skills/design/engine/LICENSE +21 -0
- package/skills/design/engine/RUN.md +515 -0
- package/skills/design/engine/UPSTREAM.json +4 -0
- package/skills/design/engine/assets/app.css +216 -0
- package/skills/design/engine/assets/base.css +105 -0
- package/skills/design/engine/assets/base.js +37 -0
- package/skills/design/engine/assets/charts.js +617 -0
- package/skills/design/engine/assets/scenes/stage.css +28 -0
- package/skills/design/engine/assets/scenes/stage.js +133 -0
- package/skills/design/engine/library/app.json +1327 -0
- package/skills/design/engine/library/directions.json +1078 -0
- package/skills/design/engine/library/heroes.json +1086 -0
- package/skills/design/engine/library/motions.json +92 -0
- package/skills/design/engine/library/palettes.json +1485 -0
- package/skills/design/engine/library/photos.json +7240 -0
- package/skills/design/engine/library/sections.json +4429 -0
- package/skills/design/engine/library/sites.json +8350 -0
- package/skills/design/engine/library/type.json +2024 -0
- package/skills/design/engine/references/app-screens.md +1199 -0
- package/skills/design/engine/references/colour.md +141 -0
- package/skills/design/engine/references/copy.md +177 -0
- package/skills/design/engine/references/critique.md +182 -0
- package/skills/design/engine/references/imagery.md +791 -0
- package/skills/design/engine/references/layout.md +292 -0
- package/skills/design/engine/references/motion.md +980 -0
- package/skills/design/engine/references/research.md +356 -0
- package/skills/design/engine/references/scenes.md +174 -0
- package/skills/design/engine/references/tells.md +115 -0
- package/skills/design/engine/references/type.md +170 -0
- package/skills/design/engine/scripts/assets.mjs +865 -0
- package/skills/design/engine/scripts/draw.mjs +584 -0
- package/skills/design/engine/scripts/inspect.mjs +303 -0
- package/skills/design/engine/scripts/lib/app-draw.mjs +203 -0
- package/skills/design/engine/scripts/lib/cdp.mjs +248 -0
- package/skills/design/engine/scripts/lib/probe.mjs +909 -0
- package/skills/design/engine/scripts/lib/rules.mjs +243 -0
- package/skills/design/engine/scripts/lint.mjs +119 -0
- package/skills/design/engine/scripts/scenes.mjs +124 -0
- package/skills/design/evals/cases.yaml +35 -46
- package/skills/design/references/design-md.md +49 -0
- package/skills/design/references/finish-review.md +46 -0
- package/skills/design/references/modes.md +51 -0
- package/skills/expo/SKILL.md +3 -4
- package/skills/marketing/references/landing-copy.md +2 -2
- package/skills/react/SKILL.md +2 -2
- package/skills/react-native/SKILL.md +3 -3
- package/skills/suggest/SKILL.md +12 -8
- package/skills/suggest/evals/cases.yaml +6 -0
- package/skills/swift-ios/SKILL.md +1 -1
- package/skills/ui-engineering/SKILL.md +3 -4
- package/skills/ui-engineering/evals/cases.yaml +2 -2
- package/skills/variant-explorer/SKILL.md +4 -4
- package/skills/variant-explorer/evals/cases.yaml +1 -1
- package/targets/clone-bootstrap.mjs +1 -1
- package/scripts/lib/design-identity.js +0 -117
- package/scripts/lib/starting-point.js +0 -314
- package/skills/design/evals/README.md +0 -53
- package/skills/design/references/ai-tells.md +0 -75
- package/skills/design/references/brand-grounding.md +0 -197
- package/skills/design/references/copywriting-frameworks.md +0 -138
- package/skills/design/references/design-systems.md +0 -49
- package/skills/design/references/inspiration-sources.md +0 -96
- package/skills/design/references/landing-anatomy-and-cro.md +0 -202
- package/skills/design/references/motion-and-interaction.md +0 -232
- package/skills/design/references/research-method.md +0 -154
- package/skills/design/references/signature-and-craft.md +0 -148
- package/skills/design/references/starting-point.md +0 -75
- package/skills/design/references/trends-2026.md +0 -80
- package/skills/design/references/visual-system.md +0 -237
- package/skills/design/scripts/verify.sh +0 -357
- package/skills/design-dna/PROMPT.md +0 -211
- package/skills/design-dna/SKILL.md +0 -162
- package/skills/design-dna/design-dna.schema.json +0 -230
- package/skills/design-dna/evals/README.md +0 -64
- package/skills/design-dna/evals/cases.yaml +0 -68
- package/skills/design-dna/references/method.md +0 -436
- package/skills/design-dna/scripts/emit.py +0 -164
- package/skills/design-dna/templates/PROMPT.md.tmpl +0 -42
- package/skills/design-dna/templates/SKILL.md.tmpl +0 -42
- package/skills/design-dna/templates/check.py.tmpl +0 -143
- package/skills/design-loop/SKILL.md +0 -160
- package/skills/design-loop/evals/README.md +0 -56
- package/skills/design-loop/evals/cases.yaml +0 -60
- package/skills/motion-craft/SKILL.md +0 -125
- package/skills/motion-craft/evals/cases.yaml +0 -38
- 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.
|