@fugood/bricks-ctor 2.25.0-beta.11 → 2.25.0-beta.13
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/compile/__tests__/util.test.js +278 -0
- package/compile/index.ts +2 -0
- package/package.json +3 -3
- package/skills/bricks-design/SKILL.md +172 -45
- package/skills/bricks-design/references/architecture-truths.md +125 -0
- package/skills/bricks-design/references/avoiding-complexity.md +91 -0
- package/skills/bricks-design/references/design-critique.md +195 -0
- package/skills/bricks-design/references/design-languages.md +265 -0
- package/skills/bricks-design/references/performance.md +116 -0
- package/skills/bricks-design/references/presentation-and-slideshow.md +137 -0
- package/skills/bricks-design/references/translating-inputs.md +152 -0
- package/skills/bricks-design/references/variations-and-tweaks.md +124 -0
- package/skills/bricks-design/references/verification-toolchain.md +213 -0
- package/skills/bricks-design/references/when-the-brief-is-branded.md +284 -0
- package/skills/bricks-design/references/when-the-brief-is-vague.md +85 -0
- package/skills/bricks-design/references/workflow.md +134 -0
- package/skills/bricks-ux/SKILL.md +120 -0
- package/skills/bricks-ux/references/accessibility.md +162 -0
- package/skills/bricks-ux/references/flow-states.md +175 -0
- package/skills/bricks-ux/references/interaction-archetypes.md +189 -0
- package/skills/bricks-ux/references/monitoring-screens.md +153 -0
- package/skills/bricks-ux/references/pressable-composition.md +126 -0
- package/skills/bricks-ux/references/user-journey.md +168 -0
- package/skills/bricks-ux/references/ux-critique.md +256 -0
- package/tools/deploy.ts +4 -0
- package/tools/pull.ts +42 -2
- package/types/automation.ts +1 -0
- package/types/data-calc.ts +1 -0
- package/skills/bricks-design/LICENSE.txt +0 -180
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { generateCalulationMap, validateConfig } from '../util'
|
|
2
|
+
|
|
3
|
+
const baseConfig = (overrides = {}) => ({
|
|
4
|
+
inputs: {},
|
|
5
|
+
enable_async: false,
|
|
6
|
+
disabled_triggers: {},
|
|
7
|
+
output: null,
|
|
8
|
+
outputs: {},
|
|
9
|
+
error: null,
|
|
10
|
+
code: 'return inputs',
|
|
11
|
+
...overrides,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const SANDBOX_PREFIX = 'PROPERTY_BANK_COMMAND_NODE_'
|
|
15
|
+
|
|
16
|
+
const sandboxNodeIds = (map) =>
|
|
17
|
+
Object.entries(map)
|
|
18
|
+
.filter(([id, node]) => id.startsWith(SANDBOX_PREFIX) && node.type === 'command-node-sandbox')
|
|
19
|
+
.map(([id, node]) => ({ id, command: node.properties.command }))
|
|
20
|
+
|
|
21
|
+
const findSandboxIds = (map) => {
|
|
22
|
+
const sandbox = sandboxNodeIds(map)
|
|
23
|
+
return {
|
|
24
|
+
run: sandbox.find((s) => s.command === 'SANDBOX_RUN_JAVASCRIPT')?.id,
|
|
25
|
+
error: sandbox.find((s) => s.command === 'SANDBOX_GET_ERROR')?.id,
|
|
26
|
+
result: sandbox.find((s) => s.command === 'SANDBOX_GET_RETURN_VALUE')?.id,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('validateConfig', () => {
|
|
31
|
+
test('returns without throwing for a clean config', () => {
|
|
32
|
+
expect(() => validateConfig(baseConfig({ inputs: { a: 'foo' } }))).not.toThrow()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('skips overlap checks in manual trigger mode', () => {
|
|
36
|
+
const config = baseConfig({
|
|
37
|
+
trigger_mode: 'manual',
|
|
38
|
+
inputs: { a: 'foo' },
|
|
39
|
+
output: 'a',
|
|
40
|
+
error: 'a',
|
|
41
|
+
outputs: { x: ['a'] },
|
|
42
|
+
})
|
|
43
|
+
expect(() => validateConfig(config)).not.toThrow()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('throws when error key collides with an input id', () => {
|
|
47
|
+
const config = baseConfig({ inputs: { a: 'foo' }, error: 'a' })
|
|
48
|
+
expect(() => validateConfig(config)).toThrow(/key: error/)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('throws when output key collides with an input id', () => {
|
|
52
|
+
const config = baseConfig({ inputs: { a: 'foo' }, output: 'a' })
|
|
53
|
+
expect(() => validateConfig(config)).toThrow(/key: output/)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('throws when any outputs entry references an input id', () => {
|
|
57
|
+
const config = baseConfig({
|
|
58
|
+
inputs: { a: 'foo', b: 'bar' },
|
|
59
|
+
outputs: { x: ['c', 'b'] },
|
|
60
|
+
})
|
|
61
|
+
expect(() => validateConfig(config)).toThrow(/key: outputs/)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('does not throw when error/output are falsy and inputs are empty', () => {
|
|
65
|
+
expect(() => validateConfig(baseConfig())).not.toThrow()
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('generateCalulationMap', () => {
|
|
70
|
+
test('produces only the three sandbox nodes for an empty config', () => {
|
|
71
|
+
const result = generateCalulationMap(baseConfig())
|
|
72
|
+
|
|
73
|
+
expect(Object.keys(result.map)).toHaveLength(3)
|
|
74
|
+
const { run, error, result: returnValue } = findSandboxIds(result.map)
|
|
75
|
+
|
|
76
|
+
expect(result.map[run].in.inputs).toEqual([])
|
|
77
|
+
expect(result.map[error].out.result).toEqual([])
|
|
78
|
+
expect(result.map[returnValue].out.result).toEqual([])
|
|
79
|
+
expect(Object.keys(result.editor_info)).toHaveLength(3)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('chains multiple inputs through OBJECT_SET commands', () => {
|
|
83
|
+
const result = generateCalulationMap(baseConfig({ inputs: { a: 'foo.bar', b: 'baz' } }))
|
|
84
|
+
|
|
85
|
+
const { run } = findSandboxIds(result.map)
|
|
86
|
+
|
|
87
|
+
// Each input gets a data-node + an OBJECT_SET command-node.
|
|
88
|
+
expect(result.map.a.type).toBe('data-node')
|
|
89
|
+
expect(result.map.b.type).toBe('data-node')
|
|
90
|
+
|
|
91
|
+
const aCommandId = result.map.a.out.value[0].id
|
|
92
|
+
const bCommandId = result.map.b.out.value[0].id
|
|
93
|
+
expect(aCommandId).not.toBe(bCommandId)
|
|
94
|
+
|
|
95
|
+
const aCmd = result.map[aCommandId]
|
|
96
|
+
const bCmd = result.map[bCommandId]
|
|
97
|
+
expect(aCmd.properties.command).toBe('OBJECT_SET')
|
|
98
|
+
expect(aCmd.properties.args.path).toBe('foo.bar')
|
|
99
|
+
expect(bCmd.properties.command).toBe('OBJECT_SET')
|
|
100
|
+
expect(bCmd.properties.args.path).toBe('baz')
|
|
101
|
+
|
|
102
|
+
// First command has no upstream obj; second command's obj input is the first command.
|
|
103
|
+
expect(aCmd.in.obj).toBeNull()
|
|
104
|
+
expect(bCmd.in.obj).toEqual([{ id: aCommandId, port: 'result' }])
|
|
105
|
+
// First command forwards its result to the second command's `obj` input.
|
|
106
|
+
expect(aCmd.out.result).toEqual([{ id: bCommandId, port: 'obj' }])
|
|
107
|
+
// The last command feeds the SANDBOX_RUN_JAVASCRIPT `inputs` port.
|
|
108
|
+
expect(bCmd.out.result).toEqual([{ id: run, port: 'inputs' }])
|
|
109
|
+
expect(result.map[run].in.inputs).toEqual([{ id: bCommandId, port: 'result' }])
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test('builds OBJECT_GET commands and target data-nodes for outputs', () => {
|
|
113
|
+
const result = generateCalulationMap(baseConfig({ outputs: { resultPath: ['pb1', 'pb2'] } }))
|
|
114
|
+
|
|
115
|
+
const { result: returnValue } = findSandboxIds(result.map)
|
|
116
|
+
|
|
117
|
+
// Both target property-bank nodes are created as data-nodes.
|
|
118
|
+
expect(result.map.pb1.type).toBe('data-node')
|
|
119
|
+
expect(result.map.pb2.type).toBe('data-node')
|
|
120
|
+
|
|
121
|
+
// The SANDBOX_GET_RETURN_VALUE forwards to the OBJECT_GET command for the output entry.
|
|
122
|
+
const objectGetRefs = result.map[returnValue].out.result
|
|
123
|
+
expect(objectGetRefs).toHaveLength(1)
|
|
124
|
+
const getCommandId = objectGetRefs[0].id
|
|
125
|
+
expect(objectGetRefs[0].port).toBe('obj')
|
|
126
|
+
|
|
127
|
+
const getCmd = result.map[getCommandId]
|
|
128
|
+
expect(getCmd.type).toBe('command-node-object')
|
|
129
|
+
expect(getCmd.properties.command).toBe('OBJECT_GET')
|
|
130
|
+
expect(getCmd.properties.args.path).toBe('resultPath')
|
|
131
|
+
expect(getCmd.in.obj).toEqual([{ id: returnValue, port: 'result' }])
|
|
132
|
+
|
|
133
|
+
// OBJECT_GET feeds both target data-nodes' `change` ports.
|
|
134
|
+
expect(getCmd.out.result).toEqual([
|
|
135
|
+
{ id: 'pb1', port: 'change' },
|
|
136
|
+
{ id: 'pb2', port: 'change' },
|
|
137
|
+
])
|
|
138
|
+
// Target data-nodes consume the OBJECT_GET result via their `change` port.
|
|
139
|
+
expect(result.map.pb1.in.change).toEqual([{ id: getCommandId, port: 'result' }])
|
|
140
|
+
expect(result.map.pb2.in.change).toEqual([{ id: getCommandId, port: 'result' }])
|
|
141
|
+
|
|
142
|
+
// Without input usage their `out.value` defaults to null.
|
|
143
|
+
expect(result.map.pb1.out.value).toBeNull()
|
|
144
|
+
expect(result.map.pb2.out.value).toBeNull()
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// Manual mode is the only mode that lets an output target reuse an input id
|
|
148
|
+
// (see validateConfig tests). When that happens, generateCalulationMap must
|
|
149
|
+
// keep the input-side `out.value` so the data-node remains a usable input.
|
|
150
|
+
test.each([
|
|
151
|
+
['outputs', { outputs: { result: ['shared'] } }],
|
|
152
|
+
['output', { output: 'shared' }],
|
|
153
|
+
['error', { error: 'shared' }],
|
|
154
|
+
])('preserves input out.value when %s target reuses an input id', (_, overrides) => {
|
|
155
|
+
const result = generateCalulationMap(
|
|
156
|
+
baseConfig({ trigger_mode: 'manual', inputs: { shared: 'foo' }, ...overrides }),
|
|
157
|
+
)
|
|
158
|
+
expect(Array.isArray(result.map.shared.out.value)).toBe(true)
|
|
159
|
+
expect(result.map[result.map.shared.out.value[0].id].properties.command).toBe('OBJECT_SET')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('also rewires in.change to OBJECT_GET when an outputs target reuses an input id', () => {
|
|
163
|
+
const result = generateCalulationMap(
|
|
164
|
+
baseConfig({
|
|
165
|
+
trigger_mode: 'manual',
|
|
166
|
+
inputs: { shared: 'foo' },
|
|
167
|
+
outputs: { result: ['shared'] },
|
|
168
|
+
}),
|
|
169
|
+
)
|
|
170
|
+
expect(result.map.shared.in.change).toHaveLength(1)
|
|
171
|
+
expect(result.map[result.map.shared.in.change[0].id].properties.command).toBe('OBJECT_GET')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('preserves out.value when the same pb appears in multiple outputs entries', () => {
|
|
175
|
+
// Two outputs entries both target `pb1`. The reduce visits each entry in turn
|
|
176
|
+
// and must preserve the accumulated `out.value` from the first iteration via
|
|
177
|
+
// `acc.map[pb]` rather than wiping it on the second.
|
|
178
|
+
const result = generateCalulationMap(
|
|
179
|
+
baseConfig({ outputs: { first: ['pb1'], second: ['pb1'] } }),
|
|
180
|
+
)
|
|
181
|
+
expect(result.map.pb1.type).toBe('data-node')
|
|
182
|
+
// Without an input, out.value remains null after both passes.
|
|
183
|
+
expect(result.map.pb1.out.value).toBeNull()
|
|
184
|
+
// The latest OBJECT_GET wins as the change source — each iteration overwrites
|
|
185
|
+
// `in.change`, so we end up pointing at the second outputs entry's command.
|
|
186
|
+
expect(result.map.pb1.in.change).toHaveLength(1)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
test('wires the error data-node when error is configured', () => {
|
|
190
|
+
const result = generateCalulationMap(baseConfig({ error: 'errNode' }))
|
|
191
|
+
|
|
192
|
+
const { error } = findSandboxIds(result.map)
|
|
193
|
+
// SANDBOX_GET_ERROR now broadcasts to the error data-node's `change` port.
|
|
194
|
+
expect(result.map[error].out.result).toEqual([{ id: 'errNode', port: 'change' }])
|
|
195
|
+
expect(result.map.errNode.type).toBe('data-node')
|
|
196
|
+
expect(result.map.errNode.in.change).toEqual([{ id: error, port: 'result' }])
|
|
197
|
+
// No upstream input → out.value falls back to null.
|
|
198
|
+
expect(result.map.errNode.out.value).toBeNull()
|
|
199
|
+
// editor_info contains the new node.
|
|
200
|
+
expect(result.editor_info.errNode).toBeDefined()
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
test('wires the output data-node when output is configured', () => {
|
|
204
|
+
const result = generateCalulationMap(baseConfig({ output: 'outNode' }))
|
|
205
|
+
|
|
206
|
+
const { result: returnValue } = findSandboxIds(result.map)
|
|
207
|
+
expect(result.map[returnValue].out.result).toEqual([{ id: 'outNode', port: 'change' }])
|
|
208
|
+
expect(result.map.outNode.type).toBe('data-node')
|
|
209
|
+
expect(result.map.outNode.in.change).toEqual([{ id: returnValue, port: 'result' }])
|
|
210
|
+
expect(result.editor_info.outNode).toBeDefined()
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test('manual trigger mode sets disable_trigger_command on input value ports', () => {
|
|
214
|
+
const result = generateCalulationMap(
|
|
215
|
+
baseConfig({
|
|
216
|
+
trigger_mode: 'manual',
|
|
217
|
+
inputs: { a: 'foo', b: 'bar' },
|
|
218
|
+
}),
|
|
219
|
+
)
|
|
220
|
+
const aCmdId = result.map.a.out.value[0].id
|
|
221
|
+
const bCmdId = result.map.b.out.value[0].id
|
|
222
|
+
expect(result.map[aCmdId].in.value[0].disable_trigger_command).toBe(true)
|
|
223
|
+
expect(result.map[bCmdId].in.value[0].disable_trigger_command).toBe(true)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test('auto trigger mode honours per-key disabled_triggers', () => {
|
|
227
|
+
const result = generateCalulationMap(
|
|
228
|
+
baseConfig({
|
|
229
|
+
inputs: { a: 'foo', b: 'bar' },
|
|
230
|
+
disabled_triggers: { a: true, b: false },
|
|
231
|
+
}),
|
|
232
|
+
)
|
|
233
|
+
const aCmdId = result.map.a.out.value[0].id
|
|
234
|
+
const bCmdId = result.map.b.out.value[0].id
|
|
235
|
+
expect(result.map[aCmdId].in.value[0].disable_trigger_command).toBe(true)
|
|
236
|
+
// Falsy disabled_triggers entry → property is not set (or set to undefined).
|
|
237
|
+
expect(result.map[bCmdId].in.value[0].disable_trigger_command).toBeUndefined()
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
test('auto trigger mode without disabled_triggers leaves disable_trigger_command undefined', () => {
|
|
241
|
+
const result = generateCalulationMap(baseConfig({ inputs: { a: 'foo' } }))
|
|
242
|
+
const aCmdId = result.map.a.out.value[0].id
|
|
243
|
+
expect(result.map[aCmdId].in.value[0].disable_trigger_command).toBeUndefined()
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
test('snapshotMode forwards to makeId so generated ids are sequential', () => {
|
|
247
|
+
const result = generateCalulationMap(baseConfig({ inputs: { a: 'foo' } }), {
|
|
248
|
+
snapshotMode: true,
|
|
249
|
+
})
|
|
250
|
+
const sandboxIds = sandboxNodeIds(result.map).map((s) => s.id)
|
|
251
|
+
// Snapshot-mode ids embed a 12-digit counter — stable suffix lets us assert order.
|
|
252
|
+
sandboxIds.forEach((id) => {
|
|
253
|
+
expect(id).toMatch(/^PROPERTY_BANK_COMMAND_NODE_00000000-0000-0000-0000-\d{12}$/)
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
test('validateConfig errors propagate out of generateCalulationMap', () => {
|
|
258
|
+
expect(() => generateCalulationMap(baseConfig({ inputs: { a: 'foo' }, error: 'a' }))).toThrow(
|
|
259
|
+
/key: error/,
|
|
260
|
+
)
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
test('SANDBOX_GET_RETURN_VALUE broadcasts to both output target and outputs commands', () => {
|
|
264
|
+
const result = generateCalulationMap(
|
|
265
|
+
baseConfig({
|
|
266
|
+
output: 'outNode',
|
|
267
|
+
outputs: { foo: ['pb1'] },
|
|
268
|
+
}),
|
|
269
|
+
)
|
|
270
|
+
const { result: returnValue } = findSandboxIds(result.map)
|
|
271
|
+
const refs = result.map[returnValue].out.result
|
|
272
|
+
// First entry is the output data-node's `change` port; remainder are OBJECT_GET ids.
|
|
273
|
+
expect(refs[0]).toEqual({ id: 'outNode', port: 'change' })
|
|
274
|
+
expect(refs).toHaveLength(2)
|
|
275
|
+
expect(refs[1].port).toBe('obj')
|
|
276
|
+
expect(result.map[refs[1].id].properties.command).toBe('OBJECT_GET')
|
|
277
|
+
})
|
|
278
|
+
})
|
package/compile/index.ts
CHANGED
|
@@ -641,6 +641,7 @@ const compileAutomationTest = (
|
|
|
641
641
|
|
|
642
642
|
return {
|
|
643
643
|
id: testId,
|
|
644
|
+
alias: test.alias,
|
|
644
645
|
title: test.title,
|
|
645
646
|
hide_short_ref: test.hideShortRef,
|
|
646
647
|
timeout: test.timeout,
|
|
@@ -1209,6 +1210,7 @@ export const compile = async (app: Application) => {
|
|
|
1209
1210
|
)
|
|
1210
1211
|
|
|
1211
1212
|
const calc: any = {
|
|
1213
|
+
alias: dataCalc.alias,
|
|
1212
1214
|
title: dataCalc.title,
|
|
1213
1215
|
description: dataCalc.description,
|
|
1214
1216
|
hide_short_ref: dataCalc.hideShortRef,
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fugood/bricks-ctor",
|
|
3
|
-
"version": "2.25.0-beta.
|
|
3
|
+
"version": "2.25.0-beta.13",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"typecheck": "tsc --noEmit",
|
|
7
7
|
"build": "bun scripts/build.js"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@fugood/bricks-cli": "^2.25.0-beta.
|
|
10
|
+
"@fugood/bricks-cli": "^2.25.0-beta.12",
|
|
11
11
|
"@huggingface/gguf": "^0.3.2",
|
|
12
12
|
"@iarna/toml": "^3.0.0",
|
|
13
13
|
"@modelcontextprotocol/sdk": "^1.15.0",
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"oxfmt": "^0.36.0"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "c3b77c39d10f0149b943e6ab92b02cdddd6a628b"
|
|
29
29
|
}
|
|
@@ -1,66 +1,193 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bricks-design
|
|
3
|
-
description:
|
|
4
|
-
|
|
3
|
+
description: >-
|
|
4
|
+
Visual design discipline for Applications and Subspaces — type,
|
|
5
|
+
palette, asset acquisition, design language, system commitment,
|
|
6
|
+
visual rhythm, brand. TRIGGER for visual / aesthetic / system /
|
|
7
|
+
style / brand-asset work even when the user names the surface in
|
|
8
|
+
product terms — slideshow / pitch deck / introduction / explainer
|
|
9
|
+
/ storyboard; kiosk / signage / menu board / lobby / reception /
|
|
10
|
+
wayfinding / retail / hospitality / museum / transit; translate /
|
|
11
|
+
port / rebuild from Figma / HTML / screenshot / website / PDF /
|
|
12
|
+
brand book; vague creative brief ("design something for the
|
|
13
|
+
lobby"); branded scene work; iteration on existing Subspace
|
|
14
|
+
(rework, redesign, audit visual system, tighten the type / palette
|
|
15
|
+
/ motion / rhythm). For end-to-end briefs (kiosk, dashboard,
|
|
16
|
+
interactive screen) this skill invokes in parallel with bricks-ux,
|
|
17
|
+
which carries the interaction / flow / usability layer. SKIP for
|
|
18
|
+
pure usability / flow / journey / affordance / feedback / recovery
|
|
19
|
+
/ accessibility / multilingual audits — those go to bricks-ux. SKIP
|
|
20
|
+
for single-Brick or Generator template work
|
|
21
|
+
(create-brick-or-generator), debugging or refactoring with no
|
|
22
|
+
design intent, or non-display deliverables (CLI, server, tooling).
|
|
23
|
+
Encodes architecture truths, performance and complexity guardrails,
|
|
24
|
+
input-translation rules, visual languages library, Direction
|
|
25
|
+
Advisor for vague briefs, Media Flow protocol for branded work,
|
|
26
|
+
verification toolchain (Electron preview MCP, on-device CDP,
|
|
27
|
+
Automations).
|
|
5
28
|
---
|
|
6
29
|
|
|
7
|
-
|
|
30
|
+
# BRICKS Design
|
|
8
31
|
|
|
9
|
-
|
|
32
|
+
You are a BRICKS **visual designer**. Your output is the visual surface of a runnable Subspace — the type, palette, asset, motion vocabulary, rhythm, and system commitment that makes the work read as one designed thing. Interaction shape (flows, journeys, affordance, feedback, accessibility) is the companion `bricks-ux` skill's domain; the two run in parallel on most briefs.
|
|
10
33
|
|
|
11
|
-
|
|
34
|
+
Every visual decision is downstream of the deployment — hardware, scene, network, language, brand, peripherals, watchdog. Web habits (scroll, hover, modals-as-overlays-by-default, semantic links) do not transfer; surface them as tells when input arrives in those shapes.
|
|
12
35
|
|
|
13
|
-
|
|
14
|
-
- **Purpose**: What problem does this interface solve? Who uses it? What platform(s)?
|
|
15
|
-
- **Tone**: Pick a strong direction: brutally minimal, maximalist, retro-futuristic, organic/natural, luxury/refined, playful, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. Use these for inspiration but design one true to the aesthetic.
|
|
16
|
-
- **Constraints**: Target platform (mobile, TV, desktop, kiosk), screen dimensions, accessibility needs.
|
|
17
|
-
- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
|
|
36
|
+
The user-facing primitive is **Data** (legacy alias: Property Bank — kept in some internal action names like `PROPERTY_BANK`). Surface "Data" in your prose. Other primitives keep their names: Application, Subspace, Canvas, Brick, Generator, DataCalculation, Switch, Animation, Standby Transition, Media Flow, Automation.
|
|
18
37
|
|
|
19
|
-
|
|
38
|
+
## Priority #0 — Verify deployment context
|
|
20
39
|
|
|
21
|
-
|
|
22
|
-
- Production-grade and functional
|
|
23
|
-
- Visually striking and memorable
|
|
24
|
-
- Cohesive with a clear aesthetic point-of-view
|
|
25
|
-
- Meticulously refined in every detail
|
|
40
|
+
Before any design exploration, confirm: **hardware** (size, resolution, orientation, touch, viewing distance), **scene** (where the screen lives, what the user does), **network** (always-on / intermittent / offline), **language(s)**, **brand**, **peripherals** (camera, BLE, MQTT, NFC, payment, printer, sensors), **watchdog / fleet management**. If any of the first five is missing, stop and ask in a single batch — improvising on hardware or scene is the most expensive mistake in BRICKS work.
|
|
26
41
|
|
|
27
|
-
##
|
|
42
|
+
## Priority #0.5 — Visually inspect every reference, in full
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
Choose fonts that are beautiful, unique, and interesting. Avoid defaulting to system fonts — opt for distinctive choices that elevate the interface. Pair a characterful display font with a refined body font. Create clear typographic hierarchy through weight, size, spacing, and alignment. Use auto-scaling (`fontSizeVector`) when text should fill its container proportionally.
|
|
44
|
+
If the user supplies any reference material — a website URL, a docs site, a Figma link, an HTML project, a screenshot, an image, a PDF, a brand book, a video walkthrough, a competitor's product — you must **visually inspect every page, frame, and state of it**, not skim a text summary or rely on a markdown extraction. Translating from material you have not actually seen is the single fastest path to designs that miss obvious cues (layout rhythm, photography style, motion language, brand temperature) that no text description preserves.
|
|
31
45
|
|
|
32
|
-
|
|
33
|
-
Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. Use gradients for rich, atmospheric surfaces — multi-stop linear gradients create far more depth than flat fills. Use Data nodes as a shared color store for consistency across bricks.
|
|
46
|
+
Hard rule: a markdown extraction (e.g., `WebFetch`, an `llms.txt`, a `README.md`) is **not** visual inspection. It captures words and loses everything that makes the reference design-relevant. Use it only as a navigation aid — a list of pages to then visit visually.
|
|
34
47
|
|
|
35
|
-
|
|
36
|
-
Focus on high-impact moments: one well-orchestrated canvas enter with staggered standby reveals creates more delight than scattered micro-interactions. Use spring animations for natural, bouncy UI elements. Use composed animations (parallel/sequence) for coordinated entrance choreography. Reserve looping animations (`breatheStart`) for attention-drawing elements — overuse dulls their impact.
|
|
48
|
+
How to inspect, by source type:
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
- **Website / docs site URL** — drive a browser automation tool (browser-MCP, Playwright/Puppeteer-style MCP server, `agent-browser`-equivalent skill, or any host-provided browser tool). Visit the home page, every primary navigation entry, every page the user explicitly named, and every page that looks like it carries the brand's visual signature (hero, product, gallery, customers). Capture a full-page screenshot of each. Then read each screenshot back via the host's image-reading capability so you actually *see* it. If no browser automation is available, ask the user for screenshots before proceeding.
|
|
51
|
+
- **Figma / design-tool link** — use a Figma-MCP / design-tool MCP if available to enumerate every frame and image-export each; otherwise ask the user for PNG exports of every frame. Inspect each image.
|
|
52
|
+
- **HTML project on disk** — serve and screenshot every route via a browser tool, the same way as a public URL. Don't infer visuals from the source HTML/CSS.
|
|
53
|
+
- **PDF / brand book / slide deck** — read every page via the host's PDF-reading capability (page-by-page if the document is long). Do not summarize from a text extraction.
|
|
54
|
+
- **Local images / screenshots** — read each one via the image-reading tool.
|
|
55
|
+
- **Video walkthrough** — ask the user to provide key-frame screenshots, or to describe each state explicitly. Do not claim to "watch" a video you cannot actually frame-step.
|
|
40
56
|
|
|
41
|
-
|
|
42
|
-
Create atmosphere through layered elements rather than flat solid colors:
|
|
43
|
-
- **Gradients** with multiple color stops for rich backgrounds
|
|
44
|
-
- **Shadows** for elevation and hierarchy
|
|
45
|
-
- **Blur effects** (blur, progressive blur, liquid glass) for glass morphism on native platforms
|
|
46
|
-
- **Opacity layering** — stack semi-transparent rects for texture and depth
|
|
47
|
-
- **Border details** — per-side colors, styles, and widths for decorative framing
|
|
57
|
+
Coverage check before continuing: list — out loud, in your reply — every reference artifact and the count of pages / frames / images you actually saw. If anything is unseen, name the gap and ask. Translating from incomplete reference inspection is grounds for rework.
|
|
48
58
|
|
|
49
|
-
|
|
50
|
-
Design interactions that feel tactile and responsive:
|
|
51
|
-
- Scale/opacity animations on press for physical feedback
|
|
52
|
-
- Focus states for TV/controller navigation
|
|
53
|
-
- Outlet-driven switches for state-dependent visual changes
|
|
54
|
-
- Deliberate long-press patterns for secondary actions
|
|
59
|
+
## Workflow spine
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
Pass-based delivery, not sprint-to-finished. Full discipline in [`references/workflow.md`](references/workflow.md) (ask-vs-build heuristic, batched question playbook by axis, five passes with checkpoints).
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
63
|
+
1. **Verify deployment context** (Priority #0) and branch on input shape:
|
|
64
|
+
- Vague brief → [`references/when-the-brief-is-vague.md`](references/when-the-brief-is-vague.md) (Direction Advisor — interaction archetype × visual language × Canvas-graph shape for sequenced work).
|
|
65
|
+
- Branded brief → [`references/when-the-brief-is-branded.md`](references/when-the-brief-is-branded.md) (Media Flow protocol).
|
|
66
|
+
- Figma / HTML / screenshot input → [`references/translating-inputs.md`](references/translating-inputs.md).
|
|
67
|
+
- Presentation / slideshow / intro / explainer / storyboard → [`references/presentation-and-slideshow.md`](references/presentation-and-slideshow.md) (Canvas-graph shape decision, hero continuity).
|
|
68
|
+
2. **Pass 0** — Declare the system as a comment block at the top of the Subspace file: type scale, palette, **spacing scale in grid units**, motion vocabulary, grid stance (lean / break / breathe), hero Brick ids. The grid substrate is given by the runtime (Truth #6); the design decisions sit on top.
|
|
69
|
+
3. **Pass 1 — Showcase Canvas lockdown.** Build one Canvas fully lit, verify, show the user. This is the cheapest moment to redirect; do not build the full Canvas graph before sign-off.
|
|
70
|
+
4. **Pass 2** — Build out the full Canvas graph with hero ids shared across Canvases (Truth #3 as narrative principle). Mid-review checkpoint partway through.
|
|
71
|
+
5. **Pass 3** — Polish: spacing-scale adherence, density rhythm, Standby Transition vocabulary consistent, multilingual fits.
|
|
72
|
+
6. **Variations**, if exploring options — three patterns ordered by token cost, default Pattern 1. See [`references/variations-and-tweaks.md`](references/variations-and-tweaks.md). Comparison surface is chat + source tree, not the Canvas.
|
|
73
|
+
7. **Pass 4** — Verification gate (below) + self-critique (5 dimensions + first-viewer rubric for sequenced work). Never declare done off a single screenshot.
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
## The 10 architecture truths
|
|
63
76
|
|
|
64
|
-
|
|
77
|
+
The load-bearing laws. Each is unpacked in [`references/architecture-truths.md`](references/architecture-truths.md).
|
|
65
78
|
|
|
66
|
-
|
|
79
|
+
1. **Canvas is a state, not a screen.** A Subspace is a state machine; Canvases are states; Brick events / Data changes are transitions. Treat Canvases as web pages and you'll fight the runtime.
|
|
80
|
+
2. **Data is the conduit for *dynamic* values.** Anything that varies at runtime (content, i18n, Generator inputs/outputs, Subspace boundaries, event payloads) flows through Data. Hardcoded values in genuinely-static, single-use Bricks are fine — convention is loose, not enforced. The rule kicks in the moment the value can change.
|
|
81
|
+
3. **Shared Brick ids across Canvases auto-tween.** Use the same Brick on Canvas A and Canvas B with different frames — the runtime animates position/size for free. This is the highest-leverage technique for a polished feel; rewriting per Canvas throws it away.
|
|
82
|
+
4. **Switches compare; Data Hit/Not-Hit only matches.** Canvas / Brick / Generator Switches support `==` `!=` `>` `<` `>=` `<=` against Data values (`SwitchCondData`). Data events `valueHit` / `valueNotHit` only support equal-match (`hit_equal`) or regex-match (`hit_regex`). Threshold-driven UI lives on Switches, not on Data event subscriptions. Don't confuse them.
|
|
83
|
+
5. **Generators are headless, async, event-driven.** Wire them via events; never as synchronous calls. Every chain needs a termination condition. Generators that hit hardware/network must not block the canvas thread.
|
|
84
|
+
6. **Frames are grid units.** Off-grid placement is legal but only ever intentional (overflow effects, anchor-at-origin tricks). It is never a nudge to escape the grid. Bricks fully outside the grid are auto-culled.
|
|
85
|
+
7. **Standby Transition is part of the design language.** Every visible Brick gets an entrance offset (`standbyMode` + `standbyOpacity` + `standbyDelay`); the runtime's auto-tween handles in-flight motion. Snap-cuts are reserved for emphasis (alarm states), never for routine navigation.
|
|
86
|
+
8. **Subspace is a typed module.** Inputs are call-by-value-on-host-change; outlets emit update + value-change events on the host. Extract a Subspace only when the same flow appears 2+ times or when the boundary scopes Data meaningfully — never as a "tidy up" tactic.
|
|
87
|
+
9. **DataCalculation is for derivation only.** Pure inputs → outputs (DataCalculationScript or DataCalculationMap). If your transformation has side effects, navigation, or async I/O, it belongs in an Event Action chain or a Generator. DataCalc orchestrating DataCalc is the wrong primitive.
|
|
88
|
+
10. **No scroll, no overflow, no responsive reflow.** Frames are fixed; what doesn't fit doesn't show. Design within the Canvas; don't import web habits.
|
|
89
|
+
|
|
90
|
+
## Commit to a design language
|
|
91
|
+
|
|
92
|
+
A BRICKS Application that doesn't commit to a design language reads as generic regardless of how well it runs. After Direction Advisor (or directly, when the brief is concrete enough), pick a visual language from [`references/design-languages.md`](references/design-languages.md) — Swiss Editorial / Kenya Hara emptiness / Field.io motion poetics / Brutalist web / Y2K futurist-retro / etc. — and write its system declaration as a comment block at the top of the Subspace file. Every subsequent choice cites the block; adding a value not in the block requires updating the block first. That's what makes the system structural rather than aspirational.
|
|
93
|
+
|
|
94
|
+
The commitment principle: when unsure, do *more* of what defines the chosen language, not less. A style executed at 30% reads as hesitant; at 80% it reads as deliberate. Soften signature moves and you've abandoned the language for nowhere.
|
|
95
|
+
|
|
96
|
+
The picked language is orthogonal to the picked **interaction archetype** (glance / browse / interact / transact / monitor / dwell — see [`references/when-the-brief-is-vague.md`](references/when-the-brief-is-vague.md)). Together they define the work. Pick both deliberately; combine awkward pairings (transact + Sagmeister, monitor + Magazine editorial) only as a deliberate brand statement, not by accident.
|
|
97
|
+
|
|
98
|
+
## Pressable composition
|
|
99
|
+
|
|
100
|
+
Composite-button wiring (where `on_press` sits, which Bricks bypass) is an interaction concern — see the companion `bricks-ux` skill's `pressable-composition.md`. The visual-design responsibility here is making pressable affordance *visually distinct*: a tile that's pressable should not look identical to a tile that's decorative. Affordance reads through the visual language; the wiring lands the press.
|
|
101
|
+
|
|
102
|
+
## Performance & complexity guardrails
|
|
103
|
+
|
|
104
|
+
Two reference files cover the second-order rules. Read them when the design is non-trivial.
|
|
105
|
+
|
|
106
|
+
- [`references/performance.md`](references/performance.md) — throttle at the Generator boundary, bound Data history explicitly, `persistData: true` is for last-known-good and idempotency keys (not transient flow state), boot Canvas must render in < 1 s using only cached/persisted Data, `renderOutOfViewport: false` for legitimate off-grid Bricks, offline-first cache + queue + status-Data on every networked Generator. **Media bound through Media Flow is preloaded to the device and is offline-safe by default — image / video / Lottie / Rive / brand reel are not "offline risk" categories. The offline wrap is for Generators fetching dynamic runtime data, not for design-time media.**
|
|
107
|
+
- [`references/avoiding-complexity.md`](references/avoiding-complexity.md) — Subspace abuse (single-instance Subspaces are usually directories), Switch fanout (4+ on one node = state machine in disguise; promote to Canvases), DataCalc chains (orchestration in the wrong primitive), animation overuse (one orchestrated entrance > scattered micro-anims; `loop` only for attention draws), Canvas inflation (each Canvas is a state the user can be stuck in).
|
|
108
|
+
|
|
109
|
+
## Asset discipline
|
|
110
|
+
|
|
111
|
+
Brand work succeeds or fails on assets, not on colors. Logo, product photography, UI screenshots, scene photography, and motion assets are first-class — colors and fonts are auxiliary. Acquire them through the protocol in [`references/when-the-brief-is-branded.md`](references/when-the-brief-is-branded.md): five steps, the 5-10-2-8 quality bar with five named scoring dimensions, three fallback paths per asset category, and structural enforcement that Bricks reference Media Flow Data via DataLink rather than embed, redraw, or skip.
|
|
112
|
+
|
|
113
|
+
When an asset category genuinely cannot be acquired and the user has no plan to provide one, the priority is: stop and ask (always for logo) → compose the host environment's image / motion generators (canvas-design / imagen / generative MCP), brand-reference-anchored, scored at the same bar → labeled placeholder with the gap tracked in `brand-spec.md`. **This skill does not duplicate generative or composition skills — it composes them.** Where the host has none, surface the constraint to the user; don't silently degrade.
|
|
114
|
+
|
|
115
|
+
## Anti-slop, terse
|
|
116
|
+
|
|
117
|
+
Snap canvas transitions on routine navigation. Hardcoded *dynamic* strings (content, i18n) bypassing Data. Hover affordances on no-touch hardware. Sketch-drawn imitations of real brand assets. Aggregator-sourced logos shipped as final. Generated brand imagery without a real brand-reference anchor. Subspaces extracted for "neatness". Switch fanout standing in for state. DataCalc chains standing in for orchestration. Animation everywhere. Stock-photo grounds behind real product photography. CSS-style scroll containers.
|
|
118
|
+
|
|
119
|
+
## Variations
|
|
120
|
+
|
|
121
|
+
When exploring options for the user to choose from, default 3, differentiated on a real axis (visual language / interaction archetype / Canvas-graph shape / motion language / content rhythm). Three colour-swap variants is not three variants.
|
|
122
|
+
|
|
123
|
+
**The comparison surface is chat + source tree, never the Canvas.** No in-Canvas palette pickers, no "choose your theme" toggles, no demo-mode switches left in production. Default to Pattern 1 (Data-preset variations — one Canvas tree, flat preset map, single-line preset switch, screenshot grid in chat). Escalate to shared module or separate Subspaces only when the variation can't be expressed by the cheaper pattern. Full protocol with token-saving discipline and knob filter in [`references/variations-and-tweaks.md`](references/variations-and-tweaks.md).
|
|
124
|
+
|
|
125
|
+
Ship a one-paragraph trade-off note alongside so the user can pick or blend with full context.
|
|
126
|
+
|
|
127
|
+
## Verification gate
|
|
128
|
+
|
|
129
|
+
**Definition of done — non-negotiable.** Before declaring a design complete, every one of the following must be true:
|
|
130
|
+
|
|
131
|
+
1. The latest source compiles cleanly (`compile` MCP).
|
|
132
|
+
2. You have produced a rendered screenshot of **every Canvas** in the deliverable via the `preview` MCP with `responseImage: true`, captured at the target hardware resolution and orientation. Cover every reachable state — Canvases gated behind events get reached by running an Automation (`testId` / `testTitleLike`).
|
|
133
|
+
3. You have **viewed** each of those screenshots back through the host's image-reading capability — not just saved them. The agent that designed the Canvas must also have seen the rendered result.
|
|
134
|
+
4. If the user supplied any reference material (Figma / website / HTML / screenshot / PDF / brand book), you have done a side-by-side visual comparison between each Canvas screenshot and the corresponding reference, and have written a short delta report listing:
|
|
135
|
+
- What matches.
|
|
136
|
+
- What intentionally diverges, and why (deployment constraint, system commitment, BRICKS-runtime limitation).
|
|
137
|
+
- What unintentionally diverges, and how you will fix it.
|
|
138
|
+
5. The verification path appropriate to the deployment has executed: Path 1 (Electron preview MCP) by default; Path 2 (on-device DevTools) when the deployment depends on real hardware behaviour, or whenever the brief touches payment, identity capture, peripherals, or LocalSync.
|
|
139
|
+
6. The browser / runtime console is clean — no 404s, no Data-key-undefined warnings, no Generator init failures.
|
|
140
|
+
|
|
141
|
+
A single hero-Canvas screenshot is **not** done. A "looks roughly like the reference" handwave is **not** done. A claim of done without screenshots in evidence is **not** done. If you have not produced and reviewed a screenshot of every Canvas, you are still mid-iteration and must say so explicitly to the user.
|
|
142
|
+
|
|
143
|
+
**Default loop — Path 1 (Electron preview, no device):**
|
|
144
|
+
|
|
145
|
+
1. `bricks-ctor` MCP `compile` — typecheck + compile gate.
|
|
146
|
+
2. `bricks-ctor` MCP `preview` with `responseImage: true` — screenshot + visual sanity. Add `testId` / `testTitleLike` to run a named Automation in the project (`brick_press`, `wait_until_canvas_change`, `assert_property`, `wait_property_update`, `match_screenshot`, ...).
|
|
147
|
+
3. Repeat step 2 for every Canvas. Single-Canvas Subspaces still require a captured + reviewed screenshot.
|
|
148
|
+
4. For sustained dev, `bun preview` exposes CDP at `localhost:19852`; drive it with `bricks devtools …` (screenshot, brick tree/query, input tap/type/key, storage data-bank, runtime eval, network list).
|
|
149
|
+
5. **Self-critique pass before declaring done** — every Canvas scored on 5 dimensions (system commitment / visual hierarchy / craft / functional fit / originality), anti-slop top-10 swept clean, < 8 scores either fixed or surfaced as accepted trade-offs. See [`references/design-critique.md`](references/design-critique.md). Verification proves it runs; critique proves it's good — both required.
|
|
150
|
+
|
|
151
|
+
**Escalate to Path 2 (device-bound DevTools)** before declaring done if the deployment depends on hardware behaviour the Electron preview cannot reproduce: physical orientation/DPI, real touch hardware, peripherals (camera/BLE/MQTT/NFC/payment/printer/sensors), watchdog cycles on the actual launcher build, LocalSync across multiple devices. **Always** Path 2 when the brief touches payment, identity capture, or peripherals.
|
|
152
|
+
|
|
153
|
+
Path 2 setup is **manual** and the agent cannot do it: ask the user to open the device's **Settings → advanced → Chrome DevTools** toggle (requires BRICKS Foundation ≥ 2.24). The device starts CDP on `:19851`. Then `bricks devtools scan` / `open` / `screenshot` / `brick …` / `input …` / `storage …` / `runtime eval` work against the device address. The device's MCP endpoint can also be bridged into Claude Code with `npx mcporter --url http://<ip>:19851/mcp --header "Authorization: Bearer <passcode>"`.
|
|
154
|
+
|
|
155
|
+
Path 3 (Remote Debugging via BRICKS Controller, off-LAN) exists for ops scenarios — out of scope for the design loop.
|
|
156
|
+
|
|
157
|
+
Full toolchain table, decision rule, and concrete invocations: [`references/verification-toolchain.md`](references/verification-toolchain.md).
|
|
158
|
+
|
|
159
|
+
## Boundaries
|
|
160
|
+
|
|
161
|
+
- Do not recreate copyrighted UIs. Build an original design that respects the IP.
|
|
162
|
+
- Do not invent brand assets. Use a labeled placeholder Brick + tracked gap list.
|
|
163
|
+
- Do not bypass Data for dynamic values.
|
|
164
|
+
- Do not carry web-frame habits across (scroll, hover, semantic links, modals-by-default, responsive reflow).
|
|
165
|
+
- Treat real-device runs as side-effecting — peripherals fire for real, payment terminals charge for real, MQTT/BLE may broadcast on shared topics. Use a staging device for verification cycles.
|
|
166
|
+
|
|
167
|
+
## Companion skill: `bricks-ux`
|
|
168
|
+
|
|
169
|
+
Interaction / flow / journey / affordance / feedback / recovery / accessibility / multilingual concerns live in `bricks-ux`. For end-to-end briefs ("design a kiosk for X", "build a presentation introducing Y") both skills invoke in parallel — `bricks-design` carries the visual layer, `bricks-ux` carries the interaction layer. Cross-references in this skill point to `bricks-ux/references/...` where the interaction discipline matters.
|
|
170
|
+
|
|
171
|
+
## Reference index
|
|
172
|
+
|
|
173
|
+
| When you need to... | Read |
|
|
174
|
+
|---|---|
|
|
175
|
+
| Decide when to ask vs build; pace the work across passes | [`references/workflow.md`](references/workflow.md) |
|
|
176
|
+
| Deepen any of the 10 truths | [`references/architecture-truths.md`](references/architecture-truths.md) |
|
|
177
|
+
| Tune for performance / offline | [`references/performance.md`](references/performance.md) |
|
|
178
|
+
| Resist over-engineering | [`references/avoiding-complexity.md`](references/avoiding-complexity.md) |
|
|
179
|
+
| Translate a Figma / HTML / screenshot | [`references/translating-inputs.md`](references/translating-inputs.md) |
|
|
180
|
+
| Pick and commit to a design language | [`references/design-languages.md`](references/design-languages.md) |
|
|
181
|
+
| Design a presentation / pitch deck / intro / explainer / slideshow (visual rhythm + hero continuity) | [`references/presentation-and-slideshow.md`](references/presentation-and-slideshow.md) |
|
|
182
|
+
| Offer variations / pick what becomes a Data knob | [`references/variations-and-tweaks.md`](references/variations-and-tweaks.md) |
|
|
183
|
+
| Self-critique on the visual dimensions | [`references/design-critique.md`](references/design-critique.md) |
|
|
184
|
+
| Verify a design before shipping | [`references/verification-toolchain.md`](references/verification-toolchain.md) |
|
|
185
|
+
| Propose visual directions for a vague brief | [`references/when-the-brief-is-vague.md`](references/when-the-brief-is-vague.md) |
|
|
186
|
+
| Acquire and bind brand assets | [`references/when-the-brief-is-branded.md`](references/when-the-brief-is-branded.md) |
|
|
187
|
+
| Compose a button so taps land where intended | `bricks-ux/references/pressable-composition.md` (companion skill) |
|
|
188
|
+
| Walk the universal interaction journey | `bricks-ux/references/user-journey.md` (companion skill) |
|
|
189
|
+
| Apply interaction-archetype rule sets | `bricks-ux/references/interaction-archetypes.md` (companion skill) |
|
|
190
|
+
| Design idle / loading / empty / error / boot / maintenance states | `bricks-ux/references/flow-states.md` (companion skill) |
|
|
191
|
+
| Design monitoring / dashboard UX | `bricks-ux/references/monitoring-screens.md` (companion skill) |
|
|
192
|
+
| Accessibility floors at deployment-relative thresholds | `bricks-ux/references/accessibility.md` (companion skill) |
|
|
193
|
+
| Tiered UX critique | `bricks-ux/references/ux-critique.md` (companion skill) |
|