@bytefaceinc/web-lang 0.1.1

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.
@@ -0,0 +1,1433 @@
1
+ # WEB Compiler Reference For LLM Agents
2
+
3
+ This document is a compact but comprehensive reference for generating valid WEB source for the current compiler implementation.
4
+
5
+ Use this when you need to:
6
+
7
+ - write `.web` source
8
+ - predict what `compiler.js` will emit
9
+ - understand which constructs affect HTML vs CSS
10
+ - use compile-time JavaScript safely
11
+ - avoid features the compiler does not support
12
+
13
+ This is an implementation-oriented reference, not a marketing overview.
14
+
15
+ ## 1. Fast Mental Model
16
+
17
+ WEB has three practical entry points:
18
+
19
+ - legacy script mode: `layout.web` -> `index.html`, `styles.css`
20
+ - CLI mode: `home.web` -> `home.html`, `home.css`
21
+ - CLI scaffold mode: `web init [directory]` creates `index.web`, `web-lang-agents.md`, and then compiles to `index.html` plus `index.css` in the chosen project directory
22
+
23
+ Run:
24
+
25
+ ```bash
26
+ node compiler.js
27
+ web init
28
+ web init .
29
+ web init website
30
+ web home.web
31
+ web home
32
+ web .
33
+ web ./code/*
34
+ ```
35
+
36
+ The compiler pipeline is:
37
+
38
+ 1. tokenize source
39
+ 2. parse into a small AST
40
+ 3. build a variable table
41
+ 4. build a type symbol table
42
+ 5. build the HTML document model
43
+ 6. build the CSS style model
44
+ 7. emit `index.html`
45
+ 8. emit `styles.css`
46
+
47
+ Generated HTML shell:
48
+
49
+ - `<!DOCTYPE html>`
50
+ - `<html lang="en">`
51
+ - `<meta charset="UTF-8" />`
52
+ - `<meta name="viewport" content="width=device-width, initial-scale=1.0" />`
53
+ - fixed `<title>WEB Output</title>`
54
+ - fixed stylesheet link to `styles.css`
55
+ - top-level `::head` entries are rendered before `</head>`
56
+ - all rendered WEB nodes are inserted inside `<body>`
57
+ - top-level `::script` blocks are rendered near the end of `<body>`
58
+
59
+ Key idea:
60
+
61
+ - one optional top-level `define { ... }` block owns variables, type declarations, and style inheritance
62
+ - normal WEB blocks describe DOM structure and CSS
63
+ - top-level `html { ... }` and `* { ... }` describe global CSS selectors only
64
+ - `::attrs { ... }` adds real HTML attributes to normal WEB nodes
65
+ - `::head { ... }` adds literal `meta` and `link` tags to `<head>` without entering the CSS pipeline
66
+ - `::script { ... }` adds literal `<script></script>` tags without entering the CSS pipeline
67
+ - `derivedName extends baseName;` declarations still describe CSS inheritance only, and they only appear inside `define { ... }`
68
+ - `styles { ... }`, `::pseudo`, and `::media` describe CSS only
69
+ - `::keyframes` describes global animation blocks only
70
+ - `js\`...\`` runs at compile time, never in the browser
71
+
72
+ If you remember only one rule, remember this:
73
+
74
+ - HTML structure comes from normal DOM-scope paths
75
+ - CSS comes from property assignments plus style scopes, pseudo blocks, media blocks, and raw CSS
76
+
77
+ ## 2. Minimal Working Shape
78
+
79
+ This is the simplest useful WEB file shape:
80
+
81
+ ```web
82
+ define {
83
+ @pageWidth = "960px";
84
+ Main pageMain;
85
+ Heading heroTitle;
86
+ Paragraph heroCopy;
87
+ Button heroButton;
88
+ }
89
+
90
+ pageMain {
91
+ width = @pageWidth;
92
+ display = "grid";
93
+ gap = "16px";
94
+
95
+ heroTitle {
96
+ textContent = "Hello";
97
+ }
98
+
99
+ heroCopy {
100
+ textContent = "WEB compiles to HTML and CSS.";
101
+ }
102
+
103
+ heroButton {
104
+ textContent = "Launch";
105
+ padding = "12px 18px";
106
+ }
107
+ }
108
+ ```
109
+
110
+ ## 3. Top-Level Rule Order
111
+
112
+ The compiler is strict about what may appear at the top of the file.
113
+
114
+ Recommended order:
115
+
116
+ 1. one optional `define { ... }` block
117
+ 2. optional top-level global selector blocks with `html { ... }` or `* { ... }`
118
+ 3. rules and blocks
119
+ 4. optional top-level `::head` blocks
120
+ 5. optional top-level `::script` blocks
121
+ 6. top-level `::keyframes` may appear with other top-level rules, but never inside a block
122
+
123
+ Hard constraints:
124
+
125
+ - variables must be declared inside `define { ... }`
126
+ - type declarations must be declared inside `define { ... }`
127
+ - style inheritance declarations must be declared inside `define { ... }`
128
+ - `define { ... }` may appear at most once
129
+ - `define { ... }` must appear before rules
130
+ - variables cannot use `js\`...\``
131
+ - `html { ... }` and `* { ... }` are reserved for global CSS selector blocks
132
+ - `::head` must be top-level
133
+ - `::script` must be top-level
134
+ - `::keyframes` must be top-level
135
+
136
+ ## 4. Core Syntax
137
+
138
+ ### 4.1 `define { ... }`
139
+
140
+ Syntax:
141
+
142
+ ```web
143
+ define {
144
+ @space = "1rem";
145
+ Button actionButton;
146
+ actionButton heroButton;
147
+ heroButton extends actionButton;
148
+ }
149
+ ```
150
+
151
+ What belongs inside `define`:
152
+
153
+ - variable declarations
154
+ - type declarations
155
+ - style inheritance declarations
156
+
157
+ What does not belong inside `define`:
158
+
159
+ - DOM rules
160
+ - CSS property assignments
161
+ - `styles { ... }`
162
+ - `::pseudo`
163
+ - `::media`
164
+ - `::attrs`
165
+ - `::head`
166
+ - `::script`
167
+ - `::keyframes`
168
+
169
+ ### 4.2 Type declarations
170
+
171
+ Syntax:
172
+
173
+ ```web
174
+ BaseType NewType;
175
+ ```
176
+
177
+ Examples:
178
+
179
+ ```web
180
+ Button actionButton;
181
+ actionButton heroButton;
182
+ Heading2 sectionHeading;
183
+ Article featureCard;
184
+ ```
185
+
186
+ What this does:
187
+
188
+ - creates a named type alias
189
+ - allows semantic inheritance
190
+ - affects HTML tag inference
191
+
192
+ What this does not do:
193
+
194
+ - it does not automatically share CSS properties
195
+ - use `storeCard extends card;` for CSS inheritance
196
+ - it does not create output by itself
197
+
198
+ Type declarations must live inside the top-level `define { ... }` block.
199
+
200
+ ### 4.3 Style inheritance
201
+
202
+ Syntax:
203
+
204
+ ```web
205
+ derivedName extends baseName;
206
+ ```
207
+
208
+ Example:
209
+
210
+ ```web
211
+ define {
212
+ Article card;
213
+ card storeCard;
214
+ storeCard extends card;
215
+ }
216
+ ```
217
+
218
+ What this does:
219
+
220
+ - copies CSS from `card` into `storeCard`
221
+ - keeps HTML tag inference separate from CSS reuse
222
+ - supports transitive inheritance such as `featuredStoreCard extends storeCard;`
223
+
224
+ What this does not do:
225
+
226
+ - it does not change HTML tag inference by itself
227
+ - it does not create output by itself
228
+
229
+ Important behavior:
230
+
231
+ - style inheritance is CSS-only
232
+ - explicit derived styles override inherited base styles
233
+ - closer ancestors override farther ancestors
234
+ - circular style inheritance is not allowed
235
+ - inheritance applies to normal rules, descendant rules, style scopes, pseudo blocks, media blocks, and `raw`
236
+
237
+ If you want the derived name to share both:
238
+
239
+ - the same semantic HTML tag
240
+ - and the same CSS
241
+
242
+ combine type inheritance and style inheritance:
243
+
244
+ ```web
245
+ define {
246
+ Article card;
247
+ card storeCard;
248
+ storeCard extends card;
249
+ }
250
+ ```
251
+
252
+ Style inheritance declarations must live inside the top-level `define { ... }` block.
253
+
254
+ ### 4.4 `::head`
255
+
256
+ Use `::head { ... }` when you want WEB to emit literal `meta` and `link` tags inside the document head.
257
+
258
+ Syntax:
259
+
260
+ ```web
261
+ ::head {
262
+ meta {
263
+ name = "description";
264
+ content = "Landing page built in WEB.";
265
+ }
266
+
267
+ link {
268
+ rel = "help";
269
+ href = "compiler.md";
270
+ type = "text/markdown";
271
+ }
272
+ }
273
+ ```
274
+
275
+ Behavior:
276
+
277
+ - `::head` is top-level only
278
+ - it creates HTML only, not CSS
279
+ - it only supports `meta { ... }` and `link { ... }` entries
280
+ - each `meta` or `link` entry supports direct HTML attribute assignments only
281
+ - attribute names are written in camelCase and compile to kebab-case
282
+ - rendered head entries are inserted before `</head>`
283
+
284
+ ### 4.5 `::script`
285
+
286
+ Use `::script { ... }` when you want WEB to emit a literal script tag.
287
+
288
+ Syntax:
289
+
290
+ ```web
291
+ ::script {
292
+ src = "/assets/app.js";
293
+ defer = "true";
294
+
295
+ code {
296
+ function helloWorld () {
297
+ alert("hello world");
298
+ }
299
+ }
300
+ }
301
+ ```
302
+
303
+ Behavior:
304
+
305
+ - `::script` is top-level only
306
+ - it creates HTML only, not CSS
307
+ - it supports direct HTML attribute assignments
308
+ - it supports one optional `code { ... }` block
309
+ - the `code { ... }` body is copied as raw script text
310
+ - rendered script tags are inserted near the end of `<body>`
311
+
312
+ Attribute notes:
313
+
314
+ - `defer = true;` emits a bare boolean attribute
315
+ - `defer = "true";` emits `defer="true"`
316
+ - camelCase names compile to kebab-case, so `dataMode` becomes `data-mode`
317
+
318
+ ### 4.6 Global selector blocks
319
+
320
+ WEB has two special global CSS blocks:
321
+
322
+ ```web
323
+ * {
324
+ boxSizing = "border-box";
325
+ }
326
+
327
+ html {
328
+ background = "#101522";
329
+ color = "#f7f6ff";
330
+ }
331
+ ```
332
+
333
+ Behavior:
334
+
335
+ - these compile directly to `*` and `html` CSS selectors
336
+ - they are CSS-only and never create DOM nodes
337
+ - they must appear at the top level
338
+ - they are also allowed inside a top-level `::media`
339
+ - nested `html { ... }` or `* { ... }` inside an element scope is invalid
340
+
341
+ These are not normal WEB nodes. They bypass tag inference and the document model.
342
+
343
+ ### 4.7 Variables
344
+
345
+ Syntax:
346
+
347
+ ```web
348
+ define {
349
+ @name = value;
350
+ }
351
+ ```
352
+
353
+ Examples:
354
+
355
+ ```web
356
+ @pageWidth = "min(1120px, calc(100vw - 48px))";
357
+ @panelRadius = "24px";
358
+ @borderRule = "1px solid rgba(0, 0, 0, 0.08)";
359
+ ```
360
+
361
+ Rules:
362
+
363
+ - variable names must start with `@`
364
+ - variables must be declared inside `define { ... }`
365
+ - variables may reference other variables
366
+ - variables may not use `js\`...\``
367
+
368
+ ### 4.8 Dot notation
369
+
370
+ Syntax:
371
+
372
+ ```web
373
+ pageMain.heroSection.heroTitle.textContent = "Hello";
374
+ pageMain.heroSection.padding = "24px";
375
+ ```
376
+
377
+ Meaning:
378
+
379
+ - the path before the final segment identifies a node
380
+ - the final segment is the property name
381
+
382
+ ### 4.9 Nested blocks
383
+
384
+ Syntax:
385
+
386
+ ```web
387
+ pageMain {
388
+ heroSection {
389
+ heroTitle {
390
+ textContent = "Hello";
391
+ }
392
+ }
393
+ }
394
+ ```
395
+
396
+ Equivalent to dot notation:
397
+
398
+ ```web
399
+ pageMain.heroSection.heroTitle.textContent = "Hello";
400
+ ```
401
+
402
+ ### 4.10 Dotted block scopes
403
+
404
+ Also valid:
405
+
406
+ ```web
407
+ pageMain.heroSection {
408
+ heroTitle {
409
+ textContent = "Hello";
410
+ }
411
+ }
412
+ ```
413
+
414
+ ### 4.11 Bare identifiers are not valid rules
415
+
416
+ This is invalid:
417
+
418
+ ```web
419
+ pageMain
420
+ ```
421
+
422
+ After a path, the parser expects either:
423
+
424
+ - `=` for an assignment
425
+ - `{` for a block
426
+
427
+ ## 5. HTML Structure Model
428
+
429
+ WEB does not let you write literal HTML tags in normal source. It creates HTML by rebuilding a tree from rule paths.
430
+
431
+ Example:
432
+
433
+ ```web
434
+ pageMain.heroSection.heroTitle.textContent = "Hello";
435
+ pageMain.heroSection.heroButton.textContent = "Launch";
436
+ ```
437
+
438
+ This produces a DOM tree like:
439
+
440
+ - `pageMain`
441
+ - `heroSection` inside `pageMain`
442
+ - `heroTitle` inside `heroSection`
443
+ - `heroButton` inside `heroSection`
444
+
445
+ Important:
446
+
447
+ - each unique path maps to one element
448
+ - later assignments update the same node
449
+ - path identity matters more than declaration order
450
+
451
+ ## 6. HTML Tag Inference
452
+
453
+ The compiler resolves each node name through the type symbol table, then infers an HTML tag from the resolved base type.
454
+
455
+ ### 6.1 Declared names
456
+
457
+ Example:
458
+
459
+ ```web
460
+ Button heroButton;
461
+ ```
462
+
463
+ `heroButton` resolves to base type `Button`, so it renders as `<button>`.
464
+
465
+ ### 6.2 Undeclared names
466
+
467
+ Undeclared names are allowed.
468
+
469
+ Example:
470
+
471
+ ```web
472
+ pageCanvas {
473
+ width = "100%";
474
+ }
475
+ ```
476
+
477
+ This still renders, because the compiler treats unknown names as external or built-in types and then runs tag inference on the name itself.
478
+
479
+ Examples:
480
+
481
+ - `pageCanvas` -> `<div>`
482
+ - `heroSection` -> `<section>`
483
+ - `siteHeader` -> `<header>`
484
+ - `primaryButton` -> `<button>`
485
+
486
+ If nothing matches, the fallback is:
487
+
488
+ - `<div>`
489
+
490
+ ### 6.3 Built-in tag inference
491
+
492
+ Supported mappings include:
493
+
494
+ - `Heading`, `Heading1`, `H1` -> `<h1>`
495
+ - `Heading2`, `H2` -> `<h2>`
496
+ - `Heading3`, `H3` -> `<h3>`
497
+ - `Heading4`, `H4` -> `<h4>`
498
+ - `Heading5`, `H5` -> `<h5>`
499
+ - `Heading6`, `H6` -> `<h6>`
500
+ - `Button` -> `<button>`
501
+ - `Paragraph`, `Text` -> `<p>`
502
+ - `Span` -> `<span>`
503
+ - `Section` -> `<section>`
504
+ - `Main` -> `<main>`
505
+ - `Header` -> `<header>`
506
+ - `Footer` -> `<footer>`
507
+ - `Nav`, `Navigation` -> `<nav>`
508
+ - `Article` -> `<article>`
509
+ - `Aside` -> `<aside>`
510
+ - `Figure` -> `<figure>`
511
+ - `FigureCaption`, `FigCaption` -> `<figcaption>`
512
+ - `Link`, `Anchor` -> `<a>`
513
+ - `Picture` -> `<picture>`
514
+ - `Source` -> `<source>`
515
+ - `Image`, `Img` -> `<img>`
516
+ - `Video` -> `<video>`
517
+ - `Audio` -> `<audio>`
518
+ - `Form` -> `<form>`
519
+ - `Label` -> `<label>`
520
+ - `Dialog` -> `<dialog>`
521
+ - `Details` -> `<details>`
522
+ - `Summary` -> `<summary>`
523
+ - `Input` -> `<input>`
524
+ - `TextArea` -> `<textarea>`
525
+ - `Select` -> `<select>`
526
+ - `Option` -> `<option>`
527
+ - `List`, `UnorderedList`, `BulletList` -> `<ul>`
528
+ - `OrderedList`, `NumberedList` -> `<ol>`
529
+ - `ListItem` -> `<li>`
530
+ - `Table` -> `<table>`
531
+ - `TableHead` -> `<thead>`
532
+ - `TableBody` -> `<tbody>`
533
+ - `TableFoot` -> `<tfoot>`
534
+ - `TableRow` -> `<tr>`
535
+ - `TableHeaderCell` -> `<th>`
536
+ - `TableCell` -> `<td>`
537
+ - `Code` -> `<code>`
538
+ - `Pre`, `Preformatted` -> `<pre>`
539
+ - `Strong` -> `<strong>`
540
+ - `Em`, `Emphasis` -> `<em>`
541
+ - `LineBreak`, `Br` -> `<br>`
542
+
543
+ ## 7. Reserved Properties
544
+
545
+ WEB has three special property names in normal DOM scopes:
546
+
547
+ - `textContent`
548
+ - `innerHTML`
549
+ - `raw`
550
+
551
+ Everything else is treated as a CSS property.
552
+
553
+ ### 7.1 `textContent`
554
+
555
+ - inserts escaped text into the node
556
+ - safe for plain content
557
+
558
+ Example:
559
+
560
+ ```web
561
+ heroCopy.textContent = "Safe plain text";
562
+ ```
563
+
564
+ ### 7.2 `innerHTML`
565
+
566
+ - inserts raw HTML into the node
567
+ - may also accept structured fragments returned from `js\`...\``
568
+
569
+ Example:
570
+
571
+ ```web
572
+ heroCopy.innerHTML = "Read the <strong>docs</strong>.";
573
+ ```
574
+
575
+ ### 7.3 `::attrs`
576
+
577
+ Use `::attrs { ... }` when you want real HTML attributes on a normal WEB node.
578
+
579
+ Example:
580
+
581
+ ```web
582
+ docsLink {
583
+ textContent = "Docs";
584
+
585
+ ::attrs {
586
+ href = "/docs";
587
+ target = "_blank";
588
+ rel = "noreferrer";
589
+ ariaLabel = "Open docs";
590
+ dataTrack = "docs";
591
+ }
592
+ }
593
+ ```
594
+
595
+ Behavior:
596
+
597
+ - `::attrs` must be nested directly inside an element scope
598
+ - it is not allowed inside `styles { ... }`, pseudo blocks, or media blocks
599
+ - it only supports direct attribute assignments
600
+ - attribute names are written in camelCase and compile to kebab-case
601
+ - `disabled = true;` emits a bare boolean attribute
602
+ - `open = true;` is useful for `Dialog` and `Details`
603
+ - `false`, `null`, and `undefined` omit the attribute
604
+ - `class` and `style` are not allowed in `::attrs`
605
+
606
+ ### 7.4 `raw`
607
+
608
+ - inserts raw CSS associated with the current selector
609
+ - meant as an escape hatch
610
+
611
+ Example:
612
+
613
+ ```web
614
+ heroButton.raw = `
615
+ transition: transform 0.2s ease;
616
+ &:hover {
617
+ transform: translateY(-1px);
618
+ }
619
+ `;
620
+ ```
621
+
622
+ ## 8. CSS Property Model
623
+
624
+ Any non-reserved property assignment becomes CSS.
625
+
626
+ Example:
627
+
628
+ ```web
629
+ heroSection {
630
+ display = "grid";
631
+ gap = "18px";
632
+ backgroundColor = "#101522";
633
+ }
634
+ ```
635
+
636
+ Compiles to CSS similar to:
637
+
638
+ ```css
639
+ .heroSection {
640
+ display: grid;
641
+ gap: 18px;
642
+ background-color: #101522;
643
+ }
644
+ ```
645
+
646
+ Rules:
647
+
648
+ - write CSS properties in camelCase
649
+ - compiler converts them to kebab-case
650
+ - later assignments to the same property on the same rule win
651
+
652
+ ## 9. Value Types
653
+
654
+ WEB accepts these value kinds:
655
+
656
+ - quoted strings: `"grid"`
657
+ - plain template literals: `` `some text` ``
658
+ - numbers: `1`, `0.95`, `-8`
659
+ - percentages: `50%`
660
+ - bare identifiers: `none`, `auto`, `inherit`
661
+ - variable references: `@pageWidth`
662
+ - JavaScript injections: `` js`...` ``
663
+
664
+ Examples:
665
+
666
+ ```web
667
+ heroSection.display = grid;
668
+ heroSection.opacity = 0.95;
669
+ heroSection.width = 100%;
670
+ heroSection.border = @borderRule;
671
+ heroSection.fontFamily = `"Helvetica Neue", Arial, sans-serif`;
672
+ ```
673
+
674
+ Notes:
675
+
676
+ - plain template literals are just string-like values, not executed JavaScript
677
+ - object-like values are only possible through `js\`...\``
678
+
679
+ ## 9.1 Comments
680
+
681
+ WEB source supports:
682
+
683
+ - line comments with `//`
684
+ - block comments with `/* ... */`
685
+
686
+ ## 10. Selector Generation
687
+
688
+ WEB turns paths into descendant class selectors.
689
+
690
+ Example:
691
+
692
+ ```web
693
+ pageMain.heroSection.heroButton.background = "#2d5cff";
694
+ ```
695
+
696
+ Compiles to:
697
+
698
+ ```css
699
+ .pageMain .heroSection .heroButton {
700
+ background: #2d5cff;
701
+ }
702
+ ```
703
+
704
+ The generated HTML also uses the node name as the element class.
705
+
706
+ Example:
707
+
708
+ - node name `heroButton`
709
+ - generated HTML class `class="heroButton"`
710
+
711
+ ## 11. `styles { ... }` Scope
712
+
713
+ `styles { ... }` creates CSS selectors without creating HTML nodes.
714
+
715
+ Use it when:
716
+
717
+ - `innerHTML = js\`...\`` returns class names that need styling
718
+ - you want descendant CSS rules only
719
+
720
+ Example:
721
+
722
+ ```web
723
+ featureGrid {
724
+ innerHTML = js`
725
+ return node("Article", "featureCard", {
726
+ children: [
727
+ node("Heading3", "featureTitle", { textContent: "Readable" }),
728
+ ],
729
+ });
730
+ `;
731
+
732
+ styles {
733
+ featureCard {
734
+ padding = "20px";
735
+ borderRadius = "20px";
736
+ }
737
+
738
+ featureTitle {
739
+ color = "#1e2852";
740
+ }
741
+ }
742
+ }
743
+ ```
744
+
745
+ Rules:
746
+
747
+ - `styles { ... }` affects CSS only
748
+ - it does not create DOM nodes
749
+ - `textContent` is not allowed inside `styles`
750
+ - `innerHTML` is not allowed inside `styles`
751
+ - `raw` is allowed inside `styles`
752
+ - pseudo blocks are allowed inside `styles`
753
+
754
+ ## 12. Pseudo System
755
+
756
+ WEB uses a unified `::...` syntax for:
757
+
758
+ - pseudo-classes
759
+ - pseudo-elements
760
+ - `::attrs`
761
+ - media queries
762
+ - keyframes
763
+
764
+ ### 12.1 Pseudo selector blocks
765
+
766
+ Example:
767
+
768
+ ```web
769
+ buttonCard {
770
+ background = "blue";
771
+
772
+ ::hover {
773
+ background = "red";
774
+ }
775
+ }
776
+ ```
777
+
778
+ Compiles to:
779
+
780
+ ```css
781
+ .buttonCard {
782
+ background: blue;
783
+ }
784
+
785
+ .buttonCard:hover {
786
+ background: red;
787
+ }
788
+ ```
789
+
790
+ ### 12.2 Mapping rules
791
+
792
+ The compiler normalizes pseudo names like this:
793
+
794
+ - pseudo-classes use a single CSS colon
795
+ - pseudo-elements use a double CSS colon
796
+ - camelCase is converted to kebab-case
797
+
798
+ Examples:
799
+
800
+ - `::hover` -> `:hover`
801
+ - `::focusWithin` -> `:focus-within`
802
+ - `::nthChild(2n + 1)` -> `:nth-child(2n + 1)`
803
+ - `::before` -> `::before`
804
+ - `::firstLetter` -> `::first-letter`
805
+
806
+ ### 12.3 Nested pseudo descendants
807
+
808
+ Pseudo blocks attach to the scope where they are declared.
809
+
810
+ Example:
811
+
812
+ ```web
813
+ card {
814
+ badge {
815
+ opacity = 0.4;
816
+ }
817
+
818
+ ::hover {
819
+ badge {
820
+ opacity = 1;
821
+ }
822
+ }
823
+ }
824
+ ```
825
+
826
+ Compiles conceptually to:
827
+
828
+ ```css
829
+ .card .badge {
830
+ opacity: 0.4;
831
+ }
832
+
833
+ .card:hover .badge {
834
+ opacity: 1;
835
+ }
836
+ ```
837
+
838
+ ### 12.4 Placement rule
839
+
840
+ Pseudo selector blocks must be nested inside:
841
+
842
+ - an element scope
843
+ - or a `styles { ... }` scope
844
+
845
+ This is invalid:
846
+
847
+ ```web
848
+ ::hover {
849
+ background = "red";
850
+ }
851
+ ```
852
+
853
+ ## 13. `::keyframes`
854
+
855
+ `::keyframes` creates top-level animation blocks.
856
+
857
+ Example:
858
+
859
+ ```web
860
+ card {
861
+ animation = "slideIn 0.4s ease";
862
+ }
863
+
864
+ ::keyframes slideIn {
865
+ from {
866
+ opacity = 0;
867
+ transform = "translateY(18px)";
868
+ }
869
+
870
+ 50% {
871
+ opacity = 1;
872
+ }
873
+
874
+ to {
875
+ opacity = 1;
876
+ transform = "translateY(0)";
877
+ }
878
+ }
879
+ ```
880
+
881
+ Rules:
882
+
883
+ - must be top-level
884
+ - must not be inside elements
885
+ - must not be inside `styles { ... }`
886
+ - stages may only be `from`, `to`, or percentages like `50%`
887
+ - keyframe stages accept CSS property assignments only
888
+ - `textContent`, `innerHTML`, and `raw` are not allowed inside keyframe stages
889
+
890
+ ## 14. `::media`
891
+
892
+ `::media (...)` creates CSS media query scopes.
893
+
894
+ ### 14.1 Nested media
895
+
896
+ ```web
897
+ card {
898
+ padding = "20px";
899
+
900
+ ::media (max-width: 700px) {
901
+ padding = "14px";
902
+ }
903
+ }
904
+ ```
905
+
906
+ ### 14.2 Top-level media
907
+
908
+ ```web
909
+ ::media (max-width: 700px) {
910
+ pageMain {
911
+ gap = "18px";
912
+ }
913
+ }
914
+ ```
915
+
916
+ ### 14.3 Combined media and pseudo
917
+
918
+ ```web
919
+ buttonCard {
920
+ ::media (max-width: 700px) {
921
+ ::hover {
922
+ background = "#2d5cff";
923
+ }
924
+ }
925
+ }
926
+ ```
927
+
928
+ Rules:
929
+
930
+ - write the media condition in normal CSS syntax
931
+ - `::media` can contain selector blocks, style scopes, pseudo blocks, properties, and `raw`
932
+ - `textContent` and `innerHTML` are not allowed inside media scopes
933
+ - type declarations and variables are not allowed inside media scopes
934
+
935
+ ## 15. Raw CSS Escape Hatch
936
+
937
+ Use `raw` only when WEB syntax is not expressive enough.
938
+
939
+ Supported raw behaviors:
940
+
941
+ - normal declarations
942
+ - nested selectors with `&`
943
+ - nested selectors beginning with `:` or `[` attach to the parent selector
944
+ - at-rules such as `@supports`
945
+ - passthrough at-rules such as `@keyframes`, `@font-face`, `@property`, `@counter-style`, `@page`
946
+
947
+ Example:
948
+
949
+ ```web
950
+ heroButton.raw = `
951
+ transition: transform 0.25s ease;
952
+ &:hover {
953
+ transform: translateY(-2px);
954
+ }
955
+ @supports (backdrop-filter: blur(12px)) {
956
+ backdrop-filter: blur(12px);
957
+ }
958
+ `;
959
+ ```
960
+
961
+ Guideline:
962
+
963
+ - prefer normal properties first
964
+ - then `styles { ... }`
965
+ - then `::pseudo`
966
+ - then `::media`
967
+ - use `raw` last
968
+
969
+ ## 16. Compile-Time JavaScript
970
+
971
+ `js\`...\`` executes at compile time inside Node via `vm`.
972
+
973
+ It is never emitted as browser JavaScript.
974
+
975
+ Timeout:
976
+
977
+ - 1 second
978
+
979
+ Error behavior:
980
+
981
+ - runtime errors become inline text
982
+ - syntax errors become inline text
983
+ - timeouts become inline text
984
+ - JS failure does not crash compilation
985
+
986
+ ### 16.1 Result resolution order
987
+
988
+ The compiler resolves a JS block in this order:
989
+
990
+ 1. explicit `return`
991
+ 2. output collected through `emit(value)`
992
+ 3. the last top-level declared variable in the JS source
993
+ 4. empty string
994
+
995
+ Example:
996
+
997
+ ```web
998
+ heroCopy.textContent = js`
999
+ emit("Hello ");
1000
+ emit("world");
1001
+ `;
1002
+ ```
1003
+
1004
+ ### 16.2 How results are converted
1005
+
1006
+ For normal string-like contexts:
1007
+
1008
+ - strings stay strings
1009
+ - numbers become strings
1010
+ - booleans become strings
1011
+ - plain objects are JSON-stringified if possible
1012
+ - `null` and `undefined` become empty strings
1013
+
1014
+ For `innerHTML = js\`...\``:
1015
+
1016
+ - structured fragment values are preserved and rendered as HTML
1017
+
1018
+ For `textContent = js\`...\``:
1019
+
1020
+ - fragment values are not preserved as structure
1021
+ - errors become escaped text
1022
+
1023
+ ## 17. JavaScript Injection API
1024
+
1025
+ The compile-time JS sandbox exposes these helpers:
1026
+
1027
+ - `emit(value)`
1028
+ - `node(typeName, className?, options?)`
1029
+ - `el(...)` as an alias for `node(...)`
1030
+ - `fragment(children)`
1031
+ - `text(value)`
1032
+ - `html(value)`
1033
+
1034
+ The sandbox also exposes a muted `console`:
1035
+
1036
+ - `console.log`
1037
+ - `console.info`
1038
+ - `console.warn`
1039
+ - `console.error`
1040
+
1041
+ These do nothing.
1042
+
1043
+ ### 17.1 `emit(value)`
1044
+
1045
+ Appends text to an internal output buffer.
1046
+
1047
+ Rules:
1048
+
1049
+ - `emit(undefined)` and `emit(null)` do not append text
1050
+ - returns the current accumulated buffer
1051
+ - only matters if no explicit `return` overrides it
1052
+
1053
+ ### 17.2 `node(typeName, className?, options?)`
1054
+
1055
+ Creates a structured fragment node.
1056
+
1057
+ Example:
1058
+
1059
+ ```web
1060
+ cards.innerHTML = js`
1061
+ return node("Article", "featureCard", {
1062
+ children: [
1063
+ node("Heading3", "featureTitle", { textContent: "Fast" }),
1064
+ node("Paragraph", "featureCopy", { textContent: "Generated in a loop." }),
1065
+ ],
1066
+ });
1067
+ `;
1068
+ ```
1069
+
1070
+ Rules:
1071
+
1072
+ - `typeName` must be a non-empty string
1073
+ - `className` may be a string, array, or omitted
1074
+ - `options` must be an object when provided
1075
+ - `typeName` goes through the same HTML tag inference as normal WEB nodes
1076
+
1077
+ ### 17.3 `node(...)` options
1078
+
1079
+ Supported options:
1080
+
1081
+ - `className`
1082
+ - `textContent`
1083
+ - `innerHTML`
1084
+ - `children`
1085
+ - `style`
1086
+ - `attributes`
1087
+
1088
+ Details:
1089
+
1090
+ - `textContent` becomes escaped text
1091
+ - `innerHTML` is inserted raw
1092
+ - `children` may be a single child, an array, or nested arrays
1093
+ - `style` may be a string or an object
1094
+ - `attributes` must be an object
1095
+
1096
+ ### 17.4 `style` option
1097
+
1098
+ Examples:
1099
+
1100
+ ```js
1101
+ style: "padding: 20px; color: #123;"
1102
+ ```
1103
+
1104
+ ```js
1105
+ style: {
1106
+ padding: "20px",
1107
+ backgroundColor: "#eef2ff",
1108
+ }
1109
+ ```
1110
+
1111
+ Behavior:
1112
+
1113
+ - object keys are converted from camelCase to kebab-case
1114
+ - falsey values `undefined`, `null`, and `false` are omitted
1115
+ - resulting styles are emitted inline on the fragment node
1116
+
1117
+ ### 17.5 `attributes` option
1118
+
1119
+ Example:
1120
+
1121
+ ```js
1122
+ attributes: {
1123
+ href: "/docs",
1124
+ target: "_blank",
1125
+ disabled: true,
1126
+ }
1127
+ ```
1128
+
1129
+ Behavior:
1130
+
1131
+ - `undefined`, `null`, and `false` omit the attribute
1132
+ - `true` emits a bare boolean attribute
1133
+ - all other values are stringified and escaped
1134
+
1135
+ Important:
1136
+
1137
+ - normal WEB blocks support real HTML attributes through `::attrs { ... }`
1138
+ - use JS fragment `attributes` when the node itself is being generated inside `js\`...\``
1139
+
1140
+ ### 17.6 `fragment(children)`
1141
+
1142
+ Groups children without adding a wrapper element.
1143
+
1144
+ ### 17.7 `text(value)`
1145
+
1146
+ Creates an escaped text node.
1147
+
1148
+ ### 17.8 `html(value)`
1149
+
1150
+ Creates a raw HTML fragment node.
1151
+
1152
+ Use this sparingly.
1153
+
1154
+ ## 18. Void Elements
1155
+
1156
+ Void tags cannot contain content or children.
1157
+
1158
+ Relevant built-ins include:
1159
+
1160
+ - `Image` / `Img`
1161
+ - `Input`
1162
+ - `LineBreak` / `Br`
1163
+ - `Source`
1164
+
1165
+ This rule applies both to:
1166
+
1167
+ - normal WEB nodes
1168
+ - JS fragment nodes
1169
+
1170
+ ## 19. What Creates HTML vs What Creates CSS
1171
+
1172
+ This distinction is critical for agents.
1173
+
1174
+ ### Creates HTML and CSS
1175
+
1176
+ - normal assignments in DOM scopes
1177
+ - nested blocks under normal element scopes
1178
+
1179
+ ### Creates HTML only
1180
+
1181
+ - `::attrs { ... }`
1182
+ - `::head { ... }`
1183
+ - `::script { ... }`
1184
+
1185
+ ### Creates CSS only
1186
+
1187
+ - top-level `html { ... }` and `* { ... }`
1188
+ - style inheritance declarations inside `define { ... }` such as `storeCard extends card;`
1189
+ - `styles { ... }`
1190
+ - `::hover`, `::before`, other pseudo blocks
1191
+ - `::media (...)`
1192
+ - `::keyframes`
1193
+ - `raw`
1194
+
1195
+ ### Creates compile-time generated HTML
1196
+
1197
+ - `innerHTML = js\`...\`` when the JS returns strings or structured fragments
1198
+
1199
+ ## 20. Common Agent Mistakes
1200
+
1201
+ Avoid these:
1202
+
1203
+ ### Mistake 1: treating normal properties as HTML attributes
1204
+
1205
+ This does not set a real anchor `href`:
1206
+
1207
+ ```web
1208
+ docsLink.href = "/docs";
1209
+ ```
1210
+
1211
+ It becomes CSS:
1212
+
1213
+ ```css
1214
+ .docsLink {
1215
+ href: /docs;
1216
+ }
1217
+ ```
1218
+
1219
+ Use `::attrs { ... }` on normal WEB nodes:
1220
+
1221
+ ```web
1222
+ docsLink {
1223
+ ::attrs {
1224
+ href = "/docs";
1225
+ }
1226
+ }
1227
+ ```
1228
+
1229
+ Use JS fragment `attributes` only when the node is created inside `js\`...\``.
1230
+
1231
+ ### Mistake 2: putting variables after rules
1232
+
1233
+ Invalid:
1234
+
1235
+ ```web
1236
+ pageMain.width = "100%";
1237
+ define {
1238
+ @pageWidth = "960px";
1239
+ }
1240
+ ```
1241
+
1242
+ ### Mistake 3: putting `::keyframes` inside a block
1243
+
1244
+ Invalid:
1245
+
1246
+ ```web
1247
+ card {
1248
+ ::keyframes fadeIn {
1249
+ from {
1250
+ opacity = 0;
1251
+ }
1252
+ }
1253
+ }
1254
+ ```
1255
+
1256
+ ### Mistake 4: using `textContent` inside `styles`
1257
+
1258
+ Invalid:
1259
+
1260
+ ```web
1261
+ heroSection {
1262
+ styles {
1263
+ title {
1264
+ textContent = "Hello";
1265
+ }
1266
+ }
1267
+ }
1268
+ ```
1269
+
1270
+ ### Mistake 5: confusing type inheritance with style inheritance
1271
+
1272
+ This is false today.
1273
+
1274
+ Type inheritance affects:
1275
+
1276
+ - tag resolution
1277
+ - semantic naming
1278
+
1279
+ It does not affect CSS sharing.
1280
+
1281
+ Use this for semantic inheritance:
1282
+
1283
+ ```web
1284
+ card storeCard;
1285
+ ```
1286
+
1287
+ Use this for CSS inheritance:
1288
+
1289
+ ```web
1290
+ storeCard extends card;
1291
+ ```
1292
+
1293
+ Use both if you want both.
1294
+
1295
+ Type declarations alone do not affect:
1296
+
1297
+ - CSS property inheritance inside the compiler
1298
+
1299
+ ### Mistake 6: overusing declarations that are not needed
1300
+
1301
+ If a node name already infers the right tag, you may omit the declaration.
1302
+
1303
+ Example:
1304
+
1305
+ - `heroSection` already infers `<section>`
1306
+ - `siteHeader` already infers `<header>`
1307
+ - `pageCanvas` falls back to `<div>`
1308
+
1309
+ ## 21. Recommended Authoring Strategy For Agents
1310
+
1311
+ When generating new WEB code:
1312
+
1313
+ 1. declare only the types that improve semantics or clarity
1314
+ 2. use undeclared names freely for wrapper `<div>`s
1315
+ 3. prefer nested blocks for readability
1316
+ 4. put variables, type declarations, and style inheritance inside one `define { ... }` block
1317
+ 5. use variables for repeated primitive values
1318
+ 6. use normal property assignments before raw CSS
1319
+ 7. use `::attrs { ... }` for real HTML attributes on normal WEB nodes
1320
+ 8. use `styles { ... }` for classes created by JS fragments
1321
+ 9. use `::pseudo` and `::media` before `raw`
1322
+ 10. use `js\`...\`` only when loops, conditionals, or fragment generation are genuinely needed
1323
+
1324
+ ## 22. Canonical Authoring Template
1325
+
1326
+ ```web
1327
+ define {
1328
+ @pageWidth = "min(1120px, calc(100vw - 48px))";
1329
+ @panelRadius = "24px";
1330
+ @borderRule = "1px solid rgba(0, 0, 0, 0.08)";
1331
+
1332
+ Main pageMain;
1333
+ Article card;
1334
+ card storeCard;
1335
+ storeCard extends card;
1336
+ Heading heroTitle;
1337
+ Paragraph heroCopy;
1338
+ Button heroButton;
1339
+ }
1340
+
1341
+ pageMain {
1342
+ width = @pageWidth;
1343
+ display = "grid";
1344
+ gap = "24px";
1345
+
1346
+ heroSection {
1347
+ padding = "32px";
1348
+ border = @borderRule;
1349
+ borderRadius = @panelRadius;
1350
+
1351
+ heroLink {
1352
+ textContent = "Docs";
1353
+
1354
+ ::attrs {
1355
+ href = "/docs";
1356
+ target = "_blank";
1357
+ }
1358
+ }
1359
+
1360
+ heroTitle {
1361
+ textContent = "Build with WEB";
1362
+ }
1363
+
1364
+ heroCopy {
1365
+ textContent = "One source file, compiled to HTML and CSS.";
1366
+ }
1367
+
1368
+ heroButton {
1369
+ textContent = "Launch";
1370
+
1371
+ ::hover {
1372
+ transform = "translateY(-1px)";
1373
+ }
1374
+ }
1375
+ }
1376
+
1377
+ featureGrid {
1378
+ innerHTML = js`
1379
+ const items = ["Readable", "Composable", "Static"];
1380
+ return items.map((label) =>
1381
+ node("Article", "featureCard", {
1382
+ children: [
1383
+ node("Heading3", "featureTitle", { textContent: label }),
1384
+ ],
1385
+ })
1386
+ );
1387
+ `;
1388
+
1389
+ styles {
1390
+ featureCard {
1391
+ padding = "20px";
1392
+ border = @borderRule;
1393
+ }
1394
+ }
1395
+ }
1396
+ }
1397
+
1398
+ ::media (max-width: 700px) {
1399
+ pageMain {
1400
+ gap = "16px";
1401
+ }
1402
+ }
1403
+ ```
1404
+
1405
+ ## 23. Summary
1406
+
1407
+ WEB is a path-based declarative language with:
1408
+
1409
+ - optional semantic type declarations
1410
+ - one optional top-level `define { ... }` prelude
1411
+ - variables inside `define`
1412
+ - CSS inheritance declarations with `extends` inside `define`
1413
+ - nested or dotted rule syntax
1414
+ - real HTML attributes through `::attrs`
1415
+ - literal head tags through top-level `::head`
1416
+ - literal script tags through top-level `::script`
1417
+ - CSS property generation
1418
+ - CSS-only `styles { ... }` scopes
1419
+ - first-class `::pseudo`
1420
+ - top-level `::keyframes`
1421
+ - first-class `::media`
1422
+ - compile-time `js` with a small fragment API
1423
+ - `raw` as a last-resort CSS escape hatch
1424
+
1425
+ The most important operational truth for agents is:
1426
+
1427
+ - normal scopes create DOM
1428
+ - `::attrs` adds HTML attributes to DOM nodes
1429
+ - `::head` adds literal meta/link tags to the document head
1430
+ - `::script` adds literal script tags to the HTML output
1431
+ - style scopes and pseudo/media scopes create CSS
1432
+ - JS runs only at compile time
1433
+ - JS fragment `attributes` is only needed when the node itself is generated inside `js\`...\``