@fugood/bricks-ctor 2.25.0-beta.12 → 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.
Files changed (27) hide show
  1. package/compile/index.ts +2 -0
  2. package/package.json +2 -2
  3. package/skills/bricks-design/SKILL.md +172 -45
  4. package/skills/bricks-design/references/architecture-truths.md +125 -0
  5. package/skills/bricks-design/references/avoiding-complexity.md +91 -0
  6. package/skills/bricks-design/references/design-critique.md +195 -0
  7. package/skills/bricks-design/references/design-languages.md +265 -0
  8. package/skills/bricks-design/references/performance.md +116 -0
  9. package/skills/bricks-design/references/presentation-and-slideshow.md +137 -0
  10. package/skills/bricks-design/references/translating-inputs.md +152 -0
  11. package/skills/bricks-design/references/variations-and-tweaks.md +124 -0
  12. package/skills/bricks-design/references/verification-toolchain.md +213 -0
  13. package/skills/bricks-design/references/when-the-brief-is-branded.md +284 -0
  14. package/skills/bricks-design/references/when-the-brief-is-vague.md +85 -0
  15. package/skills/bricks-design/references/workflow.md +134 -0
  16. package/skills/bricks-ux/SKILL.md +120 -0
  17. package/skills/bricks-ux/references/accessibility.md +162 -0
  18. package/skills/bricks-ux/references/flow-states.md +175 -0
  19. package/skills/bricks-ux/references/interaction-archetypes.md +189 -0
  20. package/skills/bricks-ux/references/monitoring-screens.md +153 -0
  21. package/skills/bricks-ux/references/pressable-composition.md +126 -0
  22. package/skills/bricks-ux/references/user-journey.md +168 -0
  23. package/skills/bricks-ux/references/ux-critique.md +256 -0
  24. package/tools/pull.ts +42 -2
  25. package/types/automation.ts +1 -0
  26. package/types/data-calc.ts +1 -0
  27. package/skills/bricks-design/LICENSE.txt +0 -180
@@ -0,0 +1,153 @@
1
+ # Monitoring Screens
2
+
3
+ A monitoring screen is a designed reading surface, not a designed flow. The user — operator, controller, observer — is not driving an interaction; they are absorbing state. The discipline is entirely about what they *see* and *trust*, not what they *do*.
4
+
5
+ This file is the UX layer. Runtime semantics (Generators, Data event subscriptions, Switch thresholds) live in `bricks-design/architecture-truths.md` — particularly Truth #4 (Switches compare, Data Hit/Not-Hit only matches) and Truth #5 (Generators are headless / async / event-driven). Use them as the substrate. The design question is *what the screen shows about the state of the world*.
6
+
7
+ ## The three states a viewer reads
8
+
9
+ A monitor screen is always in one of three reading-states. Designing for one without the others produces a screen that works in calm and fails under stress (or vice versa).
10
+
11
+ ### Calm — scanning for the absence of trouble
12
+
13
+ The default state. Nothing requires attention. The viewer's job is reading at a glance and confirming "still nominal." This state is on the screen 95% of the time.
14
+
15
+ **Design discipline:**
16
+
17
+ - **Readable and forgettable.** The viewer should be able to absorb the screen in one peripheral-vision pass, look away, and feel confident nothing changed. Visual rhythm should be calm, predictable, with no demand-attention motion.
18
+ - **Hierarchy by role, not by activity.** The most-important-thing-to-monitor stays in the same place even when it's calm. Operators learn the screen by location; shifting layout based on which metric is most active destroys that mental model.
19
+ - **Subtle motion is permitted.** A sweep-second hand, a gentle pulse on the "live" indicator, a slow re-flow of a chart. The point is to confirm the screen is alive, not to draw the eye. `Animation` `runType: 'loop'` is legitimate here precisely for "the screen is breathing" signalling.
20
+
21
+ **Failure modes:**
22
+
23
+ - Calm-state screen so visually busy that the viewer can't tell normal from abnormal at a glance.
24
+ - Calm with no "alive" signal — the operator can't tell if the screen is frozen or actually displaying current state.
25
+ - Hierarchy that shifts based on data values — the top-of-screen item swaps because of recent activity, and the operator's eye-anchor is gone.
26
+
27
+ ### Detect — change worth noticing
28
+
29
+ Something shifted. It isn't urgent, but it's worth the viewer's attention. A sensor crossed a soft threshold, a counter incremented, a new item entered the queue, a node went from 99.9% to 99.3% uptime.
30
+
31
+ **Design discipline:**
32
+
33
+ - **Show, don't ask.** The change should be detectable peripherally — a colour shift, a soft pulse on the affected item, a slow scale-up. The viewer can catch it without breaking focus from other things.
34
+ - **Local, not global.** The change is localised to the affected Brick / region; the rest of the screen stays calm. Global re-styling of the whole Canvas for a single sub-system change is over-reaction.
35
+ - **Time-bounded acknowledgement.** A detect-state visual should hold for long enough to be noticed (say 5–15 seconds), then quietly subside or escalate, depending on what happened next.
36
+ - **Hero continuity.** Truth #3 — the chrome around the changing element does not also re-render. The eye lands on what moved.
37
+
38
+ **Failure modes:**
39
+
40
+ - Detect-state styled identically to calm — the viewer can't see anything changed.
41
+ - Detect-state escalates immediately into demand styling (loud, motion-heavy, audible) — false alarm fatigue. The viewer learns to ignore "alarms" that are really just minor changes.
42
+ - Detect-state persists indefinitely — small changes from yesterday still highlighted on today's screen; visual debt accumulates; nothing reads as fresh.
43
+
44
+ ### Demand — compel response
45
+
46
+ A threshold breach. Something is wrong; the operator needs to act. This is the only state where the screen is allowed to *interrupt* the viewer.
47
+
48
+ **Design discipline:**
49
+
50
+ - **Unambiguous shift in register.** A clear visual rupture from calm — bold colour, accelerated motion, on-screen size, possible audio cue. The viewer's eye lands within a second.
51
+ - **Action-pathing visible.** If the operator is expected to do something (acknowledge, dispatch, escalate), the action is on-screen and reachable. If they're expected to look elsewhere (radio, terminal), the screen says so.
52
+ - **No false demand.** Demand state is earned by genuine threshold breaches; not by every-blue-moon variance. Demand-fatigue is the most expensive failure mode of a monitoring deployment — operators learn to dismiss real alarms because there were too many false ones.
53
+ - **Persistent until cleared.** Demand state holds until the underlying condition resolves *or* the operator explicitly acknowledges. It does not time out on its own.
54
+ - **Severity tiers** if the deployment has more than one demand level (warning / critical / emergency). Each tier reads distinctly; an operator under stress must be able to triage in one look.
55
+
56
+ **Failure modes:**
57
+
58
+ - Demand state styled subtly — operator misses it because it doesn't stand out.
59
+ - Multiple concurrent demand states styled identically — no priority order; the operator picks one arbitrarily.
60
+ - Demand state auto-clears after a timer — the underlying condition is still active; the screen has gone silent on it; the operator believes things are fine.
61
+ - Demand state uses motion that overwhelms — every demand item flashes; the screen becomes unreadable.
62
+
63
+ ## The transitions between states are designed
64
+
65
+ Calm → Detect → Demand → Calm is not a state machine the runtime hands you. It's a designed UX transition the agent commits to. Shared Brick auto-tween (Truth #3) carries the affected item across the transition; the chrome around it stays.
66
+
67
+ - **Calm → Detect.** A subtle promotion. The affected Brick scales by 5–10%, gains a soft colour overlay, gets a subtle Standby Transition pulse. The eye is invited, not yanked.
68
+ - **Detect → Demand.** A clear escalation. The affected Brick gains strong styling — a contrasting outline, a faster pulse, a colour shift to alarm tone — and possibly promotes in screen hierarchy (becomes larger, moves to a dedicated alarm region).
69
+ - **Demand → Calm.** The release is also designed. Operator acknowledges or condition resolves; the affected Brick returns to its calm pose with a Standby Transition. The transition reads as relief, not as the screen forgetting.
70
+
71
+ Each transition is a Switch on a state Data with thresholds (Truth #4). Each step in the transition has its own Brick configuration. The agent commits to the visual story across the four states, not just the calm baseline.
72
+
73
+ ## Stale-data trust principle
74
+
75
+ **Stale data is worse than no data.** If the viewer cannot tell whether what they're seeing is current, they will trust what they see and act on it. Staleness must be *visibly declared* — not as fine-print, as a visible state change.
76
+
77
+ **Design discipline:**
78
+
79
+ - Every live-data Brick should have a visible "data age" or "last update" indicator, or a Switch-driven visual state for staleness. Background colour shift, opacity drop, "STALE" badge, or full handover to a "feed is down" Canvas if all sources are stale.
80
+ - The threshold for "stale" is a design decision per source. Sub-second feeds (sensor stream) are stale after a few seconds. Minute-cadence feeds (weather, news) tolerate longer. Per-source thresholds belong in Data, not hardcoded everywhere.
81
+ - A feed that loses its source entirely must not silently freeze on its last value — the screen must visibly transition to a no-data state. The operator can then route around it.
82
+
83
+ **Failure modes:**
84
+
85
+ - No staleness indicator at all — viewer trusts last-known-value forever.
86
+ - Staleness indicator at 8pt in the corner — invisible to peripheral vision.
87
+ - Stale value styled identically to current value — operator looks at "23°C" and acts on it; the reading is from 4 hours ago.
88
+ - "Last update" shown but in a format the viewer must parse — "15:42:03" with no relative time framing. Relative ("2 minutes ago") beats absolute when "is this current" is the question.
89
+
90
+ ## Show vs ask — a design decision
91
+
92
+ Some monitor screens *show* — they display state, leave action to operator judgement, never suggest. Air traffic control. Power-grid status.
93
+
94
+ Others *show and ask* — they display state and surface protocol actions at the right moment. Hospital nurse stations. Building security. A "page nurse" button appears next to a patient whose vitals shifted; an "acknowledge alarm" button appears with a fire-system trigger.
95
+
96
+ **Decide which it is per deployment, not per Canvas.** A monitor that sometimes asks and sometimes only shows confuses the operator about what they should be doing.
97
+
98
+ Once decided:
99
+
100
+ - **Show-only** monitors should not have action buttons at all. Any "do something" lives in a separate system (operator radio, dedicated terminal). The monitor reads as documentation.
101
+ - **Show-and-ask** monitors integrate the action affordance into the alarm hierarchy. The action affordance follows the 7-step journey (see [`user-journey.md`](user-journey.md)) — affordance, instruction, feedback on press, in-flight visibility, continuation, recovery, closure. The monitor returns to calm only after the operator's action is registered and the underlying condition clears.
102
+
103
+ ## Hierarchy under stress
104
+
105
+ When multiple things demand attention at once — three alarms, two warnings, a stale feed — the screen must already have a priority order designed.
106
+
107
+ **Design discipline:**
108
+
109
+ - **Sort by severity, then by recency.** Critical alarms above warnings above advisories; within tier, newest at top. Operators learn the order; reading is faster.
110
+ - **Don't dilute.** Two simultaneous critical alarms don't both shrink to fit; the second one takes a designed "stacked" slot rather than reducing the first.
111
+ - **Aggregate count visible.** When more demand states exist than the screen surfaces in detail, an "N more" indicator preserves operator awareness of the full picture.
112
+ - **The calm Bricks recede.** Under stress, the rest of the screen quietens — backgrounds desaturate, non-alarm items reduce opacity. The eye is led to the demand region.
113
+
114
+ **Failure modes:**
115
+
116
+ - All concurrent alarms styled identically — operator picks one arbitrarily, may not hit the most critical.
117
+ - The most recent alarm overwrites the older one — older urgent alarms vanish from view.
118
+ - Screen layout shifts under stress — the chrome moves; the operator's spatial mental model breaks.
119
+
120
+ ## Color is necessary but never sufficient
121
+
122
+ Critical / warning / normal must be encoded redundantly — colour PLUS shape, position, weight, or text. Operators may be colour-blind; viewing conditions may be unkind to colour (sun glare on screen, monochrome failover, photocopy of the screen sent to a stakeholder, viewing through a tinted shield).
123
+
124
+ See [`accessibility.md`](accessibility.md) for the universal principle. For monitor screens specifically:
125
+
126
+ - Critical alarms get a distinctive *shape* (diamond, hexagon, doubled border) on top of colour.
127
+ - Severity indicated by *position* (top-of-list, dedicated alarm strip) as well as colour.
128
+ - Numeric values that exceed threshold get a *prefix or sign* (▲, !, [HIGH]) — not just a colour change.
129
+
130
+ ## Refresh cadence is a UX decision
131
+
132
+ How fast data updates is a UX decision before it's a wiring decision. A power-grid status board updating every 100ms reads as nervous; the same data refreshing every 5 seconds with a clear "live" pulse reads as in-control.
133
+
134
+ **Design discipline:**
135
+
136
+ - Pick a refresh cadence per source that matches how the operator should be reading it. Cadence is about *the reading experience*, not about whether the data exists.
137
+ - Visible animation of updates (a value tweens from 23.4 to 23.7 over 400ms) reads calmer than snap updates. Use it for ongoing readings, not for state transitions (those are snaps).
138
+ - For cadence < 1 second, batch the perceived update — even if the underlying stream is 10Hz, the on-screen change rolls forward in human-readable beats.
139
+ - For cadence > 30 seconds, the absence of motion can read as broken. Add a calm "alive" indicator that ticks even when data hasn't changed.
140
+
141
+ ## How to use this file
142
+
143
+ Before designing a monitoring screen:
144
+
145
+ 1. **Confirm Priority #0** — who is the operator, what are they reading, under what conditions, what's the cost of missing a state change.
146
+ 2. **Pick the show / show-and-ask posture.** Don't decide later; it shapes everything else.
147
+ 3. **Design all three reading-states** (calm / detect / demand) at the start, not just the calm one. Sketch the demand-state Canvas before the calm baseline lands.
148
+ 4. **Design the transitions** between states using shared Brick auto-tween (Truth #3) for affected items and Standby Transitions for the moment of change.
149
+ 5. **Design stale-data state per source.** No source is allowed to silently freeze.
150
+ 6. **Stress-test the hierarchy.** Stack 3 concurrent demand states in design preview; verify priority reads correctly.
151
+ 7. **Verify redundant encoding.** Take a monochrome screenshot; severity tiers should still be distinguishable.
152
+
153
+ A monitor screen that passes calm-state critique but hasn't been designed for demand is half a deliverable.
@@ -0,0 +1,126 @@
1
+ # Pressable Composition
2
+
3
+ The composition decision and the wiring decision are the same decision. Affordance — *can I press this?* — is a UX signal the user reads off the screen. Pressable configuration is how that signal lands at the runtime. Get the configuration wrong and the screen lies to the user: a tile looks pressable but isn't, or is pressable but only on certain pixels. Either way, the user fights the design.
4
+
5
+ This file covers the configuration side. For the affordance discipline above it (when a thing should look pressable in the first place, how it joins the 7-step journey), see [`user-journey.md`](user-journey.md) step 1 and the relevant archetype rule set in [`interaction-archetypes.md`](interaction-archetypes.md).
6
+
7
+ The single principle: **every composite (multi-element) button needs a deliberate `on_press` decision per Brick, with `pressable` set explicitly wherever press chains layer.** The runtime auto-bypasses safe cases (see below), so the trivial wrapper case works without ceremony — but the design intent should still be readable from the configuration when chains compete.
8
+
9
+ There is no single mandated shape. Many arrangements work. The discipline is making the decision explicit where presses can collide, not following a recipe.
10
+
11
+ ## The values
12
+
13
+ `Brick.property.pressable` accepts:
14
+
15
+ - `'enabled'` — Brick receives press events; if it has an `on_press` chain, the chain runs.
16
+ - `'disabled'` — Brick does not receive press events even if `on_press` is configured. Useful for runtime-disabled state via DataLink.
17
+ - `'bypass'` — Brick is transparent to press events; the press passes through to whatever sits behind/around it.
18
+
19
+ **Runtime auto-bypass.** Pressable-capable Bricks — `BrickText`, `BrickImage`, `BrickIcon`, `BrickRect`, `BrickSvg`, `BrickLottie`, `BrickRichText`, `BrickVideo`, `BrickVideoStreaming`, `BrickQRCode`, `BrickMaps`, `BrickGenerativeMedia` — with no configured press events (no `on_press`, no `on_focus`, no press/focus outlets feeding Switches or Animations) are auto-bypassed: the runtime renders them with `pointerEvents='none'` so taps fall through to the Brick underneath. A user-set `pressable` value always wins over auto-bypass.
20
+
21
+ **Non-pressable families** — `BrickTextInput`, `BrickCamera`, `BrickSlideshow`, `BrickItems`, `BrickChart`, `BrickWebView`, `BrickRive` — manage their own interaction internally and are not auto-bypassed. Their pressable behavior is unchanged.
22
+
23
+ `Brick.events.on_press` is the standard press handler — an array of `EventAction`s that runs on tap.
24
+
25
+ ## Common arrangements
26
+
27
+ ### Text-only press surface
28
+
29
+ Simplest, most BRICKS-idiomatic.
30
+
31
+ - One `BrickText` with an `on_press` chain.
32
+ - The entire press surface is the text run itself.
33
+
34
+ Use when: the visual is the text and only the text. Branding-light text links, in-flow CTAs.
35
+
36
+ ### Wrapped region (background, border, padding)
37
+
38
+ The moment you add a `BrickRect` (or any shape) as a tile, card, button background, or padded container, the press intent moves to the wrapper.
39
+
40
+ - `BrickRect` carries `on_press`.
41
+ - Inner Bricks (the text label, an icon, an image) sit on top with no press events of their own.
42
+ - The runtime auto-bypasses pressable-capable inner Bricks without press events, so the wrapper's chain fires regardless of where on the tile the user taps. No explicit `pressable: 'bypass'` is required for the common case.
43
+ - Setting `pressable: 'bypass'` on the inner Bricks is still good practice when the child is purely decorative: it documents the design and protects against later edits that wire press- or focus-driven effects onto the child (e.g., a Switch fed by a press outlet) and silently break the tile.
44
+
45
+ Use when: the press surface includes background, border, padding, or composition beyond a single text run.
46
+
47
+ ### Composite tile (image + text + meta, all pressable as one)
48
+
49
+ Same shape as the wrapped region, with more children.
50
+
51
+ - Backing `BrickRect` carries `on_press`.
52
+ - All visual children — image, headline, subhead, icon, meta — auto-bypass when they carry no press events of their own.
53
+ - The user perceives one tile; the runtime sees one press surface.
54
+ - Explicit `pressable: 'bypass'` per child remains a clarity discipline, especially in tile layouts that are duplicated and later edited — a stray press handler on one child would otherwise break that tile silently.
55
+
56
+ Use when: a card, a row in a list (note: `Items` Brick has its own row press model — read the template), a media tile.
57
+
58
+ ### Mixed-action composite (rare but real)
59
+
60
+ A composite where most of the surface does action A, but a small region does action B (e.g., a card whose body opens a detail view but whose corner heart icon toggles favorite).
61
+
62
+ - Backing `BrickRect` carries `on_press` for action A.
63
+ - Most children auto-bypass (no press events of their own).
64
+ - The exception child (heart icon) carries its own `on_press` for action B. Because it now has a press chain, auto-bypass is off and the icon owns its hit region.
65
+ - Caveat: ensure the exception child is large enough to hit reliably without colliding with the surrounding action A. If it's too small, users will fight it.
66
+
67
+ Use when: genuinely needed. If you find yourself reaching for this often, the design is probably trying to do too much per tile — split into clearer affordances.
68
+
69
+ ## Failure modes
70
+
71
+ ### Overlapping pressables
72
+
73
+ Two or more children of a composite each carry their own `on_press` (or a Switch/Animation fed by a press/focus outlet). Auto-bypass doesn't apply — each child has events. The runtime resolves taps to whichever Brick owns that pixel; in practice this means inconsistent behavior depending on where the user actually lands.
74
+
75
+ **Fix**: pick the press owner (usually the wrapper) and set the others to `pressable: 'bypass'` explicitly. The user-set value wins, so this forces the press through to the intended owner even when the loser Bricks have events.
76
+
77
+ ### Hidden press handlers on inner Bricks
78
+
79
+ A composite that used to auto-bypass cleanly stops doing so after a Switch is wired to read a press or focus outlet on an inner Brick, or after an `on_press` is added to that child for an unrelated effect. The wrapper appears to lose hit area for that region without an obvious cause — auto-bypass is off the moment any press event is configured.
80
+
81
+ **Fix**: when you wire any press- or focus-driven effect on a Brick inside a composite, set its `pressable` explicitly. If the effect should run *and* the wrapper should still win the tap, use `pressable: 'bypass'` on the child and drive the effect from the wrapper's chain instead.
82
+
83
+ ### Decorative overlay with unintended press chain
84
+
85
+ A decorative `BrickRect` (frame edge, gradient overlay, spacing tile) sits in front of a pressable region and has press events wired — usually a leftover Switch trigger or copy-pasted from a real button. Auto-bypass doesn't engage, and the overlay silently consumes taps.
86
+
87
+ **Fix**: any decorative overlay sitting in front of a pressable region should be `pressable: 'bypass'` explicitly. Treat "transparent in front of pressable" as a code smell that needs an explicit pressable decision — the runtime would auto-bypass a Brick with no press events, but the explicit value documents intent and survives future edits that wire effects into it.
88
+
89
+ ### Phantom hover on no-touch hardware
90
+
91
+ Carried over from a web design with hover affordances — usually a Brick that brightens or shifts on press but the deployment is signage with no touch.
92
+
93
+ **Fix**: drop the affordance, or move it to a state driven by a different signal (proximity sensor, scheduled time, Data from a peripheral Generator).
94
+
95
+ ### Web link semantics
96
+
97
+ Designers used to web habits sometimes add `<a>`-style underlines or color treatments expecting a navigation primitive.
98
+
99
+ **Fix**: BRICKS has no link semantic. `on_press` triggers `CHANGE_CANVAS` (internal navigation), a Generator (external action — open URL, dial number), or a Subspace action. The visual treatment is a design choice; the navigation primitive is a press handler.
100
+
101
+ ### `pressable: 'disabled'` without visual signal
102
+
103
+ A Brick is set to `disabled` programmatically (e.g., during a pending submission) but the visual doesn't change. Users tap and nothing happens — looks broken.
104
+
105
+ **Fix**: pair `pressable: 'disabled'` with a Switch that alters appearance (opacity, color, an overlay) so disabled state is visible. This is also step 3 (immediate feedback) of the journey — the user must see that their tap was received *and* declined.
106
+
107
+ ### Long-press misuse
108
+
109
+ `delayLongPress` is real — the runtime distinguishes tap from long-press. Useful for secondary actions on a primary tile (long-press to favourite, tap to open). Misuse: hiding *primary* actions behind a long-press because there isn't room for a button. Long-press is discoverable only to users who already know it; primary actions need primary affordances.
110
+
111
+ ### Press feedback collapsed
112
+
113
+ A press registers — the wiring works — but there is no visual change at the moment of registration (no scale-down, no opacity flash, no Switch-driven highlight). This is the most common silent UX failure: the press works, the user can't tell, so they press again. See [`user-journey.md`](user-journey.md) step 3.
114
+
115
+ **Fix**: every `on_press` must pair with a visible state change at registration. A 100ms scale-down via Standby is enough; the user's eye needs to land on *something* moving.
116
+
117
+ ## Decision checklist when authoring a composite button
118
+
119
+ For every Brick in the composite, answer:
120
+
121
+ 1. Does this Brick own a press chain — `on_press`, or a Switch/Animation fed by a press/focus outlet?
122
+ 2. If yes — should it own the tap, or should the wrapper own it?
123
+
124
+ If two or more Bricks answer "yes" on overlapping hit areas, set the loser(s) to `pressable: 'bypass'` explicitly. If only the wrapper has a press chain, pressable-capable inner Bricks auto-bypass — `pressable: 'bypass'` is recommended for clarity but not strictly required. Non-pressable family Bricks (TextInput, Camera, Slideshow, Items, Chart, WebView, Rive) follow their own interaction model and don't auto-bypass.
125
+
126
+ If any answer is "I haven't thought about it", the failure modes above are how it bites.
@@ -0,0 +1,168 @@
1
+ # The Universal User-Journey Spine
2
+
3
+ Every interaction in a BRICKS Application — touch press, peripheral input, remote command, external trigger — passes through the same seven steps. Skip any of them and the design has a hole the user will fall into. The shape is universal across QR scan, face detection, BLE proximity, NFC tap, payment terminal, voice mic, touch tile, operator remote.
4
+
5
+ This file is the spine. Anything that isn't here probably belongs in a more specific reference (transact intensification, peripheral particulars, archetype rule sets).
6
+
7
+ ## The seven steps
8
+
9
+ ### 1. Affordance — "Can I do something here?"
10
+
11
+ The user must know the channel exists before discovering it by chance. A QR-scan zone with no scan-here label, a BLE-proximity threshold with no "approach to begin" hint, a touch tile that looks decorative — all fail at step 1.
12
+
13
+ Affordance is the *invitation* to act. Without it, the rest of the journey is invisible.
14
+
15
+ **BRICKS-native expression:**
16
+
17
+ - A pressable region's visual treatment must declare "press me" — contrast, weight, a hint of motion via Standby Transition (gentle pulse), an icon. Use the runtime; don't rely on the user inferring touch from a flat Rect.
18
+ - Peripheral channels need an instructive Brick in the line-of-sight: a scan-frame Image, a microphone glyph that lights when listening is possible, a "tap card here" overlay.
19
+ - For ambient peripherals (BLE, camera detection), use a `valueHit` Data event on detection to *promote* the affordance — the screen invites the next step once the user is in range.
20
+
21
+ **Failure modes:**
22
+
23
+ - **Affordance hidden.** Pressable Brick visually indistinguishable from non-pressable. User walks past.
24
+ - **Affordance present but ambiguous.** "Touch screen" floating with no anchor — touch *what*?
25
+ - **Affordance promised, channel absent.** A scan frame drawn for a deployment that has no scanner.
26
+ - **Affordance for absent capability.** Hover affordance on a no-touch panel; web-style cursor pointer on touch hardware.
27
+
28
+ ### 2. Instruction — "What do I do?"
29
+
30
+ Once the user is invited, they need to act. The instruction must be specific enough to act on without overspecifying. Match the user's likely mental model — a payment terminal is not a QR scanner; same Canvas should not pretend otherwise.
31
+
32
+ **BRICKS-native expression:**
33
+
34
+ - A short directive in `RichText` near the affordance: "Scan your QR code", "Tap to start", "Hold card to reader". Bind to a Data string for multilingual.
35
+ - Iconography or animation that demonstrates the action (a hand reaching toward the reader, a card moving toward the panel). One demo, not a five-step diagram.
36
+ - For unfamiliar peripherals (NFC, voice for new users), include a one-shot tutorial on first entry — a separate Canvas, not a permanent overlay.
37
+
38
+ **Failure modes:**
39
+
40
+ - **Instruction missing.** Affordance present, action unclear. Common with NFC and voice — users don't know they need to *speak* or *tap*.
41
+ - **Instruction overspecified.** Five steps for a one-step action.
42
+ - **Instruction in wrong language** for the actual user — pin to deployment locale.
43
+ - **Instruction stays after the action.** "Scan now" still on screen after the scan succeeded — confuses about whether to scan again.
44
+
45
+ ### 3. Immediate feedback — "Did I do it?"
46
+
47
+ The instant between the user's action and the system's acknowledgement is the highest-anxiety moment in the flow. The user has *committed* — touched, scanned, tapped, spoken — and now needs proof their action registered.
48
+
49
+ This is non-negotiable. A flow that skips immediate feedback has a hole the user falls through every time.
50
+
51
+ **BRICKS-native expression:**
52
+
53
+ - A snap-cut visual change at the moment of registration (not a fade-in — fades read as ambient, snaps read as acknowledgement). Brick scale, opacity, or color change via Switch on the input event.
54
+ - A Standby Transition on a new Brick that enters at the moment of acknowledgement — a checkmark, a state badge, a progress indicator.
55
+ - For audio-enabled deployments, a short auditory cue at the moment of registration. Silent is OK; ambient music is not enough.
56
+ - If the registration takes any perceptible time (peripheral confirming, network round-trip), step 3 still fires first — the *system received* you — then step 4 takes over for the *processing* state.
57
+
58
+ **Failure modes:**
59
+
60
+ - **Feedback skipped, jumps to continuation.** User can't tell if their action registered or the Canvas just changed for unrelated reasons.
61
+ - **Feedback fades in slowly.** Fade reads as ambient, not as response. Use snap on the moment of registration.
62
+ - **Feedback on the wrong Canvas.** User taps, Canvas A doesn't react, Canvas B (the next one) loads after a beat — user assumes nothing happened and taps again.
63
+ - **Same feedback for success and failure.** A check-mark feedback for a payment that's still processing reads as success — user walks away from a partial transaction.
64
+
65
+ ### 4. In-flight visibility — "Is it being handled?"
66
+
67
+ When the system needs time — peripheral confirming, network round-trip, Generator awaiting response — the user must know the system is working. Silent processing reads as broken.
68
+
69
+ **BRICKS-native expression:**
70
+
71
+ - A clear in-flight Brick set: spinner, progress indicator, animated state badge. Use `Animation` `runType: 'loop'` here — this is a legitimate loop use (continuous attention-draw signalling activity).
72
+ - The trigger affordance disables (`pressable: 'disabled'` or Switch-driven hide) so the user can't trigger again.
73
+ - Time-bounded: if the in-flight state can plausibly take > 5 seconds, surface a progress estimate or a continued-activity reassurance ("Still processing — this can take up to 30 seconds"). Don't leave the user staring at an unchanging spinner past their patience threshold.
74
+ - For peripherals: the peripheral's own state (Generator event from the device) should drive Switch transitions, not a guess at how long it'll take.
75
+
76
+ **Failure modes:**
77
+
78
+ - **No in-flight state at all.** Action triggered, Canvas frozen until result. User taps again; second action queues; chaos.
79
+ - **In-flight state visually identical to idle.** No motion, no change in tone — user can't tell whether the system is working or stalled.
80
+ - **In-flight state with no time-out.** System hangs; Canvas stays on "processing…" forever. Every in-flight state needs a Generator-driven failure path back to step 6.
81
+ - **In-flight state allows re-trigger.** User can press again, queues duplicate work — especially bad for transact flows.
82
+
83
+ ### 5. Continuation — "What's next?"
84
+
85
+ The action succeeded; what now? The user needs to know whether they continue here, move to a next state, walk away, or hand over to a peripheral / operator.
86
+
87
+ **BRICKS-native expression:**
88
+
89
+ - A Canvas change is the most common continuation — Switch on the success Data event, new Canvas with the next prompt or the result.
90
+ - For flows that loop (slideshow, ambient signage), continuation can mean returning to attract / idle state.
91
+ - The continuation Canvas's hero Brick(s) share id with the previous Canvas's hero — Truth #3 auto-tween carries the user across with continuity, not by hard cut.
92
+ - If continuation is "stay here, do another thing," the prompt must update — the user shouldn't have to guess that they can act again.
93
+
94
+ **Failure modes:**
95
+
96
+ - **Continuation ambiguous.** Success acknowledged but the user is left on a static Canvas with no next-step prompt. They wait, then walk away.
97
+ - **Continuation surprises.** Success on Canvas A, but Canvas D loads next — the journey shifts in a way that didn't match the user's mental model.
98
+ - **Continuation lacks closure for some users.** A receipt flow that auto-advances after 3s before the user has read the confirmation.
99
+
100
+ ### 6. Recovery — "What if it went wrong?"
101
+
102
+ Error states are first-class designed states, not afterthoughts. The recovery path tells the user what failed, why if relevant, and how to proceed — including the option to abandon.
103
+
104
+ **BRICKS-native expression:**
105
+
106
+ - A dedicated error Canvas (or a clearly distinct error state on the current Canvas, Switch-revealed). Not a tiny red text in a corner.
107
+ - The error declares the cause in user-language: "Card declined" not "ERR_TRANSACTION_REJECTED". "Couldn't read the code — try moving closer" not "Scanner timeout".
108
+ - The recovery offers a path — retry, alternate method, get help, abandon — never just states the failure.
109
+ - Form input retained across error — user shouldn't re-type after a validation fail.
110
+ - For peripherals, distinguish *user error* (action failed; retry) from *system error* (device unavailable; not the user's fault) from *abandonment* (timeout; resume or restart).
111
+
112
+ **Failure modes:**
113
+
114
+ - **Recovery as dead-end.** "Error. Please try again." with no path forward.
115
+ - **Recovery erases progress.** User restarts from step 1 of a 5-step flow after a single-step failure.
116
+ - **Recovery in unrelated language.** Technical error codes shown to end users.
117
+ - **Recovery missing for the silent failures.** Peripheral disconnected mid-flow with no recovery path — user assumed it'd just work.
118
+ - **Same recovery for all error types.** User can't distinguish "try again" from "the system is broken, walk away" from "you need to do something different".
119
+
120
+ ### 7. Closure — "When can I leave?"
121
+
122
+ Especially in transact flows, the *past-the-point-of-no-return* moment must be explicit. The success state must declare the action complete in a way the user trusts before they walk away.
123
+
124
+ **BRICKS-native expression:**
125
+
126
+ - A success Canvas with weight — a confident headline, a check or stamp graphic, a printed-receipt analogue if applicable. Not a transient toast.
127
+ - The success Canvas must hold visibly for enough time that a careful user reads it (5–10 seconds for a transact closure; 2–3 for routine completion) before auto-advancing or resetting to idle.
128
+ - For ongoing actions (registered, will-be-notified), the closure must clarify the future: "You'll receive a confirmation by email" or "We'll call you when ready" with the expected wait clearly stated.
129
+ - For closures that have a physical artifact (receipt, ticket, badge), the artifact's presence is part of the closure — the Canvas waits for the printer event before declaring done.
130
+
131
+ **Failure modes:**
132
+
133
+ - **Closure transient.** Success shown for 800ms, auto-advances to idle. User mid-blink, never saw confirmation.
134
+ - **Closure ambiguous with in-flight.** Success badge in the same visual tone as the processing spinner — user uncertain whether they're done.
135
+ - **Closure missing for irreversible action.** Payment confirmed by terminal but no on-screen success — user walks away unsure, doubles up via a second tap.
136
+ - **Closure carries forward as a state the user can edit.** Success Canvas with input fields the user can change — implies the action isn't really committed.
137
+
138
+ ## When the seven steps compress
139
+
140
+ Some interactions naturally collapse steps. Don't pad; collapse with intention.
141
+
142
+ - **Glance-archetype loops** (signage, attractor): the user doesn't act. Step 1 (affordance) collapses to "attention", step 5 (continuation) to "loop", steps 3, 4, 6, 7 don't apply. Only 1, 2, 5 are live.
143
+ - **Pure-monitor screens**: user doesn't act. Steps 1–4, 6, 7 collapse; the design is entirely about reading the state (see [`monitoring-screens.md`](monitoring-screens.md)).
144
+ - **One-step transact** (NFC tap-and-go without amount confirmation): steps 1–4 fire in rapid sequence, but each still must be present — affordance, instruction, registration feedback, in-flight visibility, even if the in-flight is <1 second.
145
+
146
+ Collapse means *fewer steps*, not *skipped steps*. A step you've skipped is a hole, not a compression.
147
+
148
+ ## When the seven steps intensify (transact)
149
+
150
+ For payment / identity capture / safety-critical actions, the journey tightens:
151
+
152
+ - **Step 2 (instruction) gets a confirmation step bolted on** — "You're about to charge $42.50. Confirm?" — a designed checkpoint Canvas before commit.
153
+ - **Step 4 (in-flight visibility) cannot be skipped or hurried** — even a sub-second processing moment needs explicit visual presence. The cost of a missed in-flight is the user double-tapping a $42 charge.
154
+ - **Step 6 (recovery) gets explicit categories** — card declined / network error / user cancelled / timeout — each with its own recovery path. A generic "Try again" is insufficient.
155
+ - **Step 7 (closure) gets longer hold time + receipt** — the user must trust the transaction completed; cheap closures invite dispute.
156
+
157
+ This intensification is enough that transact flows often deserve a separate design pass; for v1 of this skill, treat it as the loudest application of the seven-step spine.
158
+
159
+ ## How to use this file
160
+
161
+ When designing any interaction:
162
+
163
+ 1. **Open the seven steps.** Walk the flow as the user would.
164
+ 2. **For each step, name** what the user sees / what the system does / how feedback manifests / what fails. If you cannot fill all four for a step, that step is unfinished.
165
+ 3. **Check the failure modes per step.** Each is a thing agents commonly skip. Caught here is cheap.
166
+ 4. **Express in BRICKS primitives.** Each step has expression notes — which Brick families, which architecture truths (Truth #3 hero auto-tween across the journey; Truth #5 Generator async; Truth #7 Standby Transition for feedback).
167
+
168
+ The spine doesn't tell you what to design. It tells you which holes to *not have*.