@symbo.ls/mcp 1.0.11 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,50 +1,51 @@
1
1
  # Symbols / DOMQL v3 — Strict Rules for AI Agents
2
2
 
3
- You are working in a **Symbols.app** / DOMQL v3 project. These rules are absolute and override any general coding instincts. Violations cause silent failures (black page, nothing renders).
3
+ These rules are absolute. Violations cause silent failures (black page, nothing renders). DO NOT override with general coding instincts.
4
4
 
5
5
  ---
6
6
 
7
7
  ## CRITICAL: v3 Syntax Only
8
8
 
9
- | v3 USE THIS | v2 NEVER USE |
10
- | -------------------------- | -------------------------------- |
11
- | `extends: 'Component'` | ~~`extend: 'Component'`~~ |
12
- | `childExtends: 'Component'`| ~~`childExtend: 'Component'`~~ |
13
- | `onClick: fn` | ~~`on: { click: fn }`~~ |
14
- | `onRender: fn` | ~~`on: { render: fn }`~~ |
15
- | props flattened at root | ~~`props: { ... }` wrapper~~ |
16
- | individual prop functions | ~~`props: ({ state }) => ({})` function~~ |
17
- | `flexAlign: 'center center'`| ~~`align: 'center center'`~~ |
18
- | `children` + `childExtends`| ~~`$collection`, `$propsCollection`~~ |
19
- | `children` + `childrenAs: 'state'`| ~~`$stateCollection`~~ |
9
+ | v3 USE THIS | v2 NEVER USE |
10
+ | ------------------------------ | ---------------------------------------- |
11
+ | `extends: 'Component'` | ~~`extend: 'Component'`~~ |
12
+ | `childExtends: 'Component'` | ~~`childExtend: 'Component'`~~ |
13
+ | `onClick: fn` | ~~`on: { click: fn }`~~ |
14
+ | `onRender: fn` | ~~`on: { render: fn }`~~ |
15
+ | props flattened at root | ~~`props: { ... }` wrapper~~ |
16
+ | individual prop functions | ~~`props: ({ state }) => ({})` function~~ |
17
+ | `align: 'center center'` | ~~`flexAlign: 'center center'`~~ |
18
+ | `children` + `childExtends` | ~~`$collection`, `$propsCollection`~~ |
19
+ | `children` + `childrenAs: 'state'` | ~~`$stateCollection`~~ |
20
+ | No `extends` needed for Text/Box/Flex | ~~`extends: 'Text'`~~, ~~`extends: 'Box'`~~, ~~`extends: 'Flex'`~~ |
20
21
 
21
22
  ---
22
23
 
23
24
  ## Rule 1 — Components are OBJECTS, never functions
24
25
 
25
26
  ```js
26
- // ✅ CORRECT
27
- export const Header = { extends: 'Flex', padding: 'A' }
27
+ // ✅
28
+ export const Header = { flow: 'x', padding: 'A' }
28
29
 
29
- // ❌ WRONG — never do this
30
+ // ❌
30
31
  export const Header = (el, state) => ({ padding: 'A' })
31
32
  ```
32
33
 
33
34
  ---
34
35
 
35
- ## Rule 2 — NO imports between project files — EVER
36
+ ## Rule 2 — NO imports between project files
36
37
 
37
- Components reference each other by PascalCase key in the object tree. Never use `import` between `components/`, `pages/`, `functions/`, etc.
38
+ NEVER use `import` between `components/`, `pages/`, `functions/`, etc. Reference components by PascalCase key in the object tree.
38
39
 
39
40
  ```js
40
- // ❌ WRONG
41
+ // ❌
41
42
  import { Navbar } from './Navbar.js'
42
43
 
43
- // ✅ CORRECT — just use the key name in the tree
44
+ // ✅
44
45
  Nav: { extends: 'Navbar' }
45
46
  ```
46
47
 
47
- **Only exception**: `pages/index.js` is the route registry — imports ARE allowed there.
48
+ **Only exception:** `pages/index.js` is the route registry — imports ARE allowed there.
48
49
 
49
50
  ```js
50
51
  // pages/index.js — only file where imports are permitted
@@ -54,28 +55,30 @@ export default { '/': main }
54
55
 
55
56
  ---
56
57
 
57
- ## Rule 3 — `components/index.js` use `export *` NOT `export * as`
58
+ ## Rule 3 — `components/index.js` uses `export *`, NOT `export * as`
58
59
 
59
- `export * as Foo` wraps everything in a namespace object and **breaks component resolution entirely**.
60
+ `export * as Foo` wraps in a namespace object and breaks component resolution.
60
61
 
61
62
  ```js
62
- // ✅ CORRECT
63
+ // ✅
63
64
  export * from './Navbar.js'
64
65
  export * from './PostCard.js'
65
66
 
66
- // ❌ WRONG — breaks everything
67
+ // ❌
67
68
  export * as Navbar from './Navbar.js'
68
69
  ```
69
70
 
70
71
  ---
71
72
 
72
- ## Rule 4 — Pages extend `'Page'`, not `'Flex'` or `'Box'`
73
+ ## Rule 4 — Pages extend `'Page'`
74
+
75
+ NEVER extend `'Flex'` or `'Box'` for page components.
73
76
 
74
77
  ```js
75
- // ✅ CORRECT
78
+ // ✅
76
79
  export const main = { extends: 'Page', ... }
77
80
 
78
- // ❌ WRONG
81
+ // ❌
79
82
  export const main = { extends: 'Flex', ... }
80
83
  ```
81
84
 
@@ -84,39 +87,43 @@ export const main = { extends: 'Flex', ... }
84
87
  ## Rule 5 — All folders are flat — no subfolders
85
88
 
86
89
  ```
87
- components/Navbar.js
88
- components/nav/Navbar.js
90
+ components/Navbar.js
91
+ components/nav/Navbar.js
89
92
  ```
90
93
 
91
94
  ---
92
95
 
93
96
  ## Rule 6 — PascalCase keys = child components (auto-extends)
94
97
 
98
+ A PascalCase key auto-extends the registered component matching that name.
99
+
95
100
  ```js
96
- // The key "Hgroup" auto-extends the registered Hgroup component
97
101
  export const MyCard = {
98
- Hgroup: { // auto-extends: 'Hgroup' because key matches registered component
99
- gap: '0', // ← override merges with base Hgroup
102
+ Hgroup: { // auto-extends: 'Hgroup'
103
+ gap: '0', // merges with base Hgroup
100
104
  }
101
105
  }
102
106
  ```
103
107
 
104
- If you need a different base, set `extends` explicitly.
108
+ SET `extends` explicitly only when the base differs from the key name.
105
109
 
106
110
  ---
107
111
 
108
- ## Rule 7 — State use `s.update()`, never mutate directly
112
+ ## Rule 7 — State updates via `s.update()`, NEVER mutate directly
109
113
 
110
114
  ```js
111
- onClick: (e, el, s) => s.update({ count: s.count + 1 }) // ✅ CORRECT
112
- onClick: (e, el, s) => { s.count = s.count + 1 } // ❌ WRONG — no re-render
115
+ // ✅
116
+ onClick: (e, el, s) => s.update({ count: s.count + 1 })
117
+
118
+ // ❌ — no re-render
119
+ onClick: (e, el, s) => { s.count = s.count + 1 }
113
120
  ```
114
121
 
115
122
  Root-level global state: `s.root.update({ key: val })`
116
123
 
117
124
  ---
118
125
 
119
- ## Rule 8 — `el.call('fn', arg)` — `this` is the element inside the function
126
+ ## Rule 8 — `el.call('fn', arg)` — `this` is the element
120
127
 
121
128
  ```js
122
129
  // functions/myFn.js
@@ -124,40 +131,48 @@ export const myFn = function myFn(arg1) {
124
131
  const node = this.node // 'this' is the DOMQL element
125
132
  }
126
133
 
127
- onClick: (e, el) => el.call('myFn', someArg) // ✅ CORRECT
128
- onClick: (e, el) => el.call('myFn', el, someArg) // ❌ WRONG — el passed twice
134
+ // ✅
135
+ onClick: (e, el) => el.call('myFn', someArg)
136
+
137
+ // ❌ — el passed twice
138
+ onClick: (e, el) => el.call('myFn', el, someArg)
129
139
  ```
130
140
 
131
141
  ---
132
142
 
133
- ## Rule 9 — Icons: NEVER use `Icon` inside `Button` use `Svg` atom
143
+ ## Rule 9 — Icons: use `Icon` component, store SVGs in design system
144
+
145
+ Use the `Icon` component to render icons. `Icon` extends `Svg` internally, accepts a `name` or `icon` prop to reference icons from `designSystem.icons`, and auto-converts kebab-case to camelCase. Supports sprite mode via `useIconSprite: true` in the design system.
134
146
 
135
147
  ```js
136
- // ✅ CORRECT
148
+ // ✅ — reference icon by name from designSystem.icons
149
+ MyBtn: {
150
+ tag: 'button', align: 'center center', cursor: 'pointer',
151
+ Icon: { name: 'arrowRight' }
152
+ }
153
+
154
+ // ❌ — do NOT inline SVGs via Svg component for icons
137
155
  MyBtn: {
138
- extends: 'Flex', tag: 'button', flexAlign: 'center center', cursor: 'pointer',
156
+ tag: 'button', align: 'center center', cursor: 'pointer',
139
157
  Svg: { viewBox: '0 0 24 24', width: '22', height: '22',
140
158
  html: '<path d="..." fill="currentColor"/>' }
141
159
  }
142
-
143
- // ❌ WRONG — Icon will NOT render inside Button
144
- MyBtn: { extends: 'Button', Icon: { name: 'heart' } }
145
160
  ```
146
161
 
147
162
  ---
148
163
 
149
- ## Rule 10 — `childExtends` MUST be a named component string, never an inline object
164
+ ## Rule 10 — `childExtends` MUST be a named string, never an inline object
150
165
 
151
- Inline `childExtends` objects cause ALL property values to be concatenated as visible text content on every child.
166
+ Inline objects cause ALL property values to render as visible text on every child.
152
167
 
153
168
  ```js
154
- // ✅ CORRECT — reference a named component
169
+ // ✅
155
170
  childExtends: 'NavLink'
156
171
 
157
- // ❌ WRONG — dumps all prop values as raw text on every child
172
+ // ❌ — dumps prop values as raw text
158
173
  childExtends: {
159
174
  tag: 'button',
160
- background: 'transparent', // renders as visible text!
175
+ background: 'transparent',
161
176
  border: '2px solid transparent'
162
177
  }
163
178
  ```
@@ -168,59 +183,53 @@ Define shared styles as a named component in `components/`, register in `compone
168
183
 
169
184
  ## Rule 11 — Color token syntax (dot-notation)
170
185
 
171
- Color tokens use **dot-notation** for opacity and `+`/`-`/`=` for tone modifiers:
186
+ Use dot-notation for opacity. Use `+`/`-`/`=` for tone modifiers.
172
187
 
173
188
  ```js
174
- // ✅ CORRECT — dot-notation opacity
189
+ // ✅
175
190
  { color: 'white.7' }
176
191
  { background: 'black.5' }
177
192
  { background: 'gray.92+8' } // opacity 0.92, tone +8
178
193
  { color: 'gray+16' } // full opacity, tone +16
179
194
  { color: 'gray=90' } // absolute lightness 90%
180
195
 
181
- // ❌ WRONG — old space-separated syntax (no longer supported)
196
+ // ❌ — old space-separated syntax
182
197
  { color: 'white .7' }
183
198
  { color: 'gray 1 +16' }
184
199
  ```
185
200
 
186
- For rarely-used colors, define named tokens in `designSystem/COLOR.js` instead.
201
+ For rarely-used colors, define named tokens in `designSystem/color.js`.
187
202
 
188
203
  ---
189
204
 
190
205
  ## Rule 12 — Border, boxShadow, textShadow — space-separated (CSS-like)
191
206
 
192
- These properties use **space-separated** syntax matching CSS conventions:
193
-
194
207
  ```js
195
- // ✅ CORRECT — space-separated, CSS order
208
+ // ✅
196
209
  { border: '1px solid gray.1' }
197
210
  { border: 'solid mediumGrey' }
198
211
  { boxShadow: 'black.1 0 A C C' }
199
212
  { textShadow: 'gray1 6px 6px' }
213
+ { boxShadow: 'black.1 0 A C C, white.5 0 B D D' } // multiple: use commas
214
+ { boxShadow: '0 2px 8px rgba(0,0,0,0.1)' } // raw CSS passes through
200
215
 
201
- // Multiple shadows use commas (CSS standard)
202
- { boxShadow: 'black.1 0 A C C, white.5 0 B D D' }
203
-
204
- // Raw CSS passes through unchanged
205
- { boxShadow: '0 2px 8px rgba(0,0,0,0.1)' }
206
-
207
- // ❌ WRONG — old comma-separated syntax (no longer supported)
216
+ // old comma-separated syntax
208
217
  { border: 'solid, gray, 1px' }
209
218
  { boxShadow: 'black .1, 0, A, C, C' }
210
219
  ```
211
220
 
212
221
  ---
213
222
 
214
- ## Rule 13 — CSS override precedence component level beats props level
223
+ ## Rule 13 — CSS override precedence: component level beats props level
215
224
 
216
225
  ```js
217
- // ✅ CORRECT — override at component level to match base component level
226
+ // ✅ — override at component level
218
227
  export const MyLink = {
219
228
  extends: 'Link',
220
229
  color: 'mediumBlue', // WINS — same declaration level
221
230
  }
222
231
 
223
- // ❌ WRONG — props block CANNOT override component-level CSS
232
+ // ❌ — props block CANNOT override component-level CSS
224
233
  export const MyLink = {
225
234
  extends: 'Link',
226
235
  props: { color: 'mediumBlue' } // LOSES to Link's component-level color
@@ -229,20 +238,20 @@ export const MyLink = {
229
238
 
230
239
  ---
231
240
 
232
- ## Rule 14 — Dynamic HTML attributes go in `attr: {}`, not at root level
241
+ ## Rule 14 — Dynamic HTML attributes go in `attr: {}`, not at root
233
242
 
234
243
  ```js
235
- // ✅ CORRECT — attr block resolves functions
244
+ // ✅
236
245
  export const Input = {
237
- type: 'text', // static default at root
246
+ type: 'text',
238
247
  attr: {
239
- type: ({ props }) => props.type // dynamic resolution in attr
248
+ type: ({ props }) => props.type // dynamic resolution
240
249
  }
241
250
  }
242
251
 
243
- // ❌ WRONG — function at component level gets stringified into the HTML attribute
252
+ // ❌ — function at root gets stringified: type="(el)=>el.props.type"
244
253
  export const Input = {
245
- type: ({ props }) => props.type // renders as: type="(el)=>el.props.type"
254
+ type: ({ props }) => props.type
246
255
  }
247
256
  ```
248
257
 
@@ -260,24 +269,24 @@ onRender: (el) => {
260
269
 
261
270
  ---
262
271
 
263
- ## Rule 16 — SVGs belong in `designSystem/svg_data.js`, not inline in components
272
+ ## Rule 16 — Icons use `Icon`, decorative/structural SVGs use `Svg`
264
273
 
265
- Store SVG markup in the design system and reference via context:
274
+ For **icons**, always use the `Icon` component referencing `designSystem.icons` by name. For **decorative or structural SVGs** (backgrounds, illustrations, shapes) that are not icons, use `Svg` and store the SVG data in `designSystem/svg_data.js`.
266
275
 
267
276
  ```js
268
- // designSystem/svg_data.js
269
- export default {
270
- folderTopRight: '<svg ...>...</svg>',
271
- folderBottomLeft: '<svg ...>...</svg>',
272
- }
277
+ // ✅ — icons: use Icon component
278
+ Icon: { name: 'arrowRight' }
273
279
 
274
- // In component — reference from designSystem
280
+ // decorative/structural SVG: reference from designSystem
275
281
  Svg: {
276
- src: ({ context }) => context.designSystem.SVG_DATA && context.designSystem.SVG_DATA.folderTopRight,
282
+ src: ({ context }) => context.designSystem.svg_data && context.designSystem.svg_data.folderTopRight,
277
283
  aspectRatio: '466 / 48',
278
284
  }
279
285
 
280
- // ❌ WRONG inline SVG string in component
286
+ // ❌ — do NOT use Svg for icons
287
+ Svg: { viewBox: '0 0 24 24', html: '<path d="..." fill="currentColor"/>' }
288
+
289
+ // ❌ — do NOT inline SVG strings in components
281
290
  Svg: {
282
291
  src: '<svg fill="none" viewBox="0 0 466 48">...</svg>',
283
292
  }
@@ -287,7 +296,7 @@ Svg: {
287
296
 
288
297
  ## Rule 17 — `customRouterElement` for persistent layouts
289
298
 
290
- Use `customRouterElement` in config to render pages inside a specific element instead of the root:
299
+ Use `customRouterElement` in config to render pages inside a specific element instead of root.
291
300
 
292
301
  ```js
293
302
  // config.js
@@ -298,18 +307,18 @@ export default {
298
307
  }
299
308
  ```
300
309
 
301
- The `/` (main) page defines the persistent layout. Sub-pages are rendered inside the target element without re-creating the layout.
310
+ The `/` (main) page defines the persistent layout. Sub-pages render inside the target element without re-creating the layout.
302
311
 
303
312
  ---
304
313
 
305
- ## Rule 18a — Tab/view switching use DOM IDs + function, NOT reactive `display`
314
+ ## Rule 18 — Tab/view switching: use DOM IDs, NOT reactive `display`
306
315
 
307
316
  Reactive `display: (el, s) => ...` on multiple full-page trees causes rendering failures.
308
317
 
309
318
  ```js
310
- // ✅ CORRECT use DOM ID pattern
311
- HomeView: { id: 'view-home', extends: 'Flex', ... },
312
- ExploreView: { id: 'view-explore', extends: 'Flex', display: 'none', ... },
319
+ // ✅ — DOM ID pattern
320
+ HomeView: { id: 'view-home', flow: 'column', ... },
321
+ ExploreView: { id: 'view-explore', flow: 'column', display: 'none', ... },
313
322
 
314
323
  // functions/switchView.js
315
324
  export const switchView = function switchView(view) {
@@ -322,10 +331,10 @@ export const switchView = function switchView(view) {
322
331
 
323
332
  ---
324
333
 
325
- ## Rule 17v3 Conditional Props use `isX` + `'.isX'`
334
+ ## Rule 19 — Conditional props: use `isX` + `'.isX'`
326
335
 
327
336
  ```js
328
- // ✅ NEW — v3 conditional props pattern
337
+ // ✅ — v3 conditional props
329
338
  export const ModalCard = {
330
339
  opacity: '0',
331
340
  visibility: 'hidden',
@@ -337,7 +346,7 @@ export const ModalCard = {
337
346
  },
338
347
  }
339
348
 
340
- // ❌ OLD — props function with conditional spread (deprecated)
349
+ // ❌ — deprecated props function with conditional spread
341
350
  export const ModalCard = {
342
351
  props: (el, s) => ({
343
352
  ...(s.root.activeModal ? { opacity: '1' } : { opacity: '0' })
@@ -347,9 +356,9 @@ export const ModalCard = {
347
356
 
348
357
  ---
349
358
 
350
- ## Rule 18 — CSS transitions require forced reflow
359
+ ## Rule 20 — CSS transitions require forced reflow
351
360
 
352
- DOMQL + Emotion apply all CSS changes in one JS tick. The browser skips the "before" state. Fix:
361
+ DOMQL + Emotion apply all CSS changes in one JS tick. The browser skips the "before" state. Force reflow to fix.
353
362
 
354
363
  ```js
355
364
  // FadeIn pattern
@@ -371,9 +380,9 @@ setTimeout(() => {
371
380
 
372
381
  ---
373
382
 
374
- ## Rule 19 — Semantic-First Architecture
383
+ ## Rule 21 — Semantic-First Architecture
375
384
 
376
- Use semantic components, never generic divs for meaningful content:
385
+ Use semantic components for meaningful content. NEVER use generic divs.
377
386
 
378
387
  | Intent | Use |
379
388
  | ------------------------- | ---------------- |
@@ -389,7 +398,9 @@ Use semantic components, never generic divs for meaningful content:
389
398
 
390
399
  ---
391
400
 
392
- ## Rule 20 — ARIA and accessibility attributes
401
+ ## Rule 22 — ARIA and accessibility attributes
402
+
403
+ Place ARIA attributes in `attr: {}`. Use native elements instead of role overrides wherever possible.
393
404
 
394
405
  ```js
395
406
  attr: {
@@ -401,57 +412,33 @@ attr: {
401
412
  }
402
413
  ```
403
414
 
404
- Use native elements instead of role overrides wherever possible.
405
-
406
415
  ---
407
416
 
408
- ## Project structure quick reference
417
+ ## Rule 23 Picture `src` goes on Img child, NEVER on Picture
409
418
 
410
- ```
411
- smbls/
412
- ├── index.js # export * as components, export default pages, etc.
413
- ├── state.js # export default { ... }
414
- ├── dependencies.js # export default { 'pkg': 'version' }
415
- ├── components/
416
- │ ├── index.js # export * from './Foo.js' ← flat re-exports ONLY
417
- │ └── Navbar.js # export const Navbar = { ... }
418
- ├── pages/
419
- │ ├── index.js # import + export default { '/': main }
420
- │ └── main.js # export const main = { extends: 'Page', ... }
421
- ├── functions/
422
- │ ├── index.js # export * from './switchView.js'
423
- │ └── switchView.js # export const switchView = function(...) {}
424
- └── designSystem/
425
- └── index.js # export default { COLOR, THEME, ... }
426
- ```
427
-
428
- ---
429
-
430
- ## Rule 21 — Picture `src` goes on Img child, never on Picture
431
-
432
- The HTML `<picture>` tag does NOT support `src` as an attribute. In v3, lowercase props move to `element.props`, so `element.parent.src` returns `undefined`.
419
+ The `<picture>` tag does NOT support `src`. In v3, lowercase props move to `element.props`, so `element.parent.src` returns `undefined`.
433
420
 
434
421
  ```js
435
- // ✅ CORRECT — src on the Img child
422
+ // ✅
436
423
  Picture: {
437
424
  Img: { src: '/files/photo.jpg' },
438
425
  width: '100%',
439
426
  aspectRatio: '16/9'
440
427
  }
441
428
 
442
- // ❌ WRONG — src on Picture is silently ignored
429
+ // ❌
443
430
  Picture: { src: '/files/photo.jpg', width: '100%' }
444
431
  ```
445
432
 
446
433
  ---
447
434
 
448
- ## Rule 22Component key `Map` auto-detects as `<map>` — add `tag: 'div'`
435
+ ## Rule 24 — `Map` component key needs `tag: 'div'`
449
436
 
450
- The key `Map` is auto-detected as the HTML `<map>` element (for image maps), which defaults to `display: inline` and has height 0. Always add `tag: 'div'` when using `Map` as a component name:
437
+ The key `Map` auto-detects as HTML `<map>` (image maps), which defaults to `display: inline` with height 0. ALWAYS add `tag: 'div'`.
451
438
 
452
439
  ```js
453
440
  export const Map = {
454
- extends: 'Flex',
441
+ flow: 'y',
455
442
  tag: 'div', // prevents <map> auto-detection
456
443
  // ...
457
444
  }
@@ -459,30 +446,74 @@ export const Map = {
459
446
 
460
447
  ---
461
448
 
462
- ## Rule 23 — `/files/` path resolution
449
+ ## Rule 25 — `/files/` path resolution
450
+
451
+ Paths like `/files/logo.png` reference the framework's embedded file system via `context.files`. The `/files/` prefix is stripped automatically — keys are just filenames (e.g., `"logo.png"`, not `"/files/logo.png"`).
452
+
453
+ ---
454
+
455
+ ## Rule 26 — NEVER extend `'Text'`, `'Box'`, or `'Flex'` — they are implicit defaults
456
+
457
+ `Text`, `Box`, and `Flex` are built-in base primitives. Every element is already a Box; any element with `text:` already behaves as Text; any element with `flow:`, `align:`, or `gap:` already behaves as Flex. Extending them explicitly causes an unnecessary merge step at runtime.
458
+
459
+ ```js
460
+ // ✅ CORRECT — no extends needed
461
+ Tag: { tag: 'span', text: 'NEW', padding: 'X A', fontSize: 'Y' }
462
+ Card: { tag: 'div', padding: 'B', background: 'white' }
463
+
464
+ // ❌ WRONG — unnecessary merge overhead
465
+ Tag: { extends: 'Text', text: 'NEW', padding: 'X A' }
466
+ Card: { extends: 'Box', padding: 'B', background: 'white' }
467
+ Row: { extends: 'Flex', gap: 'A', align: 'center' }
468
+ ```
469
+
470
+ Use a semantic or functional component instead: `Link`, `Button`, `Header`, `Section`, etc. For flex layout, use `flow:`, `align:`, or `gap:` props directly.
471
+
472
+ ---
473
+
474
+ ## Project Structure Quick Reference
463
475
 
464
- File paths like `/files/logo.png` reference the framework's embedded file system via `context.files`. The `/files/` prefix is stripped automatically when resolving — keys are just filenames (e.g., `"logo.png"`, not `"/files/logo.png"`).
476
+ ```
477
+ smbls/
478
+ ├── index.js # export * as components, export default pages, etc.
479
+ ├── state.js # export default { ... }
480
+ ├── dependencies.js # export default { 'pkg': 'version' }
481
+ ├── components/
482
+ │ ├── index.js # export * from './Foo.js' — flat re-exports ONLY
483
+ │ └── Navbar.js # export const Navbar = { ... }
484
+ ├── pages/
485
+ │ ├── index.js # import + export default { '/': main }
486
+ │ └── main.js # export const main = { extends: 'Page', ... }
487
+ ├── functions/
488
+ │ ├── index.js # export * from './switchView.js'
489
+ │ └── switchView.js # export const switchView = function(...) {}
490
+ └── designSystem/
491
+ └── index.js # export default { color, theme, ... }
492
+ ```
465
493
 
466
494
  ---
467
495
 
468
496
  ## Output Verification Checklist
469
497
 
470
- Before finalizing any generated code, verify:
471
-
472
- - [ ] Components are objects, not functions
473
- - [ ] No cross-file imports except `pages/index.js`
474
- - [ ] `components/index.js` uses `export *`, not `export * as`
475
- - [ ] All folders are flat (no subfolders)
476
- - [ ] Pages extend `'Page'`
477
- - [ ] v3 event syntax (`onClick`, `onRender`, not `on: { click: ... }`)
478
- - [ ] `flexAlign` not `align` for Flex shorthand
479
- - [ ] State updated via `state.update()`, never direct mutation
480
- - [ ] `childExtends` references named component strings only
481
- - [ ] No opacity modifier syntax in color props (`color: 'white .7'` is invalid)
482
- - [ ] Dynamic HTML attributes are in `attr: {}` block, not at root level
483
- - [ ] One H1 per page; logical heading hierarchy H1→H2→H3
484
- - [ ] Buttons for actions, Links for navigation
498
+ Before finalizing generated code, verify ALL of the following:
499
+
500
+ - [ ] Components are objects, not functions (Rule 1)
501
+ - [ ] No cross-file imports except `pages/index.js` (Rule 2)
502
+ - [ ] `components/index.js` uses `export *`, not `export * as` (Rule 3)
503
+ - [ ] Pages extend `'Page'` (Rule 4)
504
+ - [ ] All folders are flat — no subfolders (Rule 5)
505
+ - [ ] v3 event syntax: `onClick`, `onRender`, not `on: { click: ... }` (CRITICAL table)
506
+ - [ ] `align` not `flexAlign` for flex alignment shorthand (CRITICAL table)
507
+ - [ ] State updated via `s.update()`, never direct mutation (Rule 7)
508
+ - [ ] `childExtends` references named component strings only (Rule 10)
509
+ - [ ] Color uses dot-notation: `'white.7'` not `'white .7'` (Rule 11)
510
+ - [ ] Dynamic HTML attributes are in `attr: {}` block (Rule 14)
511
+ - [ ] `onRender` guards against double-init (Rule 15)
512
+ - [ ] Conditional props use `isX` / `'.isX'` pattern (Rule 19)
513
+ - [ ] One H1 per page; logical heading hierarchy H1 > H2 > H3
514
+ - [ ] Buttons for actions, Links for navigation (Rule 21)
485
515
  - [ ] Forms have labeled inputs with `name` and `type` attributes
486
- - [ ] Picture `src` is on the Img child, not on Picture itself
487
- - [ ] `Map` component key has `tag: 'div'` to avoid `<map>` auto-detection
488
- - [ ] `$propsCollection` / `$stateCollection` replaced with `children` pattern
516
+ - [ ] Picture `src` is on the Img child (Rule 23)
517
+ - [ ] `Map` component key has `tag: 'div'` (Rule 24)
518
+ - [ ] `$propsCollection` / `$stateCollection` replaced with `children` pattern (CRITICAL table)
519
+ - [ ] No `extends: 'Text'`, `extends: 'Box'`, or `extends: 'Flex'` — they are implicit defaults (Rule 26)