@jay-framework/jay-stack-cli 0.24.6 → 0.24.7

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 (26) hide show
  1. package/agent-kit-template/INSTRUCTIONS.md +24 -1
  2. package/agent-kit-template/contracts/GUIDE.md +1 -1
  3. package/agent-kit-template/contracts/component-contracts.md +42 -12
  4. package/agent-kit-template/designer/INSTRUCTIONS.md +23 -20
  5. package/agent-kit-template/designer/cli-commands.md +46 -0
  6. package/agent-kit-template/designer/contracts-and-plugins.md +30 -1
  7. package/agent-kit-template/designer/design-system-guide.md +535 -0
  8. package/agent-kit-template/designer/jay-html-components.md +101 -31
  9. package/agent-kit-template/designer/jay-html-styling.md +69 -0
  10. package/agent-kit-template/designer/jay-html-syntax.md +22 -13
  11. package/agent-kit-template/designer/jay-html-template-syntax.md +3 -3
  12. package/agent-kit-template/designer/project-structure.md +1 -1
  13. package/agent-kit-template/designer/routing.md +2 -2
  14. package/agent-kit-template/designer/validation-guide.md +31 -5
  15. package/agent-kit-template/developer/INSTRUCTIONS.md +24 -19
  16. package/agent-kit-template/developer/page-components.md +23 -2
  17. package/agent-kit-template/developer/project-structure.md +1 -1
  18. package/agent-kit-template/developer/routing.md +1 -1
  19. package/agent-kit-template/devops/production-build.md +1 -1
  20. package/agent-kit-template/plugin/INSTRUCTIONS.md +3 -1
  21. package/agent-kit-template/plugin/component-structure.md +1 -1
  22. package/agent-kit-template/plugin/contracts-guide.md +18 -0
  23. package/agent-kit-template/plugin/design-system-guide.md +173 -0
  24. package/dist/{index-B9vCxAZ1.js → index-l6QVpvnY.js} +19 -14
  25. package/dist/index.js +1903 -199
  26. package/package.json +13 -12
@@ -0,0 +1,535 @@
1
+ # Design-System Elements (Materialise, Drift, Sync)
2
+
3
+ A **design-system element** is a component whose template you flatten (copy) into your page and then own
4
+ the copy of. You edit the copy freely; `jay-stack validate` tells you where your copy has drifted from the
5
+ source; `jay-stack sync` re-flattens from the current source while keeping the edits you marked. This is
6
+ Jay's answer to "reuse a shared component but tweak this one instance" — with no runtime cost and no hidden
7
+ composition machinery.
8
+
9
+ **As a designer you both _create_ and _use_ design systems — this is your job, not only a plugin author's.**
10
+ A design-system element can be one you author from scratch, one you copy from a plugin into your project and
11
+ adapt, or one you link straight from a plugin. This guide covers **both sides**: [creating a design-system
12
+ element](#creating-a-design-system-element) (authoring the template + contract) and [using
13
+ one](#using-a-design-system-element-link--flatten) (linking, flattening, editing, syncing).
14
+
15
+ > Replaces the old `<override>` tag. If you have seen `<override ref="…">` in older pages, that mechanism
16
+ > is gone — its job is now done by editing the flattened copy and marking the edited **facet** (see
17
+ > [Marking what you own](#marking-what-you-own-facets)).
18
+
19
+ ## The three component models — only one is a design-system element
20
+
21
+ | Model | How it is imported | Template? | Validate / sync? |
22
+ | ------------------------- | -------------------------------------------------- | --------- | ---------------- |
23
+ | **Keyed headless** | `<script … key="cart">`, used via `{cart.field}` | No | No |
24
+ | **Nested (no template)** | `<jay:card>` with an inline body you wrote by hand | No | No |
25
+ | **Design-system element** | `<jay:card>` whose import carries `template="…"` | **Yes** | **Yes** |
26
+
27
+ Only a `<jay:X>` region whose `application/jay-headless` import carries a `template=` attribute is a
28
+ design-system element. That `template=` is the **provenance marker**: it records where the flattened body
29
+ came from, so validate can diff it and sync can re-flatten it. Nothing else — a keyed import, a nested
30
+ region you hand-authored, a plain instance — is ever drift-checked or synced.
31
+
32
+ ## Prefer design-system elements
33
+
34
+ Reuse UI through design-system elements, not by hand-authoring the same markup in each page. Three
35
+ preferences — `jay-stack validate` nudges you toward each (warnings, never blocking, each suppressible):
36
+
37
+ 1. **Create _and_ consume templates as design-system elements.** When a component renders UI, ship a
38
+ `.jay-html` template for it, and link it from the page with `template=` + `jay-stack sync` rather than
39
+ hand-writing the `<jay:X>` body. (Rules: `COMPONENT-NO-TEMPLATE` on the authoring side,
40
+ `REGION-NOT-LINKED` on the consuming side.)
41
+ 2. **When one instance needs extensive restyling or structural change, make _another_ template** for the same
42
+ component — a second design-system variant — instead of piling style/class/structure overrides onto one
43
+ flattened copy. (Rule: `REGION-OVERRIDE-NON-CONTENT`.)
44
+ 3. **Hand-author an inline region only for a genuinely one-off usage** — a region used once, where no shared
45
+ template exists or would help.
46
+
47
+ ### Two templates vs. a conditional — different axes
48
+
49
+ These solve different problems; do not substitute one for the other:
50
+
51
+ - **Two templates = two design-system elements.** Different _designs_ chosen **at composition time** (a
52
+ compact card vs. a feature card). You pick which template a region links to. This is the axis
53
+ `REGION-OVERRIDE-NON-CONTENT` points you to when an instance's _look or structure_ diverges.
54
+ - **A conditional (`if` / variant) = one element, runtime change.** Same design, a branch driven by
55
+ **runtime state** (logged-in vs. not). Use conditionals for state, not to fork a design.
56
+
57
+ A restyle or net-new layout DOM on one instance is a _second design_ — reach for a second template, not a
58
+ conditional.
59
+
60
+ ### What counts as drift worth a new variant
61
+
62
+ `REGION-OVERRIDE-NON-CONTENT` fires only on **non-content** changes to a linked region:
63
+
64
+ - **Content (fine — this is what flattening is for):** editing text, image `src`/`alt`, and enriching text
65
+ with inline content markup (`<strong>`, `<span>`, `<img>`, a `<ul>`/`<li>` list, a `<table>`, headings…).
66
+ - **Non-content (make a new variant):** changing `class` or inline/scoped CSS, or adding net-new layout DOM
67
+ (`<div>`/`<section>` wrappers, custom components) — a different look or structure.
68
+
69
+ ## Creating a design-system element
70
+
71
+ Authoring a design-system element is just authoring a **component**: a `.jay-contract` (its data shape,
72
+ props, refs) and a `.jay-html` template (its markup + optional `<style>`). You can start it three ways:
73
+
74
+ - **From scratch** — write the contract and template yourself for a reusable piece of your UI (a card, a
75
+ hero, a callout).
76
+ - **From a plugin, copied in** — copy a plugin's shipped `.jay-html` (and its `.jay-contract`) into your own
77
+ `src/components/…`, then adapt it. Point pages at _your_ copy; it is now project-owned, so you edit the
78
+ source directly — no `override` marks and no upstream `sync`.
79
+ - **From an existing project component** — add a `.jay-html` template to a component that only had a
80
+ contract, so pages can flatten it.
81
+
82
+ ### 1. Put it under `src/components/`
83
+
84
+ A project-owned element lives beside your pages:
85
+
86
+ ```
87
+ src/components/card/
88
+ card.jay-contract # data shape, props, refs
89
+ card.jay-html # the template pages flatten from
90
+ ```
91
+
92
+ (A plugin's element lives in its package; you link it by a `template=` path into `node_modules/…`. Same
93
+ mechanics — only the path differs.)
94
+
95
+ ### 2. Write the contract
96
+
97
+ The contract is the component's API — the props an instance is configured with and the tags the template
98
+ binds. Keep it to what varies per use. See the [Contract Authoring Guide](../contracts/GUIDE.md).
99
+
100
+ ```yaml
101
+ # card.jay-contract
102
+ name: Card
103
+ props:
104
+ - { name: heading, type: string, required: true }
105
+ - { name: body, type: string, required: true }
106
+ tags:
107
+ - { tag: heading, type: data, dataType: string }
108
+ - { tag: body, type: data, dataType: string }
109
+ ```
110
+
111
+ **A pure presentational element can have an (almost) empty contract.** If the element binds no data and
112
+ needs no code — static markup and styles only — the contract still has to exist (it names the element and is
113
+ what the page's `contract=` import resolves), but it needs **nothing but a name**:
114
+
115
+ ```yaml
116
+ # hero-banner.jay-contract — a pure design element, no data, no code
117
+ name: HeroBanner
118
+ tags: [] # no data to bind
119
+ ```
120
+
121
+ ### 3. Write the template
122
+
123
+ An ordinary `.jay-html`: a `<head>` declaring its contract, a `<body>` of markup binding `{…}` against the
124
+ component's **own** tags, and an optional `<style>` of **plain component CSS**.
125
+
126
+ ```html
127
+ <!-- card.jay-html -->
128
+ <html>
129
+ <head>
130
+ <script type="application/jay-data" contract="./card.jay-contract"></script>
131
+ </head>
132
+ <body>
133
+ <div class="ds-card">
134
+ <h3 class="ds-card__heading">{heading}</h3>
135
+ <p class="ds-card__body" jay-content>{body}</p>
136
+ </div>
137
+ </body>
138
+ <style>
139
+ .ds-card {
140
+ border: 1px solid #e2e8f0;
141
+ padding: 20px;
142
+ }
143
+ .ds-card__heading {
144
+ font-size: 1.15rem;
145
+ }
146
+ </style>
147
+ </html>
148
+ ```
149
+
150
+ > **Author plain CSS here, not `@scope`.** In the template you write ordinary class rules
151
+ > (`.ds-card { … }`). When a page flattens the element, `sync` copies this CSS into the page and rewrites it
152
+ > into the canonical `@scope (.<ref>)` / `:scope` form for you — that scoped shape is a _page-side_ concern
153
+ > (see [The shape of a region's CSS](#the-shape-of-a-regions-css)); you don't write it in the template.
154
+
155
+ ### 4. Choose the right axis of variation
156
+
157
+ Decide _how_ each instance is allowed to differ, and encode it in the element — this is the core design
158
+ skill:
159
+
160
+ | The instance varies by… | Encode it as… | Example |
161
+ | ---------------------------------------------------- | --------------------------------------------- | -------------------------------- |
162
+ | **data** (same design, different values) | a **contract prop/tag**, bound `{…}` | `{heading}`, `{price}` |
163
+ | **content** (per-page text/media the consumer fills) | a **content slot** — `jay-content` (optional) | a body paragraph, a hero `<img>` |
164
+ | **runtime state** (same design, a branch) | a **conditional** (`if` / variant) | logged-in vs. not |
165
+ | **design / structure** (a different look) | a **separate template** (a second element) | compact card vs. feature card |
166
+
167
+ Prefer props for anything that is really just data. Use `jay-content` to pre-open the content a page is meant
168
+ to fill (optional — it spares the consumer an `override`; see [Content
169
+ slots](#content-slots--jay-content-an-optional-helper)). Fork a **second template**, not a conditional, when
170
+ the _look or structure_ differs — see [Two templates vs. a conditional](#two-templates-vs-a-conditional--different-axes).
171
+
172
+ ### 5. Compose other elements inside it
173
+
174
+ A template may nest other design-system elements (`<jay:button>`). Declare their contract import in the
175
+ template `<head>`; when a page flattens this element, the nested regions flatten **transitively** (the page
176
+ must also carry a `template=` import for each nested element so it resolves). See
177
+ [jay-html-components.md](jay-html-components.md).
178
+
179
+ Once the element exists, pages consume it exactly as below.
180
+
181
+ ## Using a design-system element (link + flatten)
182
+
183
+ ### 1. Declare the import with `template=`
184
+
185
+ ```html
186
+ <head>
187
+ <script
188
+ type="application/jay-headless"
189
+ contract="./components/card/card.jay-contract"
190
+ template="./components/card/card.jay-html"
191
+ ></script>
192
+ </head>
193
+ ```
194
+
195
+ - `contract=` — the component's contract (data shape, refs, props), as for any headless import.
196
+ - `template=` — the source `.jay-html` to flatten from. Resolved **relative to the page directory**.
197
+
198
+ A `template=` that does not resolve is a **hard error**, not a warning — fix the path or remove the
199
+ attribute.
200
+
201
+ ### 2. Place the region and flatten it
202
+
203
+ Write the tag, then let `sync` fill its body from the source template:
204
+
205
+ ```html
206
+ <body>
207
+ <jay:card ref="promo" heading="{title}"></jay:card>
208
+ </body>
209
+ ```
210
+
211
+ ```bash
212
+ jay-stack sync
213
+ ```
214
+
215
+ After sync the region carries a flattened copy of the source body, plus the component's CSS copied into
216
+ the page's `<style>`, `@scope`-wrapped by the region's `ref`:
217
+
218
+ ```html
219
+ <jay:card ref="promo" heading="{title}">
220
+ <div class="card promo">
221
+ <h3 class="card-heading">{heading}</h3>
222
+ <p class="card-body">Default body</p>
223
+ </div>
224
+ </jay:card>
225
+ ```
226
+
227
+ ```css
228
+ @scope (.promo) {
229
+ :scope {
230
+ border: 1px solid #ccc;
231
+ padding: 16px;
232
+ }
233
+ .card-heading {
234
+ color: black;
235
+ }
236
+ }
237
+ ```
238
+
239
+ Note two things sync did to the CSS — both required for the styles to actually apply. **Write region CSS in
240
+ this shape yourself**; the rules are below in [The shape of a region's CSS](#the-shape-of-a-regions-css).
241
+
242
+ > A bare `<jay:X>` with **no** flattened body is a hard error at build time — flatten it with `jay-stack
243
+ sync` (first-fill is just the no-edits case of sync; there is no separate `add` command).
244
+
245
+ ### 3. Edit the copy freely
246
+
247
+ The flattened body is yours. Rewrite text, change classes, add/remove children, edit the scoped CSS. Bind
248
+ `{…}` expressions against the component's own contract — inside `<jay:card>`, `{heading}` is the card's
249
+ `heading` prop, not the page's. What you may change without prompting a "make a new variant" nudge is the
250
+ [content vs. non-content](#what-counts-as-drift-worth-a-new-variant) distinction above.
251
+
252
+ ### 4. Two variants of one component on a page — `as=`
253
+
254
+ A `<jay:X>` region tag is derived from the imported contract's name, so importing one contract twice would
255
+ make **both** imports back the same `<jay:card>` tag — an ambiguous collision (`REGION-TAG-COLLISION`, a
256
+ hard error). To flatten **two [design variants](#two-templates-vs-a-conditional--different-axes) of one
257
+ contract** side by side (e.g. a default card and a feature card — same data shape, different design), give
258
+ the second import its own region tag with `as=`:
259
+
260
+ ```html
261
+ <head>
262
+ <!-- default card → <jay:card> -->
263
+ <script
264
+ type="application/jay-headless"
265
+ contract="./components/card/card.jay-contract"
266
+ template="./components/card/card.jay-html"
267
+ ></script>
268
+ <!-- feature card (same contract, different template) → <jay:feature-card> -->
269
+ <script
270
+ type="application/jay-headless"
271
+ contract="./components/card/card.jay-contract"
272
+ template="./components/card/card.feature.jay-html"
273
+ as="feature-card"
274
+ ></script>
275
+ </head>
276
+
277
+ <body>
278
+ <jay:card ref="starter" heading="Starter"></jay:card>
279
+ <jay:feature-card ref="pro" heading="Pro"></jay:feature-card>
280
+ </body>
281
+ ```
282
+
283
+ - `as=` must be a **kebab-case region tag** (`[a-z][a-z0-9-]*`), e.g. `as="feature-card"` → `<jay:feature-card>`.
284
+ - Both imports share the same contract, so both regions bind the **same props/refs** — only the design
285
+ (template + CSS) differs. Each region flattens and drifts independently from its own `template=`.
286
+ - `as=` composes at every nesting level: a template may alias the nested regions it uses (two
287
+ `<jay:button>`s as `<jay:main-cta>` / `<jay:secondary-cta>`), as long as the page carries a matching
288
+ aliased import for each.
289
+ - Write a second variant only when the difference is [non-content](#what-counts-as-drift-worth-a-new-variant)
290
+ (class / CSS / structure). A difference that is just **runtime state** (same design, a branch) is a
291
+ conditional inside **one** element, not a second import.
292
+
293
+ ## The shape of a region's CSS
294
+
295
+ A region's CSS is the component's own CSS, copied into the page `<style>` and rewritten into a canonical
296
+ form. `sync` produces this form and `validate` enforces it — but when you **hand-write or edit** a region's
297
+ CSS, write it this way directly:
298
+
299
+ ### 1. Wrap the component's rules in `@scope (.<ref>)`
300
+
301
+ All of a region's rules live inside one `@scope` block keyed by the region's `ref`:
302
+
303
+ ```css
304
+ @scope (.promo) {
305
+ /* …the card's rules… */
306
+ }
307
+ ```
308
+
309
+ This isolates the component's styles to that region — rules inside never leak out, and page rules outside
310
+ never bleed in.
311
+
312
+ ### 2. The `ref` is a real class on the region root (scope-anchor)
313
+
314
+ A jay `ref` is not emitted to the DOM, so `@scope (.promo)` would have nothing to match. The region root
315
+ therefore carries the ref **as an actual class** — `<div class="card promo">` above. Keep that class on the
316
+ root when you edit markup; it is what anchors the scope. (validate's drift check ignores this synthetic
317
+ class, so it never shows up as drift.)
318
+
319
+ ### 3. The component's own root rule targets `:scope`, not its block class
320
+
321
+ This is the one that trips people up. **Inside `@scope (.<ref>) { … }`, a scoped selector matches
322
+ _descendants_ of the scope root only — the scope root element itself is reachable solely through `:scope`.**
323
+ So a rule for the region's own root element must be written as `:scope`, _not_ as the root's class:
324
+
325
+ ```css
326
+ @scope (.promo) {
327
+ :scope {
328
+ border: 1px solid #ccc;
329
+ } /* ✅ styles the region root (<div class="card promo">) */
330
+ .card {
331
+ border: 1px solid #ccc;
332
+ } /* ❌ never matches — .card IS the scope root, not a descendant */
333
+ .card-heading {
334
+ color: black;
335
+ } /* ✅ descendant — plain class selector is correct */
336
+ }
337
+ ```
338
+
339
+ Rule of thumb: the component's **root block class** → `:scope`; keep compounds and descendants on that
340
+ class (`.card.active` → `:scope.active`, `.card .card-heading` → `:scope .card-heading`), and leave every
341
+ **descendant/element** selector as its ordinary class.
342
+
343
+ ### 4. Instances of the same component coalesce into one block
344
+
345
+ When a page holds several regions flattened from the _same_ template, they share one `@scope` block with a
346
+ selector list — never one duplicated block per `ref`:
347
+
348
+ ```css
349
+ @scope (.cardStarter, .cardPro) {
350
+ :scope {
351
+ border: 1px solid #ccc;
352
+ }
353
+ .card-heading {
354
+ color: black;
355
+ }
356
+ }
357
+ ```
358
+
359
+ Regions from _different_ templates stay in separate blocks. If you add a second instance by hand, fold its
360
+ ref into the existing block's selector list rather than copying the block.
361
+
362
+ ## Drift: what `jay-stack validate` tells you
363
+
364
+ Because the copy has a known source (its `template=`), `jay-stack validate` diffs the two and reports each
365
+ place your copy differs — at **facet** granularity (one attribute, one style declaration, a subtree, one
366
+ CSS rule or declaration), not "this node changed":
367
+
368
+ ```
369
+ <jay:card> region differs from source template "./components/card/card.jay-html":
370
+ <h3> children changed ("{heading}" → "On sale now").
371
+ To keep the page's version, mark the node override="children";
372
+ to discard it and re-flatten from source, run `jay-stack sync`.
373
+ ```
374
+
375
+ Every drift is a **warning**, and the page still builds. A warning is a decision point, not a failure:
376
+
377
+ - **Keep your edit** → mark the facet (below). It will no longer be reported, and `sync` will preserve it.
378
+ - **Discard your edit** → run `jay-stack sync` to re-flatten that facet from source.
379
+
380
+ An unedited copy validates clean. A `template=` that cannot be read, and a template that (transitively)
381
+ includes itself, are **hard errors**.
382
+
383
+ ## Marking what you own (facets)
384
+
385
+ To keep an edit, mark the exact facet you own. Everything you do not mark still reconciles with source, so
386
+ you get the source's future fixes for free on the parts you did not touch.
387
+
388
+ ### Markup facets — the `override` attribute
389
+
390
+ | You own… | Mark it | Effect |
391
+ | ------------------------------- | --------------------------- | ------------------------------------------------------ |
392
+ | one attribute | `override="class"` | your `class` is kept; other attributes reconcile |
393
+ | one inline-style declaration | `override="style.color"` | your `color` is kept; other style props reconcile |
394
+ | this element's children/subtree | `override="children"` | your subtree is kept; the element's attrs reconcile |
395
+ | several facets at once | `override="class children"` | space- **or** comma-separated list; each facet is kept |
396
+ | the whole node | `override` (bare) or `*` | the entire node is kept verbatim |
397
+
398
+ `override` is a **facet list**: list the facets you own, separated by spaces or commas (`override="class, style.color"`). A bare `override` (or `override="*"`) is the shorthand for "the whole node is mine." Anything not listed still reconciles with source.
399
+
400
+ ```html
401
+ <jay:card ref="promo" heading="{item.title}">
402
+ <div class="card featured" override="class">
403
+ <!-- class is page-owned; the rest of this div still reconciles -->
404
+ <h3 override="children">Half price this week</h3>
405
+ <p class="card-body">Default body</p>
406
+ </div>
407
+ </jay:card>
408
+ ```
409
+
410
+ - **Removing a child:** delete it and mark the parent `override="children"` — otherwise `sync` restores it.
411
+ - **Attribute / style tweak:** edit the value and mark that facet (`override="href"`,
412
+ `override="style.color"`), leaving siblings to reconcile.
413
+
414
+ ### CSS facets — the `jay:override` pragma
415
+
416
+ A comment has no element to hang an attribute on, so scoped CSS uses a pragma placed immediately before (or
417
+ inside) the rule:
418
+
419
+ ```css
420
+ @scope (.promo) {
421
+ /* jay:override: color */
422
+ .card-heading {
423
+ color: red; /* kept across sync */
424
+ font-weight: bold; /* still reconciles with source */
425
+ }
426
+ /* jay:override */
427
+ .card-badge {
428
+ /* the whole rule is page-owned */
429
+ }
430
+ }
431
+ ```
432
+
433
+ - `/* jay:override: <prop> */` — the page owns that one declaration.
434
+ - `/* jay:override */` — the page owns the whole rule.
435
+
436
+ ### Content slots — `jay-content` (an optional helper)
437
+
438
+ `override` lives on the **page** and is per-instance. `jay-content` is its mirror on the **template** side —
439
+ and because you [author templates too](#creating-a-design-system-element), it is a tool you reach for when
440
+ _creating_ an element, not something only plugin authors use. Put it on a template node to pre-declare a
441
+ **content slot**: "this part is meant to be filled per page." Edits to a marked facet are then expected by
442
+ design — `validate` never reports them as drift, and `sync` keeps the consumer's version — **without** any
443
+ page-side `override`.
444
+
445
+ ```html
446
+ <!-- in a template you author or own: -->
447
+ <p jay-content>{body}</p>
448
+ <img jay-content="src alt" src="placeholder.png" alt="" />
449
+ ```
450
+
451
+ ```html
452
+ <!-- in each flattened page, just edit — no override needed: -->
453
+ <p>Everything your team needs, in one place.</p>
454
+ <img src="/hero.png" alt="Our team at work" />
455
+ ```
456
+
457
+ **`jay-content` is optional, not required.** It is purely a convenience that removes the need to mark
458
+ `override` on the slot. Unlike slot/children mechanisms in other frameworks, you do **not** have to annotate
459
+ every variable spot — an unmarked content edit still works; it just shows up as drift until you either mark
460
+ it (`override` on the page, or `jay-content` on the template) or `sync` it away. Reach for `jay-content` when
461
+ a node is _meant_ to vary across pages and you'd rather not repeat an `override` on every instance.
462
+
463
+ It is a **template-side** marker — it lives on the source template and is stripped from the flattened page
464
+ (so it is stated once, and resilient to template edits). It uses the **same facet-list grammar** as
465
+ `override` (`children`, `style.<prop>`, attribute names, `*` — see
466
+ [_Marking what you own_](#marking-what-you-own-facets)), with one difference: a **bare** `jay-content` means
467
+ **children** (the common text slot), whereas a bare `override` means the whole node.
468
+
469
+ ## Upgrading: `jay-stack sync`
470
+
471
+ When the source template changes (a component upgrade, a design fix), re-flatten:
472
+
473
+ ```bash
474
+ jay-stack sync # every design-system region in the project
475
+ jay-stack sync src/pages/home.jay-html # one page
476
+ jay-stack sync --all # explicit "all pages"
477
+ ```
478
+
479
+ Sync is **re-flatten, not merge**: it overwrites everything from the current source **except** the facets
480
+ you marked `override`, which it preserves verbatim. There is no merge base, no hash, no conflict prompt —
481
+ your region is by definition equal to its source except at your marked facets, so sync is deterministic
482
+ overwrite-with-holes. It can never silently combine two edited versions into wrong markup. A synced region
483
+ validates clean.
484
+
485
+ CSS reconciles the same way as markup — drift is overwritten, marked facets are kept:
486
+
487
+ - A region block **equal** to the canonical form (or missing) is (re-)emitted as the canonical
488
+ `@scope (.<ref>)` block, coalesced with its same-template siblings.
489
+ - A block that **diverges but carries a `/* jay:override */` pragma** is preserved verbatim — your pragma is
490
+ never rewritten.
491
+ - A block that **diverges without any pragma** is unmarked CSS drift: `sync` overwrites it back to the
492
+ canonical form, exactly as it re-flattens unmarked markup. (This is what lets `sync` _migrate_ a page to a
493
+ new canonical form — e.g. an older `.card { … }` root rule is rewritten to `:scope { … }`.)
494
+
495
+ So after one `sync` a page's CSS is in canonical form, and a `validate`-clean page is one `sync` leaves
496
+ unchanged. To keep a hand-edit through `sync`, mark it with a `jay:override` pragma.
497
+
498
+ ## Suppressing the preference warnings
499
+
500
+ Each "prefer design-system elements" warning is suppressible for the legitimate case:
501
+
502
+ | Rule | Suppress |
503
+ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
504
+ | `REGION-NOT-LINKED` | `jay-validations="REGION-NOT-LINKED"` on the `application/jay-headless` import (per region type), or list the contract under `allow-inline-region` (below) |
505
+ | `REGION-OVERRIDE-NON-CONTENT` | `jay-validations="REGION-OVERRIDE-NON-CONTENT"` on the import |
506
+ | `COMPONENT-NO-TEMPLATE` | `allow-no-template: [Contract]` (a data-only component has no `.jay-html` to host an attribute) |
507
+ | `NO-DESIGN-SYSTEM` | `allow-no-design-system: true` |
508
+
509
+ Per-region-type suppression lives on the import; put multiple rules in one attribute:
510
+
511
+ ```html
512
+ <script
513
+ type="application/jay-headless"
514
+ contract="./components/card/card.jay-contract"
515
+ jay-validations="REGION-NOT-LINKED REGION-OVERRIDE-NON-CONTENT"
516
+ ></script>
517
+ ```
518
+
519
+ Project-wide / list suppression lives in the page's validations script:
520
+
521
+ ```html
522
+ <script type="application/jay-validations">
523
+ jay-stack:
524
+ allow-inline-region: [Card, HeroBanner] # contract names — allow hand-authored regions
525
+ allow-no-template: [MetricsProvider] # data-only components with no UI
526
+ allow-no-design-system: true # project intentionally shares no design system
527
+ </script>
528
+ ```
529
+
530
+ ## Why copy instead of reference?
531
+
532
+ Flattening removes the composition "seam": the region compiles through the ordinary headless-instance path,
533
+ so there is **no runtime crossing machinery** and no per-component ref-scope rules. The cost is that page
534
+ files grow and a copy duplicates its source — which is exactly what `validate` (drift) and `sync`
535
+ (re-flatten) exist to manage. Design edits stay in the page; the source stays reusable.