@jay-framework/jay-stack-cli 0.15.5 → 0.15.6

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 (36) hide show
  1. package/agent-kit-template/{INSTRUCTIONS.md → designer/INSTRUCTIONS.md} +11 -8
  2. package/agent-kit-template/{jay-html-syntax.md → designer/jay-html-components.md} +89 -158
  3. package/agent-kit-template/designer/jay-html-styling.md +97 -0
  4. package/agent-kit-template/designer/jay-html-syntax.md +44 -0
  5. package/agent-kit-template/designer/jay-html-template-syntax.md +203 -0
  6. package/agent-kit-template/developer/INSTRUCTIONS.md +34 -0
  7. package/agent-kit-template/developer/cli-commands.md +228 -0
  8. package/agent-kit-template/developer/component-data.md +109 -0
  9. package/agent-kit-template/developer/component-refs.md +117 -0
  10. package/agent-kit-template/developer/component-state.md +140 -0
  11. package/agent-kit-template/developer/configuration.md +76 -0
  12. package/agent-kit-template/developer/page-components.md +103 -0
  13. package/agent-kit-template/developer/page-contracts.md +114 -0
  14. package/agent-kit-template/developer/project-structure.md +242 -0
  15. package/agent-kit-template/developer/render-results.md +112 -0
  16. package/agent-kit-template/developer/routing.md +161 -0
  17. package/agent-kit-template/developer/seo-guide.md +93 -0
  18. package/agent-kit-template/plugin/INSTRUCTIONS.md +40 -0
  19. package/agent-kit-template/plugin/actions-guide.md +125 -0
  20. package/agent-kit-template/plugin/component-context.md +103 -0
  21. package/agent-kit-template/plugin/component-data.md +109 -0
  22. package/agent-kit-template/plugin/component-refs.md +117 -0
  23. package/agent-kit-template/plugin/component-state.md +140 -0
  24. package/agent-kit-template/plugin/component-structure.md +174 -0
  25. package/agent-kit-template/plugin/contracts-guide.md +193 -0
  26. package/agent-kit-template/plugin/plugin-structure.md +194 -0
  27. package/agent-kit-template/plugin/render-results.md +112 -0
  28. package/agent-kit-template/plugin/seo-guide.md +93 -0
  29. package/agent-kit-template/plugin/services-guide.md +116 -0
  30. package/agent-kit-template/plugin/validation.md +101 -0
  31. package/dist/index.js +678 -60
  32. package/package.json +10 -10
  33. /package/agent-kit-template/{cli-commands.md → designer/cli-commands.md} +0 -0
  34. /package/agent-kit-template/{contracts-and-plugins.md → designer/contracts-and-plugins.md} +0 -0
  35. /package/agent-kit-template/{project-structure.md → designer/project-structure.md} +0 -0
  36. /package/agent-kit-template/{routing.md → designer/routing.md} +0 -0
@@ -37,14 +37,17 @@ There is no standalone "interactive" phase. Any tag with `type: interactive` (re
37
37
 
38
38
  ## Reference Docs
39
39
 
40
- | File | Topic |
41
- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
42
- | [project-structure.md](project-structure.md) | Project layout, styling patterns (CSS themes, design tokens), configuration files |
43
- | [jay-html-syntax.md](jay-html-syntax.md) | Jay-HTML template syntax: data binding, conditions, loops, headless components |
44
- | [routing.md](routing.md) | Directory-based routing: page structure, dynamic routes, route priority |
45
- | [contracts-and-plugins.md](contracts-and-plugins.md) | Reading contracts, plugin.yaml, .jay-action files, and the materialized indexes |
46
- | [cli-commands.md](cli-commands.md) | CLI commands: setup, validate, params, action, dev server |
47
- | `references/<plugin>/` | Pre-generated discovery data: product catalogs, collection schemas (from `jay-stack agent-kit`) |
40
+ | File | Topic |
41
+ | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
42
+ | [project-structure.md](project-structure.md) | Project layout, styling patterns (CSS themes, design tokens), configuration files |
43
+ | [jay-html-syntax.md](jay-html-syntax.md) | Jay-HTML overview: philosophy, component types, nesting rules, links to sub-files |
44
+ | [jay-html-template-syntax.md](jay-html-template-syntax.md) | Template markup: data binding, conditions (boolean, enum, numeric, &&/\|\|), loops, refs |
45
+ | [jay-html-components.md](jay-html-components.md) | Component imports: headless (key/instance), headfull FS, nesting patterns |
46
+ | [jay-html-styling.md](jay-html-styling.md) | Styling: inline, external, dynamic style bindings, class bindings |
47
+ | [routing.md](routing.md) | Directory-based routing: page structure, dynamic routes, route priority |
48
+ | [contracts-and-plugins.md](contracts-and-plugins.md) | Reading contracts, plugin.yaml, .jay-action files, and the materialized indexes |
49
+ | [cli-commands.md](cli-commands.md) | CLI commands: setup, validate, params, action, dev server |
50
+ | `../references/<plugin>/` | Pre-generated discovery data: product catalogs, collection schemas (from `jay-stack agent-kit`) |
48
51
 
49
52
  ## Quick Start
50
53
 
@@ -1,137 +1,9 @@
1
- # Jay-HTML Syntax Reference
2
-
3
- ## File Structure
4
-
5
- A `.jay-html` file is standard HTML with jay-specific extensions.
6
-
7
- ```html
8
- <html>
9
- <head>
10
- <!-- Page contract (optional — defines page-level data) -->
11
- <script type="application/jay-data" contract="./page.jay-contract"></script>
12
-
13
- <!-- Explicit route params (for static override routes) -->
14
- <script type="application/jay-params">
15
- slug: ceramic-flower-vase
16
- </script>
17
-
18
- <!-- Headless component imports -->
19
- <script type="application/jay-headless" plugin="..." contract="..." key="..."></script>
20
-
21
- <!-- Headfull component imports -->
22
- <script type="application/jay-headfull" src="..." names="..." contract="..."></script>
23
-
24
- <!-- Styles -->
25
- <style>
26
- /* inline CSS */
27
- </style>
28
- <link rel="stylesheet" href="../../styles/theme.css" />
29
- </head>
30
- <body>
31
- <!-- Template with data bindings -->
32
- <h1>{title}</h1>
33
- </body>
34
- </html>
35
- ```
36
-
37
- ## Data Binding
38
-
39
- Use `{expression}` to bind contract data:
40
-
41
- ```html
42
- <h1>{productName}</h1>
43
- <!-- simple -->
44
- <span>{product.price}</span>
45
- <!-- nested via key -->
46
- <div style="color: {textColor}">{msg}</div>
47
- <!-- in attributes -->
48
- <a href="/products/{slug}">{name}</a>
49
- <!-- interpolated in attr values -->
50
- ```
51
-
52
- ## Conditional Rendering
53
-
54
- Use the `if` attribute:
55
-
56
- ```html
57
- <span if="inStock">In Stock</span>
58
- <span if="!inStock">Out of Stock</span>
59
- <div if="type===physical">Ships to your door</div>
60
- <div if="type===virtual">Instant download</div>
61
- ```
62
-
63
- Rules:
64
-
65
- - Boolean: `if="tagName"` / `if="!tagName"`
66
- - Enum variant: `if="tagName===value"` / `if="tagName!==value"` (no quotes around value)
67
- - Negation: `!` prefix
68
-
69
- ## Loops (forEach / trackBy)
70
-
71
- Iterate over repeated sub-contracts:
72
-
73
- ```html
74
- <li forEach="products" trackBy="id">
75
- <a href="/products/{slug}">
76
- <div>{name}</div>
77
- <div>{price}</div>
78
- </a>
79
- </li>
80
- ```
81
-
82
- - `forEach` — the repeated tag name from the contract
83
- - `trackBy` — stable unique key for each item (must match contract's trackBy)
84
- - Inside the loop, bindings resolve to the **current item's** tags
85
-
86
- **Nested loops:**
87
-
88
- ```html
89
- <div forEach="options" trackBy="_id">
90
- <h3>{name}</h3>
91
- <div forEach="choices" trackBy="choiceId">
92
- <button ref="choiceButton">{name}</button>
93
- </div>
94
- </div>
95
- ```
96
-
97
- ## Refs (Interactions)
98
-
99
- Map elements to contract `interactive` tags using `ref`:
100
-
101
- ```html
102
- <button ref="addToCart">Add to Cart</button>
103
- <input value="{quantity}" ref="quantityInput" />
104
- <a ref="productLink" href="/products/{slug}">{name}</a>
105
- <select ref="sizeSelector">
106
- ...
107
- </select>
108
- ```
109
-
110
- Match the element type to the contract's `elementType`:
111
-
112
- - `HTMLButtonElement` → `<button>`
113
- - `HTMLInputElement` → `<input>`
114
- - `HTMLAnchorElement` → `<a>`
115
- - `HTMLSelectElement` → `<select>`
116
-
117
- **Key-based headless refs** — prefix with the key:
118
-
119
- ```html
120
- <button ref="rating.submitButton">Submit</button> <button ref="mt.happy">+1 Happy</button>
121
- ```
122
-
123
- **Refs inside forEach** — use the tag path from the contract:
124
-
125
- ```html
126
- <div forEach="options" trackBy="_id">
127
- <div forEach="choices" trackBy="choiceId">
128
- <button ref="choiceButton">{name}</button>
129
- </div>
130
- </div>
131
- ```
1
+ # Jay-HTML Component Imports
132
2
 
133
3
  ## Headless Components
134
4
 
5
+ Headless components provide data and interactions with no UI. The page or headfull component provides the template.
6
+
135
7
  ### Pattern 1: Key-Based Import
136
8
 
137
9
  Data merged into parent ViewState under a key. Use when you have **one instance** of a component per page.
@@ -165,6 +37,8 @@ Access data and refs with the key prefix:
165
37
  </div>
166
38
  ```
167
39
 
40
+ Key-based imports are only available in **pages** (not in headfull FS components).
41
+
168
42
  ### Pattern 2: Instance-Based (jay: prefix)
169
43
 
170
44
  Multiple instances with props and inline templates. Use when you need **multiple instances** or need to pass **props**.
@@ -213,7 +87,9 @@ Inside `<jay:...>`, bindings resolve to **that instance's** contract tags (not t
213
87
 
214
88
  ## Headfull Full-Stack Components
215
89
 
216
- Headfull components that own their UI can be made full-stack by adding a `contract` attribute:
90
+ Headfull components own their UI and can be made full-stack by adding a `contract` attribute.
91
+
92
+ ### Import Declaration
217
93
 
218
94
  ```html
219
95
  <head>
@@ -232,7 +108,9 @@ Headfull components that own their UI can be made full-stack by adding a `contra
232
108
  - `names` — Component name to import
233
109
  - `contract` — Path to the contract file (makes the component full-stack with SSR)
234
110
 
235
- **Usage** — same as client-only headfull, with props:
111
+ ### Usage
112
+
113
+ Same as client-only headfull, with props:
236
114
 
237
115
  ```html
238
116
  <jay:SharedHeader logoUrl="/logo.png" />
@@ -240,50 +118,96 @@ Headfull components that own their UI can be made full-stack by adding a `contra
240
118
 
241
119
  Without `contract`, the component is client-only. With `contract`, it participates in slow/fast/interactive phases and is server-side rendered. Use headfull full-stack components for reusable UI with fixed layout that needs SSR (headers, footers, sidebars).
242
120
 
243
- ## Page-Level Contract
121
+ ### Headfull FS Component Structure
244
122
 
245
- A page can define its own data contract:
123
+ A headfull FS component has its own `.jay-html` file with the same structure as a page:
246
124
 
247
125
  ```html
248
- <script type="application/jay-data" contract="./page.jay-contract"></script>
126
+ <!-- components/header/header.jay-html -->
127
+ <html>
128
+ <head>
129
+ <script type="application/jay-data">
130
+ data:
131
+ logoUrl: string
132
+ </script>
133
+ </head>
134
+ <body>
135
+ <header>
136
+ <img src="{logoUrl}" />
137
+ <nav>Navigation here</nav>
138
+ </header>
139
+ </body>
140
+ </html>
249
141
  ```
250
142
 
251
- Tags from the page contract are bound directly (no key prefix).
143
+ ## Nesting Components
252
144
 
253
- ## Styling
145
+ ### Headfull Inside Headfull
254
146
 
255
- **Inline `<style>`:**
147
+ A layout component imports a header component:
256
148
 
257
149
  ```html
258
- <head>
259
- <style>
260
- .product-card {
261
- border: 1px solid #ccc;
262
- padding: 16px;
263
- }
264
- .price {
265
- font-weight: bold;
266
- color: #2d7d2d;
267
- }
268
- </style>
269
- </head>
150
+ <!-- layout/layout.jay-html -->
151
+ <html>
152
+ <head>
153
+ <script
154
+ type="application/jay-headfull"
155
+ src="../header/header"
156
+ contract="../header/header.jay-contract"
157
+ names="header"
158
+ ></script>
159
+ <script type="application/jay-data">
160
+ data:
161
+ sidebarLabel: string
162
+ </script>
163
+ </head>
164
+ <body>
165
+ <div class="layout">
166
+ <jay:header logoUrl="/logo.png" />
167
+ <aside>{sidebarLabel}</aside>
168
+ </div>
169
+ </body>
170
+ </html>
270
171
  ```
271
172
 
272
- **External stylesheets:**
173
+ ### Headless Inside Headfull
174
+
175
+ A header component uses a headless plugin widget:
273
176
 
274
177
  ```html
275
- <link rel="stylesheet" href="../../styles/theme.css" />
178
+ <!-- header/header.jay-html -->
179
+ <html>
180
+ <head>
181
+ <script type="application/jay-headless" plugin="my-plugin" contract="cart-indicator"></script>
182
+ <script type="application/jay-data">
183
+ data:
184
+ logoUrl: string
185
+ </script>
186
+ </head>
187
+ <body>
188
+ <header>
189
+ <img src="{logoUrl}" />
190
+ <jay:cart-indicator>
191
+ <span class="count">{itemCount}</span>
192
+ </jay:cart-indicator>
193
+ </header>
194
+ </body>
195
+ </html>
276
196
  ```
277
197
 
278
- **Dynamic style bindings:**
198
+ Nesting depth is unlimited. Circular imports are detected as errors. Key-based headless imports (`key="..."`) are not allowed inside headfull FS components — use instance-based imports instead.
279
199
 
280
- ```html
281
- <div style="color: {textColor}; width: {width}px">styled</div>
282
- ```
200
+ ## Nesting Rules
201
+
202
+ | Parent component | Can import headfull FS? | Can import headless (instance)? | Can import keyed headless? |
203
+ | ---------------- | ----------------------- | ------------------------------- | -------------------------- |
204
+ | **Page** | Yes | Yes | Yes |
205
+ | **Headfull FS** | Yes (recursive) | Yes (in its own head) | No |
206
+ | **Headless** | No (no template) | No (no template) | No (no template) |
283
207
 
284
208
  ## Complete Example
285
209
 
286
- A homepage with key-based and instance-based headless components:
210
+ A homepage with key-based, instance-based, and headfull components:
287
211
 
288
212
  ```html
289
213
  <html>
@@ -299,6 +223,12 @@ A homepage with key-based and instance-based headless components:
299
223
  plugin="product-widget"
300
224
  contract="product-widget"
301
225
  ></script>
226
+ <script
227
+ type="application/jay-headfull"
228
+ src="../components/shared-header"
229
+ names="SharedHeader"
230
+ contract="../components/shared-header/shared-header.jay-contract"
231
+ ></script>
302
232
  <script type="application/jay-data" contract="./page.jay-contract"></script>
303
233
  <style>
304
234
  .section {
@@ -313,6 +243,7 @@ A homepage with key-based and instance-based headless components:
313
243
  </style>
314
244
  </head>
315
245
  <body>
246
+ <jay:SharedHeader logoUrl="/logo.png" />
316
247
  <h1>Homepage</h1>
317
248
 
318
249
  <!-- Key-based: mood tracker -->
@@ -0,0 +1,97 @@
1
+ # Jay-HTML Styling
2
+
3
+ ## Inline Styles
4
+
5
+ Add `<style>` blocks in `<head>`:
6
+
7
+ ```html
8
+ <head>
9
+ <style>
10
+ .product-card {
11
+ border: 1px solid #ccc;
12
+ padding: 16px;
13
+ }
14
+ .price {
15
+ font-weight: bold;
16
+ color: #2d7d2d;
17
+ }
18
+ </style>
19
+ </head>
20
+ ```
21
+
22
+ ## External Stylesheets
23
+
24
+ Link external CSS files:
25
+
26
+ ```html
27
+ <link rel="stylesheet" href="../../styles/theme.css" />
28
+ ```
29
+
30
+ ## Dynamic Style Bindings
31
+
32
+ Use `{expression}` inside `style` attribute values:
33
+
34
+ ```html
35
+ <div style="color: {textColor}; width: {width}px">styled</div>
36
+ <div style="margin: 10px; color: {color}; padding: 20px">mixed static and dynamic</div>
37
+ <div style="background-color: {bgColor}; font-size: {fontSize}px">with units</div>
38
+ ```
39
+
40
+ ## Class Binding
41
+
42
+ ### Static Classes
43
+
44
+ ```html
45
+ <div class="button primary">Click me</div>
46
+ ```
47
+
48
+ ### Dynamic Class Value
49
+
50
+ Bind a contract value as a class name:
51
+
52
+ ```html
53
+ <div class="button {variant}">Click me</div>
54
+ ```
55
+
56
+ ### Conditional Class
57
+
58
+ Add a class only when a condition is true:
59
+
60
+ ```html
61
+ <div class="{isActive ? active}">Tab</div>
62
+ <div class="{hasItems ? has-items}">Cart</div>
63
+ ```
64
+
65
+ ### Ternary Class
66
+
67
+ Switch between two classes based on a condition:
68
+
69
+ ```html
70
+ <div class="{isPrimary ? primary : secondary}">Button</div>
71
+ <div class="{isExpanded ? expanded : collapsed}">Panel</div>
72
+ ```
73
+
74
+ ### Combined Classes
75
+
76
+ Mix static, dynamic, and conditional classes:
77
+
78
+ ```html
79
+ <div class="button {isPrimary ? primary : secondary}">Click me</div>
80
+ <a class="cart-indicator {hasItems ? has-items} {isLoading ? is-loading}">Cart</a>
81
+ <div class="first-class {bool1 ? main : second} {!bool1 ? third : forth}">mixed</div>
82
+ ```
83
+
84
+ ### With Enum Conditions
85
+
86
+ ```html
87
+ <div class="{status === active ? highlighted}">Item</div>
88
+ ```
89
+
90
+ ### Class Binding Rules
91
+
92
+ - Static classes are always present: `class="button"`
93
+ - `{value}` inserts the contract value as a class name
94
+ - `{condition ? class}` adds `class` when condition is truthy
95
+ - `{condition ? classA : classB}` switches between two classes
96
+ - `{!condition ? class}` uses negation
97
+ - Multiple bindings can be combined in one `class` attribute
@@ -0,0 +1,44 @@
1
+ # Jay-HTML — AI Designer Instructions
2
+
3
+ ## Philosophy
4
+
5
+ Jay-HTML is standard HTML with data bindings. There is no custom component framework — just native HTML + CSS with `{expression}` bindings for dynamic content.
6
+
7
+ The design tool can freely read and rewrite jay-html files as long as contract bindings stay intact. Bindings (`{expression}`, `if`, `forEach`, `ref`) are the only extension to HTML. Everything else — CSS, structure, semantics, accessibility — is native.
8
+
9
+ ## Component Types
10
+
11
+ ### Page
12
+
13
+ Entry point at `src/pages/`. Can import all component types.
14
+
15
+ ### Headfull FS
16
+
17
+ Reusable component with its own template + contract + three-phase rendering (slow/fast/interactive). Lives alongside the page in a components directory. Can nest other headfull FS and instance headless in its own `<head>`. Cannot use keyed headless.
18
+
19
+ ### Headless
20
+
21
+ Plugin-provided logic component. No template — the page or headfull component provides the UI via inline template (`<jay:xxx>`). Two import patterns:
22
+
23
+ - **Key-based** — one instance per page, data merged under a key prefix (`{key.tag}`)
24
+ - **Instance-based** — multiple instances with props, each gets its own inline template
25
+
26
+ ## Nesting Rules
27
+
28
+ | Parent component | Can import headfull FS? | Can import headless (instance)? | Can import keyed headless? |
29
+ | ---------------- | ----------------------- | ------------------------------- | -------------------------- |
30
+ | **Page** | Yes | Yes | Yes |
31
+ | **Headfull FS** | Yes (recursive) | Yes (in its own head) | No |
32
+ | **Headless** | No (no template) | No (no template) | No (no template) |
33
+
34
+ ## Validation
35
+
36
+ After creating or editing jay-html files, run `jay-stack validate` to check for errors. It catches issues like unknown refs, missing contracts, and invalid bindings. See [cli-commands.md](cli-commands.md) for details.
37
+
38
+ ## Reference
39
+
40
+ | File | Topic |
41
+ | ---------------------------------------------------------- | ---------------------------------------------------------------------- |
42
+ | [jay-html-template-syntax.md](jay-html-template-syntax.md) | Template markup: file structure, data binding, conditions, loops, refs |
43
+ | [jay-html-components.md](jay-html-components.md) | Component imports: headless (key/instance), headfull FS, nesting |
44
+ | [jay-html-styling.md](jay-html-styling.md) | Styling: inline, external, dynamic styles, class bindings |
@@ -0,0 +1,203 @@
1
+ # Jay-HTML Template Syntax
2
+
3
+ ## File Structure
4
+
5
+ A `.jay-html` file is standard HTML with jay-specific extensions.
6
+
7
+ ```html
8
+ <html>
9
+ <head>
10
+ <!-- Page contract (optional — defines page-level data) -->
11
+ <script type="application/jay-data" contract="./page.jay-contract"></script>
12
+
13
+ <!-- Explicit route params (for static override routes) -->
14
+ <script type="application/jay-params">
15
+ slug: ceramic-flower-vase
16
+ </script>
17
+
18
+ <!-- Headless component imports -->
19
+ <script type="application/jay-headless" plugin="..." contract="..." key="..."></script>
20
+
21
+ <!-- Headfull component imports -->
22
+ <script type="application/jay-headfull" src="..." names="..." contract="..."></script>
23
+
24
+ <!-- Styles -->
25
+ <style>
26
+ /* inline CSS */
27
+ </style>
28
+ <link rel="stylesheet" href="../../styles/theme.css" />
29
+ </head>
30
+ <body>
31
+ <!-- Template with data bindings -->
32
+ <h1>{title}</h1>
33
+ </body>
34
+ </html>
35
+ ```
36
+
37
+ ## Data Binding
38
+
39
+ Use `{expression}` to bind contract data:
40
+
41
+ ```html
42
+ <h1>{productName}</h1>
43
+ <!-- simple -->
44
+ <span>{product.price}</span>
45
+ <!-- nested via key -->
46
+ <div style="color: {textColor}">{msg}</div>
47
+ <!-- in attributes -->
48
+ <a href="/products/{slug}">{name}</a>
49
+ <!-- interpolated in attr values -->
50
+ ```
51
+
52
+ ## Boolean Attributes
53
+
54
+ HTML boolean attributes (`disabled`, `checked`, `hidden`, `readonly`) can be bound to contract data:
55
+
56
+ ```html
57
+ <button disabled="isSubmitting">Submit</button>
58
+ <!-- disabled when isSubmitting is true -->
59
+ <button disabled="!inStock">Add to Cart</button>
60
+ <!-- disabled when inStock is false -->
61
+ <input type="checkbox" checked="isSelected" />
62
+ <!-- checked when isSelected is true -->
63
+ <div hidden="!isVisible">Content</div>
64
+ <!-- hidden when isVisible is false -->
65
+ ```
66
+
67
+ - Set the attribute value to a **boolean tag name** — the attribute is present when true, absent when false
68
+ - Use `!` prefix to negate: `disabled="!enabled"` means disabled when enabled is false
69
+ - Without a value (`disabled` alone), the attribute is always present (standard HTML behavior)
70
+
71
+ ## Conditional Rendering
72
+
73
+ Use the `if` attribute to conditionally show elements.
74
+
75
+ ### Boolean
76
+
77
+ ```html
78
+ <span if="inStock">In Stock</span> <span if="!inStock">Out of Stock</span>
79
+ ```
80
+
81
+ ### Enum Variant
82
+
83
+ No quotes around the value:
84
+
85
+ ```html
86
+ <div if="type===physical">Ships to your door</div>
87
+ <div if="type!==physical">Not a physical product</div>
88
+ <div if="status===active">Active</div>
89
+ ```
90
+
91
+ ### Numeric Comparisons
92
+
93
+ Compare against numbers or other fields:
94
+
95
+ ```html
96
+ <span if="count > 0">You have {count} items</span>
97
+ <span if="count <= 0">No items</span>
98
+ <button if="currentPage <= 1" disabled>Previous</button>
99
+ <span if="price <= budget">Affordable</span>
100
+ <span if="available >= required">In stock</span>
101
+ ```
102
+
103
+ Operators: `>`, `<`, `>=`, `<=`, `==`, `!=`
104
+
105
+ ### Logical AND / OR
106
+
107
+ Combine conditions with `&&` and `||`:
108
+
109
+ ```html
110
+ <div if="inStock && hasDiscount">Great deal!</div>
111
+ <span if="isPromoted || hasDiscount">Has offer</span>
112
+ ```
113
+
114
+ Use parentheses for complex expressions:
115
+
116
+ ```html
117
+ <div if="(inStock && hasDiscount) || isPromoted">Buyable</div>
118
+ <div if="inStock && price > 0">Purchasable</div>
119
+ <button if="count <= 0 || isLoading" disabled>Checkout</button>
120
+ ```
121
+
122
+ ### Rules Summary
123
+
124
+ - Boolean: `if="flag"` / `if="!flag"`
125
+ - Enum: `if="tag===value"` / `if="tag!==value"` (no quotes around value)
126
+ - Numeric: `if="count > 0"`, `if="price <= budget"`
127
+ - Field comparison: `if="available >= required"`
128
+ - Logical: `if="a && b"`, `if="a || b"`, `if="(a || b) && c"`
129
+ - Negation: `!` prefix on booleans
130
+
131
+ ## Loops (forEach / trackBy)
132
+
133
+ Iterate over repeated sub-contracts:
134
+
135
+ ```html
136
+ <li forEach="products" trackBy="id">
137
+ <a href="/products/{slug}">
138
+ <div>{name}</div>
139
+ <div>{price}</div>
140
+ </a>
141
+ </li>
142
+ ```
143
+
144
+ - `forEach` — the repeated tag name from the contract
145
+ - `trackBy` — stable unique key for each item (must match contract's trackBy)
146
+ - Inside the loop, bindings resolve to the **current item's** tags
147
+
148
+ **Nested loops:**
149
+
150
+ ```html
151
+ <div forEach="options" trackBy="_id">
152
+ <h3>{name}</h3>
153
+ <div forEach="choices" trackBy="choiceId">
154
+ <button ref="choiceButton">{name}</button>
155
+ </div>
156
+ </div>
157
+ ```
158
+
159
+ ## Refs (Interactions)
160
+
161
+ Map elements to contract `interactive` tags using `ref`:
162
+
163
+ ```html
164
+ <button ref="addToCart">Add to Cart</button>
165
+ <input value="{quantity}" ref="quantityInput" />
166
+ <a ref="productLink" href="/products/{slug}">{name}</a>
167
+ <select ref="sizeSelector">
168
+ ...
169
+ </select>
170
+ ```
171
+
172
+ Match the element type to the contract's `elementType`:
173
+
174
+ - `HTMLButtonElement` → `<button>`
175
+ - `HTMLInputElement` → `<input>`
176
+ - `HTMLAnchorElement` → `<a>`
177
+ - `HTMLSelectElement` → `<select>`
178
+
179
+ **Key-based headless refs** — prefix with the key:
180
+
181
+ ```html
182
+ <button ref="rating.submitButton">Submit</button> <button ref="mt.happy">+1 Happy</button>
183
+ ```
184
+
185
+ **Refs inside forEach** — use the tag path from the contract:
186
+
187
+ ```html
188
+ <div forEach="options" trackBy="_id">
189
+ <div forEach="choices" trackBy="choiceId">
190
+ <button ref="choiceButton">{name}</button>
191
+ </div>
192
+ </div>
193
+ ```
194
+
195
+ ## Page-Level Contract
196
+
197
+ A page can define its own data contract:
198
+
199
+ ```html
200
+ <script type="application/jay-data" contract="./page.jay-contract"></script>
201
+ ```
202
+
203
+ Tags from the page contract are bound directly (no key prefix).