@orkestrel/scaffold 0.0.33 → 0.0.36

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 (45) hide show
  1. package/dist/host/agents/orchestration.md +28 -12
  2. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +146 -194
  3. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +29 -22
  4. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +31 -4
  5. package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +103 -33
  6. package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +1 -1
  7. package/dist/host/agents/skills/orkestrel-build-application/references/application.md +2 -2
  8. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +2 -1
  9. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +21 -3
  10. package/dist/host/agents/skills/orkestrel-falsify/agents/openai.yaml +4 -0
  11. package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +7 -7
  12. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +10 -2
  13. package/dist/host/agents/skills/orkestrel-human-journey/SKILL.md +124 -0
  14. package/dist/host/agents/skills/orkestrel-human-journey/agents/openai.yaml +4 -0
  15. package/dist/host/agents/skills/orkestrel-human-journey/references/captures.md +74 -0
  16. package/dist/host/agents/skills/orkestrel-human-journey/references/layer.md +131 -0
  17. package/dist/host/claude/agents/analyst.md +4 -4
  18. package/dist/host/claude/agents/codex.md +4 -4
  19. package/dist/host/claude/agents/grok.md +4 -4
  20. package/dist/host/claude/agents/sol.md +56 -0
  21. package/dist/host/claude/rules/application.md +5 -3
  22. package/dist/host/claude/rules/documentation.md +6 -3
  23. package/dist/host/claude/rules/patterns.md +10 -0
  24. package/dist/host/claude/rules/quality.md +5 -4
  25. package/dist/host/claude/rules/tests.md +17 -12
  26. package/dist/host/claude/settings.json +94 -5
  27. package/dist/host/claude/skills/orkestrel-human-journey/SKILL.md +10 -0
  28. package/dist/host/claude/skills/orkestrel-polish-surface/SKILL.md +1 -1
  29. package/dist/host/codex/agents/grok.toml +1 -1
  30. package/dist/host/codex/agents/opus.toml +3 -3
  31. package/dist/host/codex/agents/planner.toml +1 -1
  32. package/dist/host/codex/agents/reviewer.toml +1 -1
  33. package/dist/host/dotfiles/gitignore +3 -0
  34. package/dist/host/manifest.json +41 -1
  35. package/dist/host/scripts/codex.sh +0 -0
  36. package/dist/host/scripts/cursor.sh +0 -0
  37. package/dist/host/scripts/deps.sh +0 -0
  38. package/dist/host/scripts/ollama.sh +0 -0
  39. package/dist/host/tests/policy.test.ts +33 -0
  40. package/dist/host/tests/setupPolicy.ts +319 -2
  41. package/dist/src/core/index.cjs +1 -1
  42. package/dist/src/core/index.cjs.map +1 -1
  43. package/dist/src/core/index.js +1 -1
  44. package/dist/src/core/index.js.map +1 -1
  45. package/package.json +2 -2
@@ -139,7 +139,7 @@ when the role file already pins it.
139
139
  | Creative design and alternatives | `planner` | `planner` | Opus 5 (native / bridge) |
140
140
  | Design-fit review and audit | `reviewer` | `reviewer` | Opus 5 (native / bridge) |
141
141
  | Objective analysis and correctness audit | `analyst` | `analyst` | GPT-5.6 Sol (bridge / native) |
142
- | Nontrivial implementation (objective) | `codex` route `implementer` | `implementer` | GPT-5.6 Sol (bridge / native) |
142
+ | Nontrivial implementation (objective) | `sol` | `implementer` | GPT-5.6 Sol (bridge / native) |
143
143
  | Nontrivial implementation (subjective) | `implementer` | `implementer` route `opus` | Opus 5 (native / bridge) |
144
144
  | Bounded primary-source research | `researcher` | `researcher` | Grok → Luna → Sonnet |
145
145
  | Repository reconnaissance | `scout` | `scout` | Grok → Luna → Sonnet |
@@ -160,9 +160,8 @@ when the role file already pins it.
160
160
  has stepped past Grok. Record which step you are on.
161
161
  - `orkestrel` stays native because it carries the package catalog in its own role file. Sending its
162
162
  job to a bench means shipping that catalog across, which costs more than the bench saves.
163
- - One gap is open and recorded rather than improvised: the Sol implementer is still `codex` route
164
- `implementer` while its Codex mirror has a named `opus` bridge. Closing it means deciding where
165
- the shared Sol transport contract lives once two bridges follow it.
163
+ - `codex` is the shared Sol transport contract, not a route. `analyst` and `sol` are the named
164
+ bridges; both bind that contract by reference and pin only their route and sandbox.
166
165
  - Opus and Sol roles use high effort. Native cheap-tier roles use low or medium. Bridge drivers use
167
166
  the cheapest tier that can run a CLI.
168
167
  - Never route orchestration or acceptance across a bridge.
@@ -202,6 +201,10 @@ Every role honours this floor. No dispatch may widen it.
202
201
  same class of defect through a new door, the search is following the frame rather than the
203
202
  defect. Bound the scope, then fan out independent lenses over disjoint slices in one pass.
204
203
  Parallelism is worth more here for the framing it breaks than for the wall-clock it saves.
204
+ - Two lanes is the adversarial pass's FLOOR, not its shape. Where a subject has more seams than
205
+ two lanes can attack, fan out one lens per seam over disjoint slices, keep every lens blind and
206
+ clean-contexted, and number every slice's claims in one shared sequence. Change the lenses in a
207
+ successor round rather than repeating them.
205
208
  - Decompose by required context and independently verifiable acceptance criteria, not by task type.
206
209
  - Send instructions down fully specified. Return findings smaller than the context consumed.
207
210
  - Parallelize independent work. Serialize dependencies and shared-file contention.
@@ -366,10 +369,11 @@ The harness bridge names the concrete mechanism for each of these.
366
369
  - Send a decision taken mid-campaign to every unit already in flight whose brief it invalidates. An
367
370
  executor cannot see a change made after it was dispatched, so it writes the state its brief
368
371
  described and the defect surfaces as its own.
369
- - Treat brief and report files as unit evidence, not deliverables. Never commit them, and sweep
370
- them when the campaign that produced them is accepted.
372
+ - Treat the `tmp/` brief and report pair as ephemeral launch copies. **Bench laws** rule 4 owns
373
+ what is kept and what is swept; follow it there rather than deciding here.
371
374
  - Promote anything that must outlive the campaign into a durable artifact before the sweep — a
372
- commit message, a guide, a rule, a retrospective. What is only in a swept file did not survive.
375
+ commit message, a guide, a rule, a retrospective. What is only in a swept file did not survive,
376
+ and a debrief that must quote the record verbatim has nothing to quote.
373
377
 
374
378
  ### Where campaign artifacts live
375
379
 
@@ -491,11 +495,17 @@ command that outlives the turn that started it. Every law here binds all of them
491
495
  - Write a multi-step chain to a script file and run the file. A chain composed inside one shell
492
496
  argument cannot be read back, corrected, or re-run, and the record of what actually ran is the
493
497
  argument text in a transcript rather than a file on disk.
498
+ - On a Windows host this binds every program-carrying command, not only long ones. Heredocs,
499
+ `node -e`, `node -p`, `&&` chaining, and any argument carrying `${...}` trip the Git Bash
500
+ approval classifier and turn an unattended run into a manual approval prompt. Write the program
501
+ to a file, invoke the file, and keep each shell call one plain command.
494
502
  - Detach anything that must survive its launching shell with `setsid`. A backgrounded flow the
495
503
  harness reaps mid-step leaves the work half done and the exit status missing, and the reap looks
496
504
  identical to the step failing.
497
- - Size the cap from the observed high mark of comparable commands, plus an independently budgeted
498
- gate allowance, plus explicit slack. Never size it from the estimate alone.
505
+ - Size the cap yourself, from the observed high mark of comparable commands, plus an
506
+ independently budgeted gate allowance, plus explicit slack. Never size it from the estimate
507
+ alone. Never delegate it: a bridge starts with a clean context, holds no record of prior runs,
508
+ and can only guess. A cap-killed exec is indistinguishable from a real failure.
499
509
  - Run the first use of any CLI flag, subcommand, quoting form, or stdin combination in a throwaway
500
510
  probe. Never inside a dispatched unit or a publish chain.
501
511
  - A launch is not a launch until its record grows past its header. Confirm the log advanced beyond
@@ -575,9 +585,11 @@ transport.
575
585
  4. **Ephemeral streams, durable records.** A journal proves a bench is alive and recovers an
576
586
  interrupted session. Keep journals under `tmp/`, never commit them, and sweep them at acceptance
577
587
  after the final gate evidence is recorded. The **brief**, the returned **distillate**, the
578
- **audit verdict**, and the **acceptance evidence** are not streams. Commit each one as its unit
579
- is dispatched and as it returns, because each encodes knowledge that costs real money to
580
- re-derive and none of it is reproducible from the diff.
588
+ **audit verdict**, and the **acceptance evidence** are not streams. Copy each into
589
+ `.orkestrel/<package>/` and commit it as its unit is dispatched and as it returns, then sweep
590
+ only the `tmp/` copy. None of it is reproducible from the diff, and a debrief has no primary
591
+ source without it. This rule owns retention for the whole contract; **Dispatch anatomy**
592
+ points here rather than answering it.
581
593
 
582
594
  ### Recovering a dark bench
583
595
 
@@ -641,6 +653,10 @@ and propagates as files rather than as a cascade.
641
653
  prove that target's gates still green. `repair` restores `tests/setupPolicy.ts` and
642
654
  `tests/policy.test.ts`, so a vendored-only release can turn a green target red. A target bumps
643
655
  only when its own published surface moved.
656
+ - Keep a target's own Claude permissions in `.claude/settings.local.json`, never in the vendored
657
+ `.claude/settings.json`. `repair` restores the vendored copy, so a `defaultMode` or an `allow`
658
+ entry added there is reverted without warning and the operator loses grants they set
659
+ deliberately. Change the vendored file only here, in the host inventory.
644
660
  - Never edit a vendored file inside a target. `repair` restores it, so the edit is reverted and
645
661
  reports as drift in `scaffold audit`. In this repository those same files are the published
646
662
  `dist/host` surface, so editing one forces a bump, a publish, and a re-propagation across every
@@ -14,101 +14,99 @@ description: >-
14
14
 
15
15
  # Enterprise Bootstrap
16
16
 
17
- General-purpose guide for **intentional visual design** executed with **Bootstrap 5.3**: distinctive where it matters, disciplined everywhere else, utilities-first, accessible, and responsive.
17
+ Set a deliberate visual direction, build it from Bootstrap 5.3 components and utilities, and settle
18
+ every claim about the result from what renders.
18
19
 
19
- This package is layered on purpose:
20
+ Open the reference that owns a subject before writing markup. Never guess a class name: an invented
21
+ utility (`.vw-50`, `.pointer-events-none`) has no rule in the shipped CSS and fails silently. Pick
22
+ components from [components.md](references/components.md) → Choosing components, take their markup
23
+ from the same file, and take fine layout from [utilities.md](references/utilities.md). Where
24
+ Bootstrap ships no component for the need — combobox, date picker, tags input, data grid, tree —
25
+ work the native-first ladder in [bootstrap-reference.md](references/bootstrap-reference.md) → When
26
+ not to hand-roll before building one.
20
27
 
21
28
  | Layer | File | Holds |
22
29
  | -------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------- |
23
- | Operate | `SKILL.md` (this file) | Process, decision rules, checklists, portability |
24
- | Design craft | [frontend-design.md](references/frontend-design.md) | Full aesthetic, typography, signature, copy, anti-defaults |
30
+ | Operate | `SKILL.md` (this file) | Process, decision rules, checklist |
31
+ | Design craft | [frontend-design.md](references/frontend-design.md) | Aesthetic, typography, signature, interface copy, anti-defaults |
25
32
  | Components | [components.md](references/components.md) | Bootstrap component markup + enterprise selection notes |
26
33
  | Utilities | [utilities.md](references/utilities.md) | Class index, helpers, composition notes |
27
34
  | Bootstrap deep | [bootstrap-reference.md](references/bootstrap-reference.md) | Color modes, theming/tokens, forms, JS lifecycle, a11y depth, enterprise patterns |
28
35
 
29
- Open the relevant reference instead of guessing class names or watering down the craft — the class indexes exist because invented utilities (`.vw-50`, `.pointer-events-none`) ship silent no-ops.
30
-
31
36
  ---
32
37
 
33
- ## Portability (any project / tool / environment)
34
-
35
- Product-agnostic. Apply the same way wherever this package is loaded.
38
+ ## Portability
36
39
 
37
- 1. **No repo assumptions.** Do not assume Vue, React, a skin library, a folder layout, or a named product. Infer stack from the workspace.
38
- 2. **Bootstrap contract.** Target Bootstrap **5.3.x** class names and behaviors. Compatible skins that keep `.btn`, `.card`, `.form-control`, `data-bs-*`, etc. are fine — obey the same contracts.
39
- 3. **Project rules win on code law.** If the repo defines conventions (`AGENTS.md`, lint rules, a design system), follow those for languages, layout, and forbidden patterns. This package owns **UI craft + Bootstrap usage**, not language law.
40
- 4. **Framework-neutral markup.** Prefer semantic HTML + Bootstrap classes. Wire behavior with whatever the project already uses; in SPAs prefer the framework-native Bootstrap wrappers over raw `bootstrap.*` JS (see [bootstrap-reference.md](references/bootstrap-reference.md) → JavaScript lifecycle).
41
- 5. **Distribution.** Keep this folder intact so relative links between files resolve. Install or vendor it wherever your tooling looks for skills/guides paths are tooling-specific; the content is not.
42
- 6. **CDN or bundler.** Either is fine see [bootstrap-reference.md](references/bootstrap-reference.md) Quick Start (5.3.8) for CDN; prefer the project's existing Bootstrap package when present.
43
- 7. **When to apply.** Use on UI, Bootstrap, or visual-design work matching the description above. When the user attaches or points at this package, treat it as authoritative for the visual pass.
40
+ 1. **Assume no stack.** Infer it from the workspace. Do not assume Vue, React, a skin library, a folder layout, or a named product.
41
+ 2. **Target Bootstrap 5.3.x** class names and behaviors. Hold a compatible skin that keeps `.btn`, `.card`, `.form-control`, and `data-bs-*` to the same contracts.
42
+ 3. **Follow the project's code law.** Its `AGENTS.md`, lint rules, and design system decide language, layout, and forbidden patterns. This package owns UI craft and Bootstrap usage, not language law.
43
+ 4. **Write framework-neutral markup** semantic HTML plus Bootstrap classes. Wire behavior with what the project already uses; in an SPA prefer the framework-native Bootstrap wrappers over raw `bootstrap.*` JS ([bootstrap-reference.md](references/bootstrap-reference.md) → JavaScript lifecycle).
44
+ 5. **Keep this folder intact** so the relative links between its files resolve. Install or vendor it wherever the tooling looks for skills; the paths are tooling-specific, the content is not.
45
+ 6. **Use the project's installed Bootstrap** when it has one; otherwise take the CDN snippet from [bootstrap-reference.md](references/bootstrap-reference.md) Quick start (5.3.8).
46
+ 7. **Apply this package** to UI, Bootstrap, and visual-design work matching the description above. When the user points at it, treat it as authoritative for the visual pass.
44
47
 
45
48
  ---
46
49
 
47
50
  ## Dual mandate
48
51
 
49
- 1. **Design lead** — point of view rooted in the _subject_ (audience, job-to-be-done, vernacular). One justified aesthetic risk; boldness in one place. Full text: [frontend-design.md](references/frontend-design.md).
50
- 2. **Bootstrap engineer** — components + utilities first; custom CSS only when the system cannot express the need; paint via `--bs-*` so light/dark survive.
52
+ 1. **Design direction** — take a point of view rooted in the _subject_ (audience, job-to-be-done, vernacular). Take one justified aesthetic risk, in one place.
53
+ 2. **Bootstrap execution** — components and utilities first; custom CSS only when the system cannot express the need; paint through `--bs-*` so light and dark both survive.
51
54
 
52
- **Context match:** a marketing page may open with a thesis-hero; an authenticated tool opens with clarity and scan paths. Same craft, different density — in product UI the signature lives in the chrome, never in the data ([frontend-design.md](references/frontend-design.md) → Where the signature lives).
55
+ Match the density to the context: a marketing page may open with a thesis-hero, an authenticated
56
+ tool opens with clarity and scan paths. In product UI put the signature in the chrome, never in the
57
+ data ([frontend-design.md](references/frontend-design.md) → Where the signature lives).
53
58
 
54
59
  ---
55
60
 
56
61
  ## Process
57
62
 
58
- Detail and wording live in [frontend-design.md](references/frontend-design.md). Condensed loop:
59
-
60
- 1. **Ground** — subject, audience, screen's single job (state it). Use known user preferences and prior designs as hints, not templates.
61
- 2. **Plan** — token system: **color** (4–6 named values), **type** (display / body / utility), **layout** (prose + ASCII if useful), **signature** (one memorable element).
62
- 3. **Critique the plan** — if swapping the logo would make it "any SaaS," revise. Avoid clustered AI defaults unless the brief asks: cream+#F4F1EA+serif+terracotta; near-black+acid green/vermilion; broadsheet hairlines / zero radius / dense columns. Brief wins when it pins a direction.
63
- 4. **Build** — Bootstrap from components/utilities; map plan tokens onto theme variables or a thin skin — no scattered one-off hex (token discipline: [bootstrap-reference.md](references/bootstrap-reference.md) → Theming & design tokens). Watch selector specificity (utility vs custom canceling).
64
- 5. **Critique again** — remove one accessory (Chanel). Contrast, focus, `prefers-reduced-motion`, mobile, all five states present. Critique the render, not the markup.
63
+ Design craft — subject grounding, hero and thesis, typography, structure, motion, restraint, and
64
+ interface copy — lives in [frontend-design.md](references/frontend-design.md). Read it before
65
+ setting a direction. The loop:
65
66
 
66
- Brainstorm privately; show higher-confidence directions.
67
+ 1. **Ground** name the subject, the audience, and the screen's single job, and state them. Use known user preferences and prior designs as hints, not templates.
68
+ 2. **Plan** — build a token system: **color** (4–6 named values), **type** (display / body / utility), **layout** (prose plus ASCII if useful), **signature** (one memorable element).
69
+ 3. **Critique the plan** — if swapping the logo would make it "any SaaS", revise. Avoid the clustered AI defaults unless the brief asks for them: cream + #F4F1EA + serif + terracotta; near-black + acid green or vermilion; broadsheet hairlines, zero radius, dense columns. The brief wins when it pins a direction.
70
+ 4. **Build** — compose Bootstrap components and utilities; map the plan's tokens onto theme variables or a thin skin, with no scattered one-off hex ([bootstrap-reference.md](references/bootstrap-reference.md) → Theming & design tokens). Watch selector specificity: a utility and a custom rule that cancel each other show up as padding and margin bugs.
71
+ 5. **Critique the render** — remove one accessory. Check contrast, focus, `prefers-reduced-motion`, mobile, and all five states. Critique what rendered, not the markup.
67
72
 
68
- **Rendered proof.** A claim about a screen is settled by what the browser paints, never by the markup that was supposed to paint it — source-reading review passes a component that renders nothing. The review input is captures at both viewports and both themes plus an accessibility snapshot; source only corroborates the mechanism. For the full review-round campaign built on that evidence, use the `orkestrel-polish-surface` skill instead of improvising one here.
69
-
70
- ---
73
+ Brainstorm privately; show a direction only once it satisfies the brief and the quality floor
74
+ ([frontend-design.md](references/frontend-design.md) → Process).
71
75
 
72
- ## Design principles
76
+ **Rendered proof.** Settle every claim about a screen from a capture, never from source alone;
77
+ `.agents/orchestration.md` owns this law where it is present. The review input here is captures at
78
+ both viewports and both themes plus an accessibility snapshot; source only corroborates the
79
+ mechanism. For a full review-round campaign built on that evidence, use the
80
+ `orkestrel-polish-surface` skill instead of improvising one here.
73
81
 
74
- - **Subject first** distinctive choices from the product's world, not a generic kit.
75
- - **Hero / thesis when it fits** — open with the most characteristic thing. Big-number+gradient-stat blocks are the template answer use only if truly best.
76
- - **Signature + restraint** one memorable element; quiet discipline around it.
77
- - **Structure is information** — eyebrows, `01/02/03`, dividers only when order/taxonomy is real.
78
- - **Typography carries personality** — deliberate pairing and scale; characterful display, restrained.
79
- - **Motion with purpose** — Bootstrap transitions first; one orchestrated moment if earned; less motion often beats "AI sparkle."
80
- - **Match complexity to vision** — maximalist = elaborate execution; minimal = precision.
81
- - **Copy is design** — see Writing below and frontend-design.
82
+ **Mechanical proof.** Three instruments settle what a capture cannot. Pair each one with a negative
83
+ control drawn from outside the population it covers, and treat an instrument whose control passes as
84
+ broken; `.claude/rules/quality.md` owns this law where it is present:
82
85
 
83
- ### Writing (interface copy)
84
-
85
- - Name what the user controls, not internals ("Notifications," not "Webhook config").
86
- - Active voice; controls say what happens ("Save changes," not "Submit").
87
- - Same verb across button → toast → confirm ("Publish" / "Published").
88
- - Errors: what failed + how to fix; no vague apologies.
89
- - Empty states: invite the next action.
90
- - Plain verbs, sentence case, no filler; one job per element (label vs example).
91
- - Where the surrounding context already names the object, the visible label is one word and the full phrase rides in `aria-label` — the button reads "Stop", the accessible name says what it stops.
92
- - One glyph means one thing across the whole surface: a check that means "finished" here must not mean "selected" there.
86
+ - **Contrast, composited.** Read every pairing through a reader that composites the painted layers, in both themes ([bootstrap-reference.md](references/bootstrap-reference.md) → Measuring the bars).
87
+ - **Authored classes against the shipped cascade.** Extract every class authored in the templates and components, and fail the run on one that has no rule in the compiled CSS the page loads. Assert a population floor so an extractor that quietly matched nothing cannot pass, and control it with a class you know is absent.
88
+ - **One glyph, one meaning.** Register each status glyph against the meaning it carries. No meaning takes two glyphs, no glyph serves two meanings, and every registered glyph resolves in the icon set actually shipped.
93
89
 
94
90
  ---
95
91
 
96
92
  ## Bootstrap operating principles
97
93
 
98
- 1. **Mobile first** — smallest screen first, then `sm` / `md` / `lg` / `xl` / `xxl`
99
- 2. **Semantic HTML** — `nav`, `main`, `section`, heading order
100
- 3. **Work down the styling ladder below** — component classes, then utilities, then Bootstrap's own extension points
101
- 4. **Test responsiveness** — every breakpoint you claim
102
- 5. **Verify against the shipped cascade** — resolve every treatment in the CSS actually loaded (Bootstrap plus every skin and dependency stylesheet the page pulls in), never against docs memory. A class with no rule of its own may still inherit one, and a token pair that passes in stock Bootstrap can fail under a compatible skin. Measure, don't assume — including the `*-subtle` / `*-emphasis` recipes, once per theme.
94
+ 1. **Mobile first** — smallest screen first, then `sm` / `md` / `lg` / `xl` / `xxl`.
95
+ 2. **Semantic HTML** — `nav`, `main`, `section`, heading order.
96
+ 3. **Work down the styling ladder below** — component classes, then utilities, then Bootstrap's own extension points.
97
+ 4. **Test every breakpoint you claim.**
98
+ 5. **Reach for Bootstrap's own transitions before writing custom animation**, spend one orchestrated moment at most, and wrap any custom animation in `prefers-reduced-motion: no-preference` ([bootstrap-reference.md](references/bootstrap-reference.md) Reduced motion).
99
+ 6. **Resolve every treatment in the shipped cascade** — Bootstrap plus every skin and dependency stylesheet the page pulls in — never from docs memory. A class with no rule of its own may still inherit one, and a token pair that passes in stock Bootstrap can fail under a compatible skin. Measure the `*-subtle` / `*-emphasis` recipes too, once per theme, with a reader that composites the translucent layers ([bootstrap-reference.md](references/bootstrap-reference.md) → Measuring the bars).
103
100
 
104
101
  ### The styling ladder
105
102
 
106
- Work down these rungs in order. Reach a lower rung only when the one above genuinely cannot express the need.
103
+ Work down these rungs in order. Reach a lower rung only when the one above genuinely cannot express
104
+ the need.
107
105
 
108
- 1. **The component's own classes, in its documented structure.** Build the element the way Bootstrap defines it: the right elements, the right nesting, the right class names, the required ARIA. A card is `.card` wrapping `.card-body` wrapping `.card-title` not a `div` with borrowed padding. The best result is an element styled entirely by correct component classes, because variants, states, colour modes, and responsive behaviour all hang off that structure.
109
- 2. **Bootstrap utilities, for refinement.** Spacing, flex, display, sizing, text, borders, colour. This is where the creative range is — compose utilities rather than reaching past them. Use only classes that exist in [utilities.md](references/utilities.md); an invented one ships a silent no-op.
106
+ 1. **The component's own classes, in its documented structure.** Use the right elements, nesting, class names, and required ARIA: a card is `.card` wrapping `.card-body` wrapping `.card-title`, not a `div` with borrowed padding. Variants, states, color modes, and responsive behavior all hang off that structure.
107
+ 2. **Bootstrap utilities, for refinement.** Spacing, flex, display, sizing, text, borders, color. Compose utilities rather than reaching past them, and use only classes that exist in [utilities.md](references/utilities.md).
110
108
  3. **Bootstrap's own extension points.** Component `--bs-{component}-*` variables and the utilities API, when a real gap remains after rungs 1 and 2.
111
- 4. **Anything beyond Bootstrap's conventions is the developer's call, not yours.** Stop at rung 3, and say plainly what rung 4 would require. Leaving that decision with the developer keeps the surface conventional, reviewable, and themeable.
109
+ 4. **Anything beyond Bootstrap's conventions is the developer's call, not yours.** Stop at rung 3 and say plainly what rung 4 would require.
112
110
 
113
111
  Never open at rung 4. Specifically, do not reach first for:
114
112
 
@@ -116,138 +114,100 @@ Never open at rung 4. Specifically, do not reach first for:
116
114
  - a `<style>` block in a page or component;
117
115
  - a new stylesheet rule for something a utility already does.
118
116
 
119
- Each of those ends the cascade for that element: it outranks the utilities, it ignores `--bs-*` retheming, and it does not change across breakpoints or colour modes. Fighting utilities with high-specificity custom rules is the usual source of padding and margin cancel bugs.
117
+ Each ends the cascade for that element: it outranks the utilities, it ignores `--bs-*` retheming, and
118
+ it does not change across breakpoints or color modes.
120
119
 
121
120
  ### Hierarchy & actions
122
121
 
123
- | Intent | Typical choice |
124
- | ----------- | -------------------------------------------------------- |
125
- | Primary | `btn btn-primary` — **one** clear primary per region |
126
- | Secondary | `btn-outline-*` matching the surface |
127
- | Destructive | `btn-danger` + the confirmation ladder below |
128
- | Tertiary | `btn-link` or text links |
129
- | Status | `badge` / `alert` / `*-emphasis` — **never color alone** |
130
-
131
- A status mark with **no text** is an icon glyph, never a `badge`: stock Bootstrap ships `.badge:empty { display: none }`, so an empty badge used as a dot never renders at all ([components.md](references/components.md) → Badge).
122
+ | Intent | Typical choice |
123
+ | ----------- | ------------------------------------------------------------------- |
124
+ | Primary | `btn btn-primary` — **one** clear primary per region |
125
+ | Secondary | `btn-secondary` solid, so the surface underneath cannot change it |
126
+ | Destructive | `btn-danger` + the confirmation ladder |
127
+ | Tertiary | `btn-link` or text links |
128
+ | Status | `badge` / `alert` / `*-emphasis` — **icon + color + word** |
129
+
130
+ **Outline buttons are the decorative tier.** They paint no background of their own, so they borrow
131
+ whatever surface they sit on and their contrast is surface- and theme-dependent by construction:
132
+ against stock Bootstrap the whole `btn-outline-*` family misses 4.5:1 across the dark theme and on
133
+ light tinted surfaces — cards, subtle alerts. Give any action that carries information or
134
+ consequence the solid variant. Solid variants paint their own background and measure identically on
135
+ every surface, and the stock fills sit at the 4.5:1 bar with nothing to spare. Re-measure a solid
136
+ variant whenever anything layers over it — an `opacity-*` utility, a translucent overlay, a skin's
137
+ own tint.
138
+
139
+ A status mark with **no text** is an icon glyph, never a `badge`
140
+ ([components.md](references/components.md) → Badge).
132
141
 
133
142
  ### Surfaces, color, contrast
134
143
 
135
- - **Contrast bars, measured in both themes:** **≥ 4.5:1** for anything information-bearing — `small`, captions, and meta text included — and **≥ 3:1** for textless marks, state indicators, and the hover/focus chrome that carries state. Verify even Bootstrap's own palette; the docs admit some defaults fall short.
136
- - `text-body-tertiary` fails the AA bar for information-bearing small text tier such text `text-body-secondary` or better, and keep tertiary for genuinely decorative marks.
144
+ - **Contrast bars, measured in both themes:** **≥ 4.5:1** for anything information-bearing — `small`, captions, and meta text included — and **≥ 3:1** for textless marks, state indicators, and the hover/focus chrome that carries state. Verify Bootstrap's own palette too; the docs admit some defaults fall short. Read both themes — a pairing that passes light routinely fails dark.
145
+ - **Information-bearing status text takes the `-emphasis` pair.** Plain `text-success` and `text-danger` miss the bar across the dark theme and on light tinted surfaces, and `text-warning` is theme-asymmetric — unreadable on light, comfortable on dark. Never make a plain semantic color the encoding; use it only as decoration beside an encoding that already passes.
146
+ - `text-body-tertiary` carries no information anywhere: it measures under 4.5:1 on every surface in both themes. Tier text a user must read `text-body-secondary` or better, and keep tertiary for genuinely decorative marks.
147
+ - **A subtle fill degrades everything inside it one notch.** Inside `alert-*` and the `*-subtle` backgrounds, outline buttons and plain semantic text fail even in light — so information-bearing text there is `-emphasis` and every button is solid.
148
+ - **A primary fill destroys every semantic color.** On `.active`, `.bg-primary`, and `text-bg-*` surfaces every tone class measured lands under the bar in both themes, the `-emphasis` family included, because the fill supplies its own contrast color and the tone class overrides it with one tuned for a different background. Carry no tone class inside such a fill; let the surface's contrast color take the text, keep the status encoded by icon and word, and verify by capturing the selected state ([components.md](references/components.md) → Selection fills).
137
149
  - Disabled controls are exempt from the bars, but a disabled **destructive** control must not keep full danger saturation — at full strength it still reads as armed. Neutralize the variant while it is disabled and carry the reason on the control with `aria-describedby` (plus `title` for pointer users), never `title` alone.
138
- - Prefer `bg-body`, `bg-body-secondary`, `bg-body-tertiary` over raw `bg-white`/`bg-light` — they track `data-bs-theme`.
139
- - Pairings: `text-bg-*`, `*-subtle`, `*-emphasis`, `text-body` / `text-body-secondary`. (`text-muted` is deprecated — use `text-body-secondary`.)
140
- - On **dark surfaces**, scope `data-bs-theme="dark"` rather than reaching for the deprecated `*-dark` component variants; gray-on-dark outlines often fail contrast.
141
- - Support `data-bs-theme="light"` and `dark` when the product offers both — drive custom paint from `var(--bs-…)`. Mechanics: [bootstrap-reference.md](references/bootstrap-reference.md) → Color modes.
150
+ - Prefer `bg-body`, `bg-body-secondary`, `bg-body-tertiary` over raw `bg-white` / `bg-light`, and drive custom paint from `var(--bs-…)` — they track `data-bs-theme`, a hard-coded hex does not.
151
+ - Pairings: `text-bg-*`, `*-subtle`, `*-emphasis`, `text-body` / `text-body-secondary`. `text-muted` is deprecated — use `text-body-secondary`.
152
+ - On **dark surfaces**, scope `data-bs-theme="dark"` rather than the deprecated component variants `navbar-dark`, `dropdown-menu-dark`, `btn-close-white`, and `carousel-dark`; gray-on-dark outlines often fail contrast.
153
+ - Support `data-bs-theme="light"` and `dark` when the product offers both. Mechanics: [bootstrap-reference.md](references/bootstrap-reference.md) → Color modes.
142
154
 
143
155
  ### Density, layout, responsive
144
156
 
145
- - Enterprise density: `table-sm`, `btn-sm` / `btn-group-sm`, compact toolbars — but keep every interactive target **≥ 24×24px** (WCAG 2.2); pad hit areas rather than shrinking them.
146
- - Offer density (comfortable/compact) as a user toggle driven by one token, not ad-hoc `-sm` sprinkling.
147
- - Cards earn their keep: `.card` when grouping helps; otherwise spacing + type.
148
- - Prefer `gap-*` on parents over margin spam on every child.
149
- - Wide data: ranked responsive strategies (priority columns horizontal scroll card-ify) in [bootstrap-reference.md](references/bootstrap-reference.md) → Dense data tables.
150
- - Toolbars: `btn-toolbar` + `overflow-auto` — don't crush icons below target size.
151
- - Below `sm`, collapse captioned buttons to their icons (accessible names retained) **before** the brand or page title starts truncating — identity survives, chrome shrinks.
152
- - App frame: sidebar via responsive `offcanvas-{bp}` (inline desktop, drawer mobile) — full pattern in [bootstrap-reference.md](references/bootstrap-reference.md) → App shell.
153
- - Print: chrome `d-print-none`; keep the data printable.
154
-
155
- ### States & feedback (decision rules)
156
-
157
- - **Every data surface ships five states:** ideal, empty, loading, partial, error. Not done until all five exist.
158
- - **Loading:** skeleton (`placeholder`) when the layout is known — it holds the frame; spinner for short or in-control waits (inside the button). Under ~1s show nothing; past ~10s show determinate progress.
159
- - **Empty:** no-data-yet invites the first action; no-results-for-filters offers "Clear filters." Never one generic "nothing here."
160
- - **Errors:** what failed + how to fix + a keyboard-reachable retry, in place never a toast, never a wiped layout.
161
- - **Channel rule:** toast = transient success; inline alert = contextual; banner = persistent page-level condition; modal = blocking decision. Full matrix: [bootstrap-reference.md](references/bootstrap-reference.md) → Feedback discipline.
162
- - **Destructive ladder:** undo > confirm dialog (verb-labeled buttons, consequence restated) > type-to-confirm (only high blast radius). Prefer undoable over interrupting.
163
-
164
- ### Views & navigation
165
-
166
- - In-page view switching → real **tabs** (`nav-tabs` / `nav-pills` / `nav-underline` + tab panes / equivalent state), not scroll-only hash links dressed as tabs.
167
- - Active items: `aria-current="page"` or `aria-selected`. Exactly **one** `aria-current` per selection — per nav, per list, per table; two is no selection at all.
168
- - A mark laid on a selected/active fill must survive it: a `text-bg-*`-family mark on a `.active` fill of the same color is overridden and vanishes, while `text-body-emphasis` keeps reading. Same trap for `btn-check` filter labels ([components.md](references/components.md) → Selection fills).
169
- - Icon-only controls: `aria-label` (and `title` when helpful).
170
- - Breadcrumbs only for real hierarchy; command palette only on top of visible nav.
171
-
172
- ### Forms (decision rules)
173
-
174
- - Visible labels (top-aligned by default) or `.form-floating` — never placeholder-only.
175
- - Validate on **blur**, re-validate error fields on input, everything on submit; keep submit **enabled**.
176
- - Long forms pair a focusable error summary with inline `.invalid-feedback` per field (`aria-describedby`, `aria-invalid`).
177
- - Bootstrap's client-side validation styles aren't exposed to assistive tech — accessible flows use the server-side classes or native validation. Mechanics + canonical JS: [bootstrap-reference.md](references/bootstrap-reference.md) → Forms in production.
178
- - Units/money: `.input-group` + `.input-group-text` (+ `.has-validation` when feedback present).
179
- - Multi-step: per-step validation, back without loss, never re-ask collected data.
180
-
181
- ### Component cheat sheet
182
-
183
- | Problem | Start with |
184
- | ------------------- | ------------------------------------------------------------------------- |
185
- | App frame | `navbar` + responsive `offcanvas-{bp}` sidebar + `container-fluid` |
186
- | Page sections | spaced headings or `card` |
187
- | Actions | `btn`, `btn-group`, `btn-toolbar`, `d-grid gap-2` |
188
- | In-page views | `nav-tabs` / `nav-pills` / `nav-underline` + panes |
189
- | Data | `table` + `table-responsive` (+ patterns in the deep reference) |
190
- | Filters / secondary | `offcanvas`, `dropdown`, `collapse` |
191
- | Long help | `accordion` and/or scrollable `modal` |
192
- | Forms | `form-control`, `form-select`, `form-floating`, `input-group`, validation |
193
- | Feedback | `alert`, `toast`, `badge`, `progress`, `spinner`, `placeholder` |
194
- | Steps / schemas | `list-group` / `list-group-numbered` |
195
- | Value pickers | `form-select` / native inputs — never a dropdown posing as an input |
196
-
197
- Markup: [components.md](references/components.md). Fine layout: [utilities.md](references/utilities.md).
157
+ - Enterprise density: `table-sm`, `btn-sm` / `btn-group-sm`, compact toolbars — but keep every interactive target **≥ 24×24px**, measured on the rendered box rather than assumed from the class (WCAG 2.2); pad hit areas rather than shrinking them.
158
+ - Where information density is the screen's job, take the `-sm` family across a control row together `btn-sm` with `form-control-sm`, `form-select-sm`, `input-group-sm` — so the row shares one height. Never mix control sizes within one row.
159
+ - Cards earn their keep: `.card` when grouping helps; otherwise spacing and type.
160
+ - Swap conditional chrome in place. A bulk-action bar or an alert that shoves the toolbar down shifts the layout mid-task.
161
+ - App shell, dense tables, filter bars, and the ranked responsive strategies for wide data: [bootstrap-reference.md](references/bootstrap-reference.md) → Enterprise patterns. Spacing, toolbar, truncation, and print composition: [utilities.md](references/utilities.md) → Composition habits.
162
+
163
+ ### States & feedback
164
+
165
+ - **Every data surface ships five states:** ideal, empty, loading, partial, error. It is not done until all five exist. Loading thresholds, empty and error specifics, and the channel matrix for toast / inline alert / banner / modal: [bootstrap-reference.md](references/bootstrap-reference.md) The five states, Feedback discipline.
166
+ - **Build a blocking decision on the native `<dialog>`.** `showModal()` brings focus containment, Esc, an inert background, and top-layer stacking from the platform, with no instance to construct and none to leak on unmount. Dress it with Bootstrap chrome inside ([components.md](references/components.md) → Modal). Reach for `.modal` and its JS only when the project already drives its dialogs that way.
167
+ - **Destructive actions:** prefer undoable over interrupting. Ladder and confirmation contracts: [bootstrap-reference.md](references/bootstrap-reference.md) → Destructive actions.
168
+
169
+ ### Forms
170
+
171
+ - Give every field a visible label (top-aligned by default) or `.form-floating` never placeholder-only.
172
+ - Validate on **blur**, re-validate error fields on input, re-check everything on submit, and keep submit **enabled**. Never disable submit as a validation strategy.
173
+ - Pair a focusable error summary with inline `.invalid-feedback` per field (`aria-describedby`, `aria-invalid`).
174
+ - Layout, validation mechanics and their assistive-tech limitation, input groups, autosave, and multi-step rules: [bootstrap-reference.md](references/bootstrap-reference.md) Forms in production, Wizards & multi-step forms.
198
175
 
199
176
  ### When custom CSS is justified
200
177
 
201
- This is rung 4 of the styling ladder, so it is the developer's decision. Propose it, name what it buys, and do not take it unprompted. Exhaust rungs 1–3 first: correct component structure, then utilities, then the extension points — component `--bs-{component}-*` variables for restyling, the utilities API for missing utility steps ([bootstrap-reference.md](references/bootstrap-reference.md) → Theming).
202
-
203
- When the developer does authorize it:
204
-
205
- - Name in Bootstrap vocabulary
206
- - Colors from `var(--bs-…)` / theme tokens so light and dark both work
207
- - Logical properties (`margin-inline-start`, not `margin-left`) so RTL works
208
- - Minimal surface area; document why
209
- - A stylesheet rule, never a `style` attribute or a `<style>` block — those cannot be themed, overridden, or made responsive
210
-
211
- ### Anti-patterns
212
-
213
- - Templated "AI" looks when the brief left the axis free (see frontend-design)
214
- - Purple-gradient / glow / emoji / pill-soup decoration without subject reason
215
- - Faux widgets that break focus and theme behavior — a `div` pretending to be a select, grid, or tab
216
- - Hand-rolling combobox/datepicker/data-grid when native or an accessible library exists ([bootstrap-reference.md](references/bootstrap-reference.md) → When not to hand-roll)
217
- - Scroll anchors posing as tabs
218
- - Color-only status
219
- - An empty `.badge` used as a dot — `.badge:empty { display: none }` means it never rendered
220
- - Assuming an unfilled `.badge` is transparent — compatible skins may give it a default background; a muted badge states its fill (`bg-*-subtle`, `bg-transparent`)
221
- - A colored mark dropped onto a selected/active fill of the same family — the fill wins and the mark disappears
222
- - Disabled destructive controls left at full danger saturation
223
- - Treatments accepted from docs memory instead of the compiled cascade
224
- - Layout-shifting conditional chrome (bulk bars or alerts that shove the toolbar — swap in place)
225
- - Nested bordered divs instead of real `card` structure
226
- - Hard-coded hex that ignores `data-bs-theme`
227
- - Deprecated dark variants (`navbar-dark`, `dropdown-menu-dark`, `btn-close-white`, `carousel-dark`, `text-muted`) in new code
228
- - Inventing utility classes not in [utilities.md](references/utilities.md) — they fail silently
229
- - Disabling submit as a validation strategy; blocking paste in password/OTP fields
230
- - Errors delivered as toasts or hover tooltips
231
- - One infinite stack of pastel cards for documentation (prefer accordion/sections)
178
+ This is rung 4 of the styling ladder, so it is the developer's decision. Propose it, name what it
179
+ buys, and do not take it unprompted. Exhaust rungs 1–3 first: correct component structure, then
180
+ utilities, then the extension points component `--bs-{component}-*` variables for restyling, the
181
+ utilities API for missing utility steps ([bootstrap-reference.md](references/bootstrap-reference.md)
182
+ Theming).
183
+
184
+ When the developer authorizes it:
185
+
186
+ - Name it in Bootstrap vocabulary.
187
+ - Take colors from `var(--bs-…)` and theme tokens so light and dark both work.
188
+ - Use logical properties (`margin-inline-start`, not `margin-left`) so RTL works.
189
+ - Keep the surface area minimal and document why.
190
+ - Write a stylesheet rule, never a `style` attribute or a `<style>` block.
232
191
 
233
192
  ---
234
193
 
235
194
  ## Accessibility baseline
236
195
 
237
- - Skip link to main; landmarks; `h1` → `h2` order
238
- - `aria-label` on icon-only controls; targets ≥ 24×24px
239
- - `aria-current` / `aria-selected` on active nav/tabs — exactly one `aria-current` per selection
240
- - `aria-expanded` / `aria-controls` for disclosure
241
- - `aria-describedby` for help and errors; `aria-invalid` on failed fields
242
- - Live regions match the message: an async status mark is `role="status"`; an alert-styled notice is `role="alert"`
243
- - A form whose host already names the request associates with that name (`aria-labelledby`) instead of repeating the prompt as its own label
244
- - Visible focus (keep Bootstrap rings; `.focus-ring` helper for custom elements never `outline: none`)
245
- - Focus not obscured by sticky chrome (`scroll-margin-top`); focus moved deliberately on SPA route change, failed submit, and row delete
246
- - Meaning not by color alone; contrast verified
247
- - Drag interactions have a non-drag alternative
248
- - Modals: `aria-labelledby`; let Bootstrap trap/restore focus; dispose instances in SPAs on unmount
249
-
250
- WCAG 2.2 deltas, APG pattern contracts, reduced motion, and SPA focus recipes: [bootstrap-reference.md](references/bootstrap-reference.md) → Accessibility.
196
+ - Skip link to main; landmarks; `h1` → `h2` order.
197
+ - `aria-label` on icon-only controls; targets ≥ 24×24px.
198
+ - `aria-current` / `aria-selected` on active nav and tabs — exactly one `aria-current` per selection.
199
+ - `aria-expanded` / `aria-controls` for disclosure.
200
+ - `aria-describedby` for help and errors; `aria-invalid` on failed fields.
201
+ - Live regions match the message: an async status mark is `role="status"`; an alert-styled notice is `role="alert"`.
202
+ - A form whose host already names the request associates with that name (`aria-labelledby`) instead of repeating the prompt as its own label.
203
+ - Visible focus keep the Bootstrap rings, use the `.focus-ring` helper for custom elements, and never write `outline: none`.
204
+ - Focus not obscured by sticky chrome (`scroll-margin-top`); focus moved deliberately on SPA route change, failed submit, and row delete.
205
+ - Meaning never by color alone; contrast verified.
206
+ - Every drag interaction has a non-drag alternative.
207
+ - Dialogs carry `aria-labelledby`; let the platform or Bootstrap trap and restore focus rather than scripting it; dispose Bootstrap instances in SPAs on unmount.
208
+
209
+ WCAG 2.2 deltas, APG pattern contracts, reduced motion, and SPA focus recipes:
210
+ [bootstrap-reference.md](references/bootstrap-reference.md) → Accessibility.
251
211
 
252
212
  ---
253
213
 
@@ -255,31 +215,23 @@ WCAG 2.2 deltas, APG pattern contracts, reduced motion, and SPA focus recipes: [
255
215
 
256
216
  ```
257
217
  Progress:
258
- - [ ] Portability: followed project code law; no wrong-stack assumptions
218
+ - [ ] Project code law followed; no wrong-stack assumptions
259
219
  - [ ] Subject, audience, single job stated
260
- - [ ] Design plan: palette, type, layout, one signature (critiqued vs AI defaults)
261
- - [ ] Bootstrap shell from components.md; utilities from utilities.md (no invented classes)
262
- - [ ] Styling ladder held: component structure, then utilities, then extension points — no `style` attribute, no `<style>` block, no custom rule doing a utility's job
263
- - [ ] Plan tokens mapped to theme / --bs-* (no hex scatter); light/dark if both ship
264
- - [ ] Copy: user language, consistent verbs, useful empty/error/loading
220
+ - [ ] Design plan critiqued against the AI defaults: palette, type, layout, one signature
221
+ - [ ] Shell from components.md, utilities from utilities.md; no invented class
222
+ - [ ] Styling ladder held: no `style` attribute, no `<style>` block, no custom rule doing a utility's job
223
+ - [ ] Plan tokens mapped to theme / --bs-* (no hex scatter); light and dark both shipped where both are offered
224
+ - [ ] Copy in user language, verbs consistent, empty/error/loading text useful
265
225
  - [ ] Five states per data surface: ideal / empty / loading / partial / error
266
- - [ ] Contrast measured in both themes: ≥ 4.5:1 information-bearing (small included), ≥ 3:1 marks and state chrome; meaning not color-alone
226
+ - [ ] Contrast composited and measured in both themes: ≥ 4.5:1 information-bearing (small included), ≥ 3:1 marks and state chrome; meaning not color-alone
227
+ - [ ] Tiers held: `-emphasis` for information-bearing status, solid buttons for real actions, no tone class inside a filled surface
267
228
  - [ ] Every treatment resolved in the shipped cascade, not from docs memory
268
- - [ ] Keyboard: focus visible, not obscured, targets 24px, icon labels
269
- - [ ] Reduced motion respected; drag has non-drag alternative
229
+ - [ ] Authored classes checked against that cascade; one glyph per meaning; every instrument's control failed
230
+ - [ ] Keyboard: focus visible, not obscured, targets ≥ 24px, icon controls named
231
+ - [ ] Reduced motion respected; every drag has a non-drag path
270
232
  - [ ] Forms: labels visible, blur validation, error summary + inline, submit enabled
271
- - [ ] Responsive spot-check (claimed breakpoints); RTL-safe (start/end only)
272
- - [ ] States: hover/focus/disabled/invalid/active
273
- - [ ] SPA hygiene: JS instances disposed on unmount (or framework wrappers used)
274
- - [ ] Remove one unnecessary accessory
233
+ - [ ] Claimed breakpoints spot-checked; RTL-safe (start/end only)
234
+ - [ ] States present: hover / focus / disabled / invalid / active
235
+ - [ ] SPA hygiene: JS instances disposed on unmount, or framework wrappers used
275
236
  - [ ] Rendered proof: captures at both viewports and both themes + an accessibility snapshot
276
237
  ```
277
-
278
- ---
279
-
280
- ## Key takeaways
281
-
282
- - **Layered package** — craft and Bootstrap recipes live in the reference files; this file is the decision loop.
283
- - **Distinctive** = subject + one signature + restraint — in product UI, signature in the chrome, discipline in the data.
284
- - **Bootstrap** = reliable shipping across breakpoints, themes, and writing directions — extend through its variables and APIs, not against them.
285
- - **Done** = design critique **and** production states **and** the accessibility bar — each proven by what rendered, in any environment.