@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.
- package/README.md +1 -0
- package/package.json +1 -1
- package/symbols_mcp/skills/AUDIT.md +148 -174
- package/symbols_mcp/skills/BRAND_IDENTITY.md +75 -0
- package/symbols_mcp/skills/COMPONENTS.md +151 -306
- package/symbols_mcp/skills/COOKBOOK.md +850 -0
- package/symbols_mcp/skills/DEFAULT_COMPONENTS.md +3856 -0
- package/symbols_mcp/skills/DEFAULT_LIBRARY.md +301 -0
- package/symbols_mcp/skills/DESIGN_CRITIQUE.md +70 -59
- package/symbols_mcp/skills/DESIGN_DIRECTION.md +109 -175
- package/symbols_mcp/skills/DESIGN_SYSTEM.md +473 -181
- package/symbols_mcp/skills/DESIGN_SYSTEM_ARCHITECT.md +65 -57
- package/symbols_mcp/skills/DESIGN_TO_CODE.md +83 -64
- package/symbols_mcp/skills/DESIGN_TREND.md +62 -50
- package/symbols_mcp/skills/FIGMA_MATCHING.md +69 -58
- package/symbols_mcp/skills/LEARNINGS.md +374 -0
- package/symbols_mcp/skills/MARKETING_ASSETS.md +71 -59
- package/symbols_mcp/skills/MIGRATION.md +158 -117
- package/symbols_mcp/skills/PATTERNS.md +101 -74
- package/symbols_mcp/skills/PRESENTATION.md +78 -0
- package/symbols_mcp/skills/PROJECT_STRUCTURE.md +114 -116
- package/symbols_mcp/skills/RULES.md +179 -148
- package/symbols_mcp/skills/RUNNING_APPS.md +476 -0
- package/symbols_mcp/skills/SEO-METADATA.md +33 -18
- package/symbols_mcp/skills/SNIPPETS.md +598 -0
- package/symbols_mcp/skills/SSR-BRENDER.md +99 -0
- package/symbols_mcp/skills/SYNTAX.md +356 -298
- package/symbols_mcp/skills/BRAND_INDENTITY.md +0 -69
- package/symbols_mcp/skills/THE_PRESENTATION.md +0 -69
|
@@ -1,50 +1,51 @@
|
|
|
1
1
|
# Symbols / DOMQL v3 — Strict Rules for AI Agents
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
10
|
-
|
|
|
11
|
-
| `extends: 'Component'`
|
|
12
|
-
| `childExtends: 'Component'
|
|
13
|
-
| `onClick: fn`
|
|
14
|
-
| `onRender: fn`
|
|
15
|
-
| props flattened at root
|
|
16
|
-
| individual prop functions
|
|
17
|
-
| `
|
|
18
|
-
| `children` + `childExtends
|
|
19
|
-
| `children` + `childrenAs: 'state'
|
|
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
|
-
// ✅
|
|
27
|
-
export const Header = {
|
|
27
|
+
// ✅
|
|
28
|
+
export const Header = { flow: 'x', padding: 'A' }
|
|
28
29
|
|
|
29
|
-
// ❌
|
|
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
|
|
36
|
+
## Rule 2 — NO imports between project files
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
NEVER use `import` between `components/`, `pages/`, `functions/`, etc. Reference components by PascalCase key in the object tree.
|
|
38
39
|
|
|
39
40
|
```js
|
|
40
|
-
// ❌
|
|
41
|
+
// ❌
|
|
41
42
|
import { Navbar } from './Navbar.js'
|
|
42
43
|
|
|
43
|
-
// ✅
|
|
44
|
+
// ✅
|
|
44
45
|
Nav: { extends: 'Navbar' }
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
**Only exception
|
|
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`
|
|
58
|
+
## Rule 3 — `components/index.js` uses `export *`, NOT `export * as`
|
|
58
59
|
|
|
59
|
-
`export * as Foo` wraps
|
|
60
|
+
`export * as Foo` wraps in a namespace object and breaks component resolution.
|
|
60
61
|
|
|
61
62
|
```js
|
|
62
|
-
// ✅
|
|
63
|
+
// ✅
|
|
63
64
|
export * from './Navbar.js'
|
|
64
65
|
export * from './PostCard.js'
|
|
65
66
|
|
|
66
|
-
// ❌
|
|
67
|
+
// ❌
|
|
67
68
|
export * as Navbar from './Navbar.js'
|
|
68
69
|
```
|
|
69
70
|
|
|
70
71
|
---
|
|
71
72
|
|
|
72
|
-
## Rule 4 — Pages extend `'Page'
|
|
73
|
+
## Rule 4 — Pages extend `'Page'`
|
|
74
|
+
|
|
75
|
+
NEVER extend `'Flex'` or `'Box'` for page components.
|
|
73
76
|
|
|
74
77
|
```js
|
|
75
|
-
// ✅
|
|
78
|
+
// ✅
|
|
76
79
|
export const main = { extends: 'Page', ... }
|
|
77
80
|
|
|
78
|
-
// ❌
|
|
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: { //
|
|
99
|
-
gap: '0', //
|
|
102
|
+
Hgroup: { // auto-extends: 'Hgroup'
|
|
103
|
+
gap: '0', // merges with base Hgroup
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
106
|
```
|
|
103
107
|
|
|
104
|
-
|
|
108
|
+
SET `extends` explicitly only when the base differs from the key name.
|
|
105
109
|
|
|
106
110
|
---
|
|
107
111
|
|
|
108
|
-
## Rule 7 — State
|
|
112
|
+
## Rule 7 — State updates via `s.update()`, NEVER mutate directly
|
|
109
113
|
|
|
110
114
|
```js
|
|
111
|
-
|
|
112
|
-
onClick: (e, el, s) => {
|
|
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
|
|
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
|
-
|
|
128
|
-
onClick: (e, el) => el.call('myFn',
|
|
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:
|
|
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
|
-
// ✅
|
|
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
|
-
|
|
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
|
|
164
|
+
## Rule 10 — `childExtends` MUST be a named string, never an inline object
|
|
150
165
|
|
|
151
|
-
Inline
|
|
166
|
+
Inline objects cause ALL property values to render as visible text on every child.
|
|
152
167
|
|
|
153
168
|
```js
|
|
154
|
-
// ✅
|
|
169
|
+
// ✅
|
|
155
170
|
childExtends: 'NavLink'
|
|
156
171
|
|
|
157
|
-
// ❌
|
|
172
|
+
// ❌ — dumps prop values as raw text
|
|
158
173
|
childExtends: {
|
|
159
174
|
tag: 'button',
|
|
160
|
-
background: 'transparent',
|
|
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
|
-
|
|
186
|
+
Use dot-notation for opacity. Use `+`/`-`/`=` for tone modifiers.
|
|
172
187
|
|
|
173
188
|
```js
|
|
174
|
-
// ✅
|
|
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
|
-
// ❌
|
|
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/
|
|
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
|
-
// ✅
|
|
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
|
-
//
|
|
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
|
|
223
|
+
## Rule 13 — CSS override precedence: component level beats props level
|
|
215
224
|
|
|
216
225
|
```js
|
|
217
|
-
// ✅
|
|
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
|
-
// ❌
|
|
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
|
|
241
|
+
## Rule 14 — Dynamic HTML attributes go in `attr: {}`, not at root
|
|
233
242
|
|
|
234
243
|
```js
|
|
235
|
-
// ✅
|
|
244
|
+
// ✅
|
|
236
245
|
export const Input = {
|
|
237
|
-
type: 'text',
|
|
246
|
+
type: 'text',
|
|
238
247
|
attr: {
|
|
239
|
-
type: ({ props }) => props.type // dynamic resolution
|
|
248
|
+
type: ({ props }) => props.type // dynamic resolution
|
|
240
249
|
}
|
|
241
250
|
}
|
|
242
251
|
|
|
243
|
-
// ❌
|
|
252
|
+
// ❌ — function at root gets stringified: type="(el)=>el.props.type"
|
|
244
253
|
export const Input = {
|
|
245
|
-
type: ({ props }) => 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 —
|
|
272
|
+
## Rule 16 — Icons use `Icon`, decorative/structural SVGs use `Svg`
|
|
264
273
|
|
|
265
|
-
|
|
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
|
-
//
|
|
269
|
-
|
|
270
|
-
folderTopRight: '<svg ...>...</svg>',
|
|
271
|
-
folderBottomLeft: '<svg ...>...</svg>',
|
|
272
|
-
}
|
|
277
|
+
// ✅ — icons: use Icon component
|
|
278
|
+
Icon: { name: 'arrowRight' }
|
|
273
279
|
|
|
274
|
-
//
|
|
280
|
+
// ✅ — decorative/structural SVG: reference from designSystem
|
|
275
281
|
Svg: {
|
|
276
|
-
src: ({ context }) => context.designSystem.
|
|
282
|
+
src: ({ context }) => context.designSystem.svg_data && context.designSystem.svg_data.folderTopRight,
|
|
277
283
|
aspectRatio: '466 / 48',
|
|
278
284
|
}
|
|
279
285
|
|
|
280
|
-
// ❌
|
|
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
|
|
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
|
|
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
|
|
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
|
-
// ✅
|
|
311
|
-
HomeView: { id: 'view-home',
|
|
312
|
-
ExploreView: { id: 'view-explore',
|
|
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
|
|
334
|
+
## Rule 19 — Conditional props: use `isX` + `'.isX'`
|
|
326
335
|
|
|
327
336
|
```js
|
|
328
|
-
// ✅
|
|
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
|
-
// ❌
|
|
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
|
|
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.
|
|
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
|
|
383
|
+
## Rule 21 — Semantic-First Architecture
|
|
375
384
|
|
|
376
|
-
Use semantic components
|
|
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
|
|
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
|
-
##
|
|
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
|
-
// ✅
|
|
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
|
-
// ❌
|
|
429
|
+
// ❌
|
|
443
430
|
Picture: { src: '/files/photo.jpg', width: '100%' }
|
|
444
431
|
```
|
|
445
432
|
|
|
446
433
|
---
|
|
447
434
|
|
|
448
|
-
## Rule
|
|
435
|
+
## Rule 24 — `Map` component key needs `tag: 'div'`
|
|
449
436
|
|
|
450
|
-
The key `Map`
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
- [ ]
|
|
476
|
-
- [ ]
|
|
477
|
-
- [ ] v3 event syntax
|
|
478
|
-
- [ ] `
|
|
479
|
-
- [ ] State updated via `
|
|
480
|
-
- [ ] `childExtends` references named component strings only
|
|
481
|
-
- [ ]
|
|
482
|
-
- [ ] Dynamic HTML attributes are in `attr: {}` block
|
|
483
|
-
- [ ]
|
|
484
|
-
- [ ]
|
|
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
|
|
487
|
-
- [ ] `Map` component key has `tag: 'div'`
|
|
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)
|