@starci/hfs 4.7.34 → 4.7.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 (54) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +8 -11
  3. package/codemod/button-intent.mjs +356 -0
  4. package/lint/run.mjs +70 -29
  5. package/package.json +3 -2
  6. package/runtime/knowledge/grammars/index.yaml +111 -0
  7. package/runtime/knowledge/grammars/offset-pop/playbook.yaml +302 -0
  8. package/runtime/knowledge/grammars/starci/playbook.yaml +1190 -0
  9. package/runtime/knowledge/hfs/canon-pins.yaml +22 -8
  10. package/runtime/knowledge/hfs/fe-stack.yaml +12 -4
  11. package/runtime/knowledge/hfs/rules.yaml +163 -83
  12. package/runtime/knowledge/hfs/slots.yaml +3 -3
  13. package/runtime/knowledge/ui/presentation/font.yaml +101 -0
  14. package/runtime/knowledge/ui/presentation/padding.yaml +217 -0
  15. package/runtime/modules/kernel/failure-codes.yaml +102 -20
  16. package/runtime/modules/kernel/removed-vocabulary.yaml +29 -0
  17. package/runtime/modules/kernel/roles.yaml +1 -1
  18. package/runtime/packages/cli/src/catalog.generated.mjs +119 -9
  19. package/runtime/scripts/gates/canon-pins.mjs +103 -0
  20. package/runtime/scripts/gates/gate.mjs +3 -36
  21. package/runtime/scripts/gates/output.mjs +40 -0
  22. package/runtime/scripts/gates/test-world-run.mjs +2 -4
  23. package/runtime/scripts/guards/release-definition.mjs +22 -3
  24. package/runtime/scripts/hfs/architecture/config.mjs +8 -8
  25. package/runtime/scripts/hfs/check.mjs +1 -1
  26. package/runtime/scripts/hfs/db-restamp-verb.mjs +2 -2
  27. package/runtime/scripts/hfs/declaration-shape.mjs +8 -2
  28. package/runtime/scripts/hfs/grammar-presentation.mjs +152 -0
  29. package/runtime/scripts/hfs/rule-catalog.mjs +2 -2
  30. package/runtime/scripts/hfs/rules/a15-findings.mjs +1 -0
  31. package/runtime/scripts/hfs/rules/fe-stack.mjs +44 -1
  32. package/runtime/scripts/hfs/slots.mjs +1 -0
  33. package/runtime/scripts/hfs/view.mjs +1 -0
  34. package/runtime/scripts/lib/canon-binding-text.mjs +129 -0
  35. package/runtime/scripts/lib/css-rules.mjs +18 -0
  36. package/runtime/scripts/lib/example-refs.mjs +1 -1
  37. package/runtime/scripts/lib/grammar-lint.mjs +48 -0
  38. package/runtime/scripts/lib/grammar-vendor-parity-jsx.mjs +360 -0
  39. package/runtime/scripts/lib/grammar-vendor-parity.mjs +110 -157
  40. package/runtime/scripts/lib/numeric-match.mjs +5 -0
  41. package/runtime/scripts/lib/release-version.mjs +119 -0
  42. package/runtime/scripts/lib/runtime-publication-tags.mjs +3 -46
  43. package/runtime/scripts/lib/source-package-imports.mjs +111 -0
  44. package/runtime/scripts/work/draw/draw-layer.mjs +5 -4
  45. package/scaffold/app.mjs +7 -5
  46. package/src/main.mjs +30 -15
  47. package/stack/config.mjs +2 -1
  48. package/stack/render.mjs +24 -3
  49. package/sync/index.mjs +8 -2
  50. package/templates/app/skeleton/.starcistacks/dev/infra/compose/stack.yaml +0 -1
  51. package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +0 -1
  52. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +0 -1
  53. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +0 -1
  54. package/templates/fe/tool-config/eslint.config.mjs +4 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starci/hfs",
3
- "version": "4.7.34",
3
+ "version": "4.7.36",
4
4
  "description": "The StarCi app command implementation for scaffolding, linting, checking and synchronizing one product repository.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -39,6 +39,7 @@
39
39
  "db/**",
40
40
  "ci/**",
41
41
  "coverage/**",
42
+ "codemod/**",
42
43
  "lint/**",
43
44
  "scaffold/**",
44
45
  "report/**",
@@ -50,7 +51,7 @@
50
51
  "CHANGELOG.md"
51
52
  ],
52
53
  "engines": {
53
- "node": ">=22.13.0"
54
+ "node": ">=22.15.0"
54
55
  },
55
56
  "publishConfig": {
56
57
  "access": "public"
@@ -0,0 +1,111 @@
1
+ schema: starci/knowledge-source@1
2
+ id: grammars
3
+ title: Grammar families
4
+ purpose: |
5
+ Catalog of authored UI Grammar families. Inventory only — not a default-family rule.
6
+ The selected design specification names the applicable installed family. The app binds that choice
7
+ explicitly in sides.fe.grammar; the catalog resolves its lint package and component contract.
8
+ A missing or ambiguous family never silently resolves to a family. DNA snapshots describe the
9
+ observed implementation; they do not install packages or prove current rendered behavior.
10
+ appliesTo:
11
+ - grammar
12
+ provenance:
13
+ limitations: |
14
+ Component names, tokens and gap tables describe the reference family at observation time.
15
+ Verify actual package identity/version, exported types, CSS and renderer source in the selected
16
+ repository before using snapshot APIs or gaps. Record current source references in the selected
17
+ .starciwork node; an old source census is not current UAT evidence. The current public consumer
18
+ entry observed for FE work is @starci/grammar/common — confirm installed exports before import.
19
+ The `common` entry is a DNA snapshot of that semantic entry and nothing more: it declares no palette,
20
+ no family boundary and no composition playbook, because the entry itself ships none of them.
21
+ guidance:
22
+ - id: catalog-not-default
23
+ requirement: |
24
+ Do not treat this catalog as selecting a default Grammar family. Family choice belongs to the
25
+ current design specification and its explicit sides.fe.grammar declaration.
26
+ rationale: |
27
+ Silent fallback to StarCi would invent product direction and override an absent/ambiguous design.
28
+ - id: verify-before-use
29
+ requirement: |
30
+ Before applying snapshot APIs or recorded gaps, inspect the selected repository's package
31
+ identity/version, exported types, CSS and renderer source.
32
+ rationale: |
33
+ Snapshots are historical; installed exports may differ. Inventing missing APIs is forbidden.
34
+ - id: ui-owns-universal
35
+ requirement: |
36
+ Common semantics, renderer anatomy, state and accessibility remain owned by knowledge/ui.
37
+ A family owns its scoped visual realization and promoted idioms; the product owns its facts,
38
+ content, routes and approved composition.
39
+ Apply FE-RULE-OWNERSHIP in knowledge/patterns/fe/index.yaml when placing requirements and checks.
40
+ rationale: |
41
+ Keeps universal UI law separate from family-scoped DNA and product composition.
42
+ topics:
43
+ - id: common
44
+ path: common/DNA.yaml
45
+ title: Common Grammar — DNA
46
+ summary: |
47
+ Generated DNA snapshot of the `@starci/grammar/common` public entry: custom properties,
48
+ the 110 renderers of its registry, their classes, stamped rule ids and recorded gaps. The DNA owns the observed package
49
+ version and source inventory. Common supplies anatomy, not colour. The snapshot is drift-checked.
50
+ - id: starci
51
+ path: starci/index.yaml
52
+ title: StarCi Core Grammar
53
+ summary: |
54
+ Historical snapshot of the StarCi Core family — DNA, idioms, playbook (including composing the
55
+ 0.5.0 forms, overlays and navigation chrome) and family boundaries.
56
+ - id: offset-pop
57
+ path: offset-pop/index.yaml
58
+ title: Offset Pop Grammar
59
+ summary: |
60
+ Snapshot of the product-neutral Offset Pop family — cream canvas, ink outline plus hard offset
61
+ shadow, one pink decision accent with a text-safe accent for text — with DNA, idioms, a
62
+ mobile-first playbook for playful learning or consumer apps, and family boundaries including two
63
+ decisions flagged for the family owner.
64
+ - id: heritage
65
+ path: heritage/DNA.yaml
66
+ title: Heritage Grammar
67
+ summary: |
68
+ Generated snapshot of the Heritage family's parity declaration, arrangement, scoped tokens and Common renderers.
69
+ families:
70
+ - id: heritage
71
+ title: heritage
72
+ contractRef: common
73
+ packageSnapshot: heritage/DNA.yaml
74
+ - id: common
75
+ title: common
76
+ componentContract:
77
+ package: '@starci/grammar'
78
+ entry: '@starci/grammar/common'
79
+ styleEntry: '@starci/grammar/common.css'
80
+ peers: [react, '@heroui/react', '@xyflow/react']
81
+ packageSnapshot: common/DNA.yaml
82
+ scope: |
83
+ DNA snapshot only. This catalog carries no Common-side family boundary, idiom set or composition
84
+ playbook: `@starci/grammar/common` is the semantic/props entry every visual family is defined over
85
+ (`defineGrammarFamily`), not a family that ships a palette, so `authority`, `reusableStyle`,
86
+ `productComposition` and `index` have nothing to name here. Universal semantics, renderer anatomy,
87
+ state and accessibility remain owned by knowledge/ui.
88
+ - id: starci
89
+ title: starci
90
+ contractRef: common
91
+ lint:
92
+ package: '@starci/eslint-grammar-starci'
93
+ factory: starciGrammarConfig
94
+ prefix: starci-grammar
95
+ authority: starci/family.yaml
96
+ packageSnapshot: starci/DNA.yaml
97
+ reusableStyle: starci/idioms.yaml
98
+ productComposition: starci/playbook.yaml
99
+ index: starci/index.yaml
100
+ - id: offset-pop
101
+ title: offset-pop
102
+ contractRef: common
103
+ lint:
104
+ package: '@starci/eslint-grammar-offset-pop'
105
+ factory: offsetPopGrammarConfig
106
+ prefix: starci-grammar
107
+ authority: offset-pop/family.yaml
108
+ packageSnapshot: offset-pop/DNA.yaml
109
+ reusableStyle: offset-pop/idioms.yaml
110
+ productComposition: offset-pop/playbook.yaml
111
+ index: offset-pop/index.yaml
@@ -0,0 +1,302 @@
1
+ schema: starci/knowledge-source@1
2
+ id: grammar.offset-pop.playbook
3
+ title: Offset Pop — playbook
4
+ purpose: |
5
+ How a playful, mobile-first learning or consumer app is composed from the renderers Offset Pop
6
+ publishes: which renderers a business shape asks for, in what order, with which props, and the rules
7
+ every Offset Pop app follows (one root, one pink per decision, thumb-sized targets, a bottom-sheet
8
+ Drawer for secondary tasks, a navigation drawer at phone widths). Product-neutral: the shapes name no product,
9
+ and every label below is a role, not copy. A shape not listed here may still be composed from Common
10
+ semantics and family anatomy for an approved product direction.
11
+ appliesTo:
12
+ - grammar
13
+ family: offset-pop
14
+ provenance:
15
+ package: "@starci/grammar"
16
+ version: "0.5.0"
17
+ checkout: b6a13be97a9d1e15b7d3f628709158e81c744a2a
18
+ captured: "2026-09-23"
19
+ publicEntry: "@starci/grammar/common"
20
+ familyEntry: "@starci/grammar/offset-pop"
21
+ limitations: |
22
+ Use this playbook only when Offset Pop is selected in the current .starciwork specification. The
23
+ shapes are package usage written from the family's source, not observations of a shipped product;
24
+ verify installed exports, props and CSS before promising a capability, and do not invent Grammar
25
+ APIs absent from DNA.yaml or installed source. Reading steps are reasoning within one op, not an
26
+ auto-running op chain.
27
+ shapes:
28
+ - id: app-shell
29
+ title: App shell — fixed frame, navbar, sidebar drawer, one Toaster
30
+ knowledge: knowledge/ui/composition/app-shell.yaml
31
+ composition:
32
+ - renderer: OffsetPopGrammarRoot
33
+ role: |
34
+ The one root around the whole app, `theme="system"` unless the product fixes a theme; imports
35
+ `@starci/grammar/offset-pop/styles.css`
36
+ - renderer: WorkspaceShell
37
+ role: |
38
+ `viewport="fixed"` owns the frame, navbar, mobile opener band and independent sidebar and
39
+ content scroll. Compose the regions required by FE-APP-SHELL-1 through FE-APP-SHELL-7.
40
+ - renderer: TopBar
41
+ role: |
42
+ Identity, search and the ordered language, theme, notifications and account controls follow
43
+ the app-shell contract. Every bar control uses `size="control"`; Grammar owns its geometry.
44
+ - renderer: Sidebar
45
+ role: |
46
+ The desktop navigation and its icon rail use `presentation="rail"`. The route owns
47
+ `selectedKey`; the product owns permissions, groups and collapse persistence.
48
+ - renderer: Drawer
49
+ role: |
50
+ The mobile opener below the navbar opens a left drawer containing Sidebar
51
+ `presentation="drawer"`, plus language and theme controls. Selection, backdrop and Escape
52
+ dismiss it; focus stays inside while it is open (FE-APP-SHELL-4).
53
+ - renderer: PageContainer
54
+ role: |
55
+ Route content lives in the shell's primary region. Create actions belong on their pages
56
+ (FE-APP-SHELL-5); app classes express layout only.
57
+ - renderer: Toaster
58
+ role: |
59
+ Once, inside the root, with `label` and `dismissLabel`; its placement keeps notifications
60
+ clear of the shell controls and the current task.
61
+ notes: |
62
+ FE-RULE-OWNERSHIP in knowledge/patterns/fe/index.yaml keeps shell semantics in universal
63
+ knowledge and the named API checks in the selected Grammar lint package. The app shell has no
64
+ bottom tab bar. Offset Pop owns the outline, palette and hard shadow in its packaged CSS.
65
+
66
+ - id: home-continue
67
+ title: Home — where the reader left off, one next step
68
+ referenceMayContribute: |
69
+ Keep: which sections exist and which one is the next step. Never crosses over: layout grid, card
70
+ chrome, or a section that would need a renderer the family does not publish.
71
+ composition:
72
+ - renderer: SectionHeader
73
+ role: The greeting or section title; its eyebrow reads `accentText`, never the pink
74
+ - renderer: SurfaceCard
75
+ role: |
76
+ One top-level sticker for the next step (`isHighlight` on that card only), holding the title
77
+ and one supporting line (Text size="sm" tone="muted"), a real measurement, and one
78
+ Button variant="primary" — the view's single pink fill
79
+ - renderer: "Progress / ProgressCircle / Meter"
80
+ role: Only a verified value with a truthful `label` (ACCENT-4); an unknown value is not drawn
81
+ - renderer: SurfaceListCard
82
+ role: Peers (recent items, suggestions) as static rows; no second primary Button
83
+ - renderer: "Skeleton / EmptyNotice"
84
+ role: Pending is the same tree at rest (Skeleton in place of content); empty or failed is EmptyNotice
85
+ notes: |
86
+ A streak, level or reward count is a Badge or a Meter, not a second pink call to action.
87
+
88
+ - id: practice-step
89
+ title: One question per screen — choose, check, see the outcome
90
+ composition:
91
+ - renderer: Progress
92
+ role: How far through the set the reader is, from a real count
93
+ - renderer: Heading
94
+ role: The prompt; `scale="display"` only for a single short prompt
95
+ - renderer: "RadioGroup / CheckboxGroup / SegmentedControl"
96
+ role: |
97
+ The answer choices as the control itself (each choice is a full-width, 44px-plus target with
98
+ its own label); `isInvalid`/`errorMessage` only for a validation problem, never for a wrong answer
99
+ - renderer: Button
100
+ role: |
101
+ One variant="primary" type="submit" ("check" in the product's words), full width at the bottom
102
+ of the content, `isDisabled` until a choice exists — the layout does not move when it enables
103
+ - renderer: Alert
104
+ role: |
105
+ The outcome in place, above the action: `tone="affirmative"` (mint) or `tone="negative"`
106
+ (critical), its single `action` for the next step; the tone is the outcome, never decoration
107
+ - renderer: Drawer
108
+ role: |
109
+ `placement="bottom"` for an optional explanation or hint the reader opens; the handle is
110
+ decorative, the close button and Escape are the ways out
111
+ notes: |
112
+ Feedback that must not interrupt (a streak kept, an item saved) is a Toast, not an Alert; feedback
113
+ the reader must act on stays in the page.
114
+
115
+ - id: result-celebration
116
+ title: Result — celebrate a finished set, offer one way on
117
+ composition:
118
+ - renderer: SurfaceCard
119
+ role: The result sticker, `isHighlight`
120
+ - renderer: Heading
121
+ role: "`scale=\"display\"` (weight 900, tight tracking) for the one headline"
122
+ - renderer: "ProgressCircle / Meter / DescriptionList"
123
+ role: The score and its facts, each a real value with a label
124
+ - renderer: Button
125
+ role: One variant="primary" onward; a secondary Button or TextAction to review
126
+ - renderer: Toast
127
+ role: An earned reward announced politely (`toastQueue.add` with `tone="affirmative"`)
128
+ notes: |
129
+ Celebration is the display headline, the mint outcome and the sticker's press; the family adds no
130
+ confetti, and motion stays press travel only (removed under reduced motion).
131
+
132
+ - id: bottom-sheet-task
133
+ title: Secondary task on a phone — filters, pickers, details, quick edit
134
+ composition:
135
+ - renderer: Drawer
136
+ role: |
137
+ `placement="bottom"` below 48rem (upward hard shadow, grab handle), `placement="right"` on wide
138
+ layouts; `title` names the task, `footer` holds one primary Button (a render function receives
139
+ `close`)
140
+ - renderer: "Select / DatePicker / TagGroup / Switch"
141
+ role: The controls inside; their own popovers stay inside the root and the sheet
142
+ notes: |
143
+ One secondary-task sheet opens at a time. The shell navigation follows FE-APP-SHELL-4's left
144
+ drawer and preserves its own navigation and dismissal behavior.
145
+
146
+ - id: sign-up-and-onboarding
147
+ title: Sign-up or onboarding — short groups, one submit per step
148
+ composition:
149
+ - renderer: Stepper
150
+ role: Where the reader is in a multi-step flow (pink current, mint complete, critical error)
151
+ - renderer: Form
152
+ role: "`label`, `onSubmit(FormData)`, `validationErrors` by field `name`, `isPending`"
153
+ - renderer: Fieldset
154
+ role: One per named group
155
+ - renderer: "Select / DateField / Checkbox / Switch / RadioGroup / Textarea"
156
+ role: Each with its own `label`, `description`, `errorMessage`, `isRequired`, `name`
157
+ - renderer: Field
158
+ role: Only for a control the package does not publish (OtpInput, a third-party widget)
159
+ - renderer: Button
160
+ role: One variant="primary" type="submit" per step
161
+ notes: |
162
+ Field focus is the hard offset shadow (critical while invalid); do not add an app focus ring.
163
+
164
+ - id: settings
165
+ title: Settings — immediate toggles, grouped, destructive actions confirmed
166
+ composition:
167
+ - renderer: Fieldset
168
+ role: One per group of settings
169
+ - renderer: "Switch / Select / SegmentedControl"
170
+ role: A Switch applies immediately; confirm the change with a polite Toast, not a dialog
171
+ - renderer: AlertDialog
172
+ role: |
173
+ For an irreversible action only: `tone="negative"` (the confirm fills critical, not pink),
174
+ `confirmLabel`/`cancelLabel` in the product's words; initial focus lands on cancel
175
+ - renderer: Toast
176
+ role: Undo for a reversible action (`action` keeps the toast until dismissed)
177
+
178
+ - id: browse-and-pick
179
+ title: Browse a catalogue — search, filter, pick
180
+ composition:
181
+ - renderer: SearchField
182
+ role: The query, labelled
183
+ - renderer: TagGroup
184
+ role: Filters as outlined chips; selected chips turn pink and lift
185
+ - renderer: "ListBox / SurfaceListCard"
186
+ role: |
187
+ ListBox when picking is the task (yellow hover wash, pink focus ring, soft-pink selected row
188
+ with a pink leading edge); SurfaceListCard when rows only report facts
189
+ - renderer: Pagination
190
+ role: Current page is the raised pink pill
191
+ - renderer: "Skeleton / EmptyNotice"
192
+ role: Pending keeps the list shape; empty and failed are EmptyNotice
193
+
194
+ readingSteps:
195
+ - id: business-shape-first
196
+ order: 1
197
+ title: Business shape first
198
+ instruction: |
199
+ Name the shape and the one outcome the screen exists to produce. That outcome is the view's single
200
+ pink fill; everything else is ink on cream.
201
+ - id: then-reference
202
+ order: 2
203
+ title: Then the reference, if the request supplied one
204
+ instruction: |
205
+ A reference contributes region order, which decision dominates, and what folds into a bottom
206
+ sheet. It never contributes palette, outline, shadow or component anatomy — those are this
207
+ family's and knowledge/ui's.
208
+ - id: then-compose
209
+ order: 3
210
+ title: Then the shape's composition, mobile first
211
+ instruction: |
212
+ Compose the shape's renderers at phone width first: the shell's left navigation drawer,
213
+ bottom-sheet secondary tasks and a full-width primary action. The published responsive shell
214
+ switches to its sidebar and the task drawer to its wide presentation.
215
+ - id: compare-dna
216
+ order: 4
217
+ title: Compare DNA with actually installed source
218
+ instruction: |
219
+ Confirm named renderers, props and closed values against DNA.yaml and installed exports, and read
220
+ family.yaml's gaps and owner decisions before promising a capability.
221
+ - id: resolve-product-composition
222
+ order: 5
223
+ title: Then resolve the remaining product composition
224
+ instruction: |
225
+ Reuse a shape where it fits; otherwise compose existing Common semantics and family anatomy. A
226
+ shape absent from this table is an evidence limit on house style, not a reason to stop work
227
+ already inside the approved product scope.
228
+
229
+ authoritySplit:
230
+ directionChoiceRequired: |
231
+ DIRECTION_CHOICE_REQUIRED is reserved for a material product choice whose acceptable options lead to
232
+ different outcomes and whose answer is absent from the request. Product facts, copy, mascot and
233
+ assets come from the product; Common semantics from knowledge/ui; outline, shadow, palette and
234
+ reusable treatments from this family. The two decisions flagged for the family owner in family.yaml
235
+ (BOUNDARY-6 departure, single pink) are not the product's to change.
236
+
237
+ guidance:
238
+ - id: one-root-overlays-inside
239
+ requirement: |
240
+ Render everything — chrome, content, every overlay trigger (Dialog, AlertDialog, Drawer, Popover,
241
+ DropdownMenu, the Select/ComboBox/DatePicker fields) and the Toaster — inside one
242
+ `OffsetPopGrammarRoot`. Overlays portal into the nearest Grammar root; a trigger outside it opens a
243
+ plain, unstyled overlay in `<body>` with no outline, no offset, no dark palette.
244
+ rationale: |
245
+ The family is a scope on the root; only its DOM descendants are Offset Pop.
246
+ - id: one-pink-per-view
247
+ requirement: |
248
+ Give each view at most one pink-filled action (Button variant="primary", or the single confirm /
249
+ toast / alert action the family fills). Text that must carry the accent uses Text tone="accent"
250
+ (which reads `accentText`); never set the pink as a text colour.
251
+ rationale: |
252
+ Pink as body text is 3.22:1 on the canvas; one fill per view is what makes the decision obvious in
253
+ a family where selection, current and focus share the same pink.
254
+ - id: big-touch-targets
255
+ requirement: |
256
+ Keep the published sizes: Common's 44px floor for controls, the Subnav toggle and the TopBar
257
+ trigger, and `size="control"` throughout the navbar. Do not shrink them with app classes,
258
+ and put answer choices in RadioGroup/CheckboxGroup/SegmentedControl rather than small
259
+ custom chips.
260
+ rationale: |
261
+ A playful, thumb-driven app is used one-handed; the family's targets are part of its idiom.
262
+ - id: bottom-sheet-for-secondary
263
+ requirement: |
264
+ On a phone, secondary tasks open in `Drawer placement="bottom"`; reserve `Dialog` for a short task
265
+ that must block the page and `AlertDialog` for an irreversible decision.
266
+ rationale: |
267
+ The bottom sheet keeps the task within thumb reach and throws its shadow upward into the page.
268
+ - id: toaster-clear-of-content
269
+ requirement: |
270
+ One Toaster stays inside the Grammar root. Its placement keeps feedback clear of the shell
271
+ controls and the task's primary action, including while the navigation drawer is open.
272
+ rationale: |
273
+ Feedback remains visible without covering the control needed to complete the current task.
274
+ - id: no-extra-chrome
275
+ requirement: |
276
+ Do not add app-level borders, blur shadows, gradients or a second accent colour to family
277
+ renderers; do not restyle the palette per screen. Adapt the family only through the
278
+ `OFFSET_POP_TOKEN_NAMES` custom properties on the root.
279
+ rationale: |
280
+ The outline, the hard offset and the one pink are the family; an extra layer turns stickers into
281
+ a different, noisier family and breaks the dark and forced-colours mappings.
282
+ - id: product-neutral
283
+ requirement: |
284
+ Keep product names, copy, mascots and illustration inside the product. The family and this
285
+ playbook name roles only.
286
+ rationale: |
287
+ Offset Pop is a shared family; its first consumer does not own it.
288
+
289
+ presentation:
290
+ font:
291
+ ownership: FE-RULE-OWNERSHIP
292
+ inherits: starci
293
+ contract: |
294
+ Offset Pop keeps Common typography renderers and props. Its font proof reads the shared Common
295
+ rank-to-renderer recipes in the StarCi playbook; the Offset Pop stylesheet owns its family palette
296
+ and display treatment.
297
+ padding:
298
+ ownership: FE-RULE-OWNERSHIP
299
+ inherits: starci
300
+ contract: |
301
+ Offset Pop keeps the same Common spacing and component geometry. Its padding proof reads the
302
+ shared Common recipes declared in the StarCi playbook; it supplies no replacement inset scale.