@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.
- package/LICENSE +21 -0
- package/README.md +277 -0
- package/bin/web.js +10 -0
- package/compiler.js +4602 -0
- package/docs/cli.md +657 -0
- package/docs/compiler.md +1433 -0
- package/docs/error-handling.md +863 -0
- package/docs/getting-started.md +805 -0
- package/docs/language-guide.md +945 -0
- package/lib/cli/commands/compile.js +127 -0
- package/lib/cli/commands/init.js +172 -0
- package/lib/cli/commands/screenshot.js +257 -0
- package/lib/cli/commands/watch.js +458 -0
- package/lib/cli/compile-service.js +19 -0
- package/lib/cli/compile-worker.js +32 -0
- package/lib/cli/compiler-runner.js +37 -0
- package/lib/cli/index.js +154 -0
- package/lib/cli/init-service.js +204 -0
- package/lib/cli/screenshot-artifacts.js +81 -0
- package/lib/cli/screenshot-service.js +153 -0
- package/lib/cli/shared.js +261 -0
- package/lib/cli/targets.js +199 -0
- package/lib/cli/watch-settings.js +37 -0
- package/package.json +50 -0
|
@@ -0,0 +1,805 @@
|
|
|
1
|
+
# WEB Getting Started
|
|
2
|
+
|
|
3
|
+
This guide walks through building a simple one-page website in WEB from both a designer and developer perspective.
|
|
4
|
+
|
|
5
|
+
The goal is not just to show syntax. It is to show:
|
|
6
|
+
|
|
7
|
+
- what to type
|
|
8
|
+
- why each part exists
|
|
9
|
+
- which features matter most when you build a real page
|
|
10
|
+
|
|
11
|
+
By the end, you will have a small landing page with:
|
|
12
|
+
|
|
13
|
+
- semantic structure
|
|
14
|
+
- design tokens
|
|
15
|
+
- shared button and card styles
|
|
16
|
+
- real HTML attributes
|
|
17
|
+
- metadata in `<head>`
|
|
18
|
+
- hover states
|
|
19
|
+
- responsive behavior
|
|
20
|
+
- one small compile-time JavaScript example
|
|
21
|
+
- one emitted browser `<script>` tag
|
|
22
|
+
|
|
23
|
+
## 1. Why WEB Feels Good For One-Page Sites
|
|
24
|
+
|
|
25
|
+
### Designer perspective
|
|
26
|
+
|
|
27
|
+
WEB keeps structure and styling close together, so you can think in sections, cards, buttons, spacing, and hierarchy without bouncing across multiple files too early.
|
|
28
|
+
|
|
29
|
+
That is useful when you are:
|
|
30
|
+
|
|
31
|
+
- trying different page sections quickly
|
|
32
|
+
- tuning rhythm, spacing, and emphasis
|
|
33
|
+
- keeping a layout consistent while the content is still moving
|
|
34
|
+
|
|
35
|
+
### Developer perspective
|
|
36
|
+
|
|
37
|
+
WEB still compiles to normal HTML and CSS, but it gives you a cleaner authoring model for:
|
|
38
|
+
|
|
39
|
+
- semantic tags through type declarations
|
|
40
|
+
- reusable patterns through shared selectors and style inheritance
|
|
41
|
+
- real HTML attributes through `::attrs`
|
|
42
|
+
- metadata through `::head`
|
|
43
|
+
- browser scripts through `::script`
|
|
44
|
+
- responsive rules through `::media`
|
|
45
|
+
|
|
46
|
+
In other words, you get a single source file for authoring, but the output is still web-native.
|
|
47
|
+
|
|
48
|
+
## 2. What We Are Building
|
|
49
|
+
|
|
50
|
+
We will make a simple studio-style landing page with:
|
|
51
|
+
|
|
52
|
+
1. a top navigation
|
|
53
|
+
2. a hero section
|
|
54
|
+
3. a three-card feature section
|
|
55
|
+
4. a final call-to-action section
|
|
56
|
+
5. a footer note
|
|
57
|
+
|
|
58
|
+
If you want a starter project generated for you first, run:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
web init
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
or, if you want to be explicit about the current directory:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
web init .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
or, if you want WEB to create a new project folder for you:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
web init website
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
That creates:
|
|
77
|
+
|
|
78
|
+
- `index.web`
|
|
79
|
+
- `index.html`
|
|
80
|
+
- `index.css`
|
|
81
|
+
- `web-lang-agents.md`
|
|
82
|
+
|
|
83
|
+
The generated `web-lang-agents.md` combines the packaged getting-started guide, CLI guide, and compiler reference into one file so local agents can load the current WEB context quickly.
|
|
84
|
+
|
|
85
|
+
If you want to follow along exactly, create a file called:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
studio.web
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 3. Compile It
|
|
92
|
+
|
|
93
|
+
Once your file exists, compile it with the CLI:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
web studio.web
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
or:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
web studio
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
That will generate:
|
|
106
|
+
|
|
107
|
+
- `studio.html`
|
|
108
|
+
- `studio.css`
|
|
109
|
+
|
|
110
|
+
Capture a screenshot of the compiled page:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
web screenshot studio.web
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Keep the file rebuilding while you work:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
web watch studio.web
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Keep a watch session running with baseline and timed screenshots:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
web watch studio.web -s
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 4. A Complete Working Example
|
|
129
|
+
|
|
130
|
+
Copy this into `studio.web`:
|
|
131
|
+
|
|
132
|
+
```web
|
|
133
|
+
define {
|
|
134
|
+
// Shared design tokens.
|
|
135
|
+
@surface = "#0f172a";
|
|
136
|
+
@surfaceSoft = "rgba(255, 255, 255, 0.08)";
|
|
137
|
+
@textStrong = "#f8fafc";
|
|
138
|
+
@textMuted = "#cbd5e1";
|
|
139
|
+
@accent = "#67e8f9";
|
|
140
|
+
@accentStrong = "#22d3ee";
|
|
141
|
+
@cardBorder = "1px solid rgba(255, 255, 255, 0.12)";
|
|
142
|
+
@shadow = "0 24px 80px rgba(2, 8, 23, 0.35)";
|
|
143
|
+
@radiusLarge = "28px";
|
|
144
|
+
@radiusPill = "999px";
|
|
145
|
+
@sectionGap = "clamp(3rem, 8vw, 6rem)";
|
|
146
|
+
|
|
147
|
+
// Semantic page structure.
|
|
148
|
+
Main pageMain;
|
|
149
|
+
Nav siteNav;
|
|
150
|
+
Link brandLink;
|
|
151
|
+
Link workLink;
|
|
152
|
+
Link contactLink;
|
|
153
|
+
|
|
154
|
+
// A reusable button pattern.
|
|
155
|
+
Link buttonBase;
|
|
156
|
+
buttonBase primaryLink;
|
|
157
|
+
buttonBase secondaryLink;
|
|
158
|
+
primaryLink extends buttonBase;
|
|
159
|
+
secondaryLink extends buttonBase;
|
|
160
|
+
|
|
161
|
+
// Hero content.
|
|
162
|
+
Section heroSection;
|
|
163
|
+
Span heroEyebrow;
|
|
164
|
+
Heading1 heroTitle;
|
|
165
|
+
Paragraph heroCopy;
|
|
166
|
+
|
|
167
|
+
// Feature content.
|
|
168
|
+
Section featureSection;
|
|
169
|
+
Span featureEyebrow;
|
|
170
|
+
Heading2 featureTitle;
|
|
171
|
+
Paragraph featureCopy;
|
|
172
|
+
Article featureCard;
|
|
173
|
+
featureCard clarityCard;
|
|
174
|
+
featureCard speedCard;
|
|
175
|
+
featureCard handoffCard;
|
|
176
|
+
clarityCard extends featureCard;
|
|
177
|
+
speedCard extends featureCard;
|
|
178
|
+
handoffCard extends featureCard;
|
|
179
|
+
|
|
180
|
+
// Call to action.
|
|
181
|
+
Section ctaSection;
|
|
182
|
+
Heading2 ctaTitle;
|
|
183
|
+
Paragraph ctaCopy;
|
|
184
|
+
|
|
185
|
+
// Footer.
|
|
186
|
+
Footer pageFooter;
|
|
187
|
+
Paragraph footerNote;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Document metadata.
|
|
191
|
+
::head {
|
|
192
|
+
meta {
|
|
193
|
+
name = "description";
|
|
194
|
+
content = "A simple one-page portfolio layout built in WEB.";
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
meta {
|
|
198
|
+
name = "theme-color";
|
|
199
|
+
content = "#0f172a";
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Browser-side JavaScript emitted near the end of <body>.
|
|
204
|
+
::script {
|
|
205
|
+
code {
|
|
206
|
+
document.documentElement.dataset.webReady = "true";
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Global CSS.
|
|
211
|
+
* {
|
|
212
|
+
boxSizing = "border-box";
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
html {
|
|
216
|
+
background = "radial-gradient(circle at top, #12213f 0%, #0f172a 55%, #020617 100%)";
|
|
217
|
+
color = @textStrong;
|
|
218
|
+
fontFamily = "Inter, \"Segoe UI\", sans-serif";
|
|
219
|
+
lineHeight = 1.5;
|
|
220
|
+
scrollBehavior = "smooth";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Shared button styles. Derived links inherit these.
|
|
224
|
+
buttonBase {
|
|
225
|
+
display = "inline-flex";
|
|
226
|
+
alignItems = "center";
|
|
227
|
+
justifyContent = "center";
|
|
228
|
+
padding = "0.95rem 1.25rem";
|
|
229
|
+
border = @cardBorder;
|
|
230
|
+
borderRadius = @radiusPill;
|
|
231
|
+
textDecoration = "none";
|
|
232
|
+
fontWeight = 700;
|
|
233
|
+
transition = "transform 0.2s ease, background 0.2s ease, color 0.2s ease";
|
|
234
|
+
raw = `
|
|
235
|
+
&:focus-visible {
|
|
236
|
+
outline: 3px solid rgba(255, 255, 255, 0.72);
|
|
237
|
+
outline-offset: 4px;
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
primaryLink {
|
|
243
|
+
background = @accent;
|
|
244
|
+
color = "#06202a";
|
|
245
|
+
|
|
246
|
+
::hover {
|
|
247
|
+
background = @accentStrong;
|
|
248
|
+
transform = "translateY(-2px)";
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
secondaryLink {
|
|
253
|
+
background = "transparent";
|
|
254
|
+
color = @textStrong;
|
|
255
|
+
|
|
256
|
+
::hover {
|
|
257
|
+
background = @surfaceSoft;
|
|
258
|
+
transform = "translateY(-2px)";
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Shared card states.
|
|
263
|
+
featureCard {
|
|
264
|
+
transition = "transform 0.2s ease, border-color 0.2s ease";
|
|
265
|
+
|
|
266
|
+
::hover {
|
|
267
|
+
transform = "translateY(-4px)";
|
|
268
|
+
borderColor = "rgba(103, 232, 249, 0.35)";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
pageMain {
|
|
273
|
+
width = "min(100% - 2rem, 1120px)";
|
|
274
|
+
margin = "0 auto";
|
|
275
|
+
padding = "1.5rem 0 4rem";
|
|
276
|
+
display = "grid";
|
|
277
|
+
gap = @sectionGap;
|
|
278
|
+
|
|
279
|
+
siteNav {
|
|
280
|
+
display = "flex";
|
|
281
|
+
alignItems = "center";
|
|
282
|
+
gap = "1rem";
|
|
283
|
+
padding = "1rem 1.25rem";
|
|
284
|
+
background = @surfaceSoft;
|
|
285
|
+
border = @cardBorder;
|
|
286
|
+
borderRadius = @radiusLarge;
|
|
287
|
+
boxShadow = @shadow;
|
|
288
|
+
|
|
289
|
+
brandLink {
|
|
290
|
+
textContent = "Northstar Studio";
|
|
291
|
+
fontWeight = 800;
|
|
292
|
+
letterSpacing = "0.04em";
|
|
293
|
+
|
|
294
|
+
::attrs {
|
|
295
|
+
href = "#top";
|
|
296
|
+
ariaLabel = "Back to the top of the page";
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
workLink {
|
|
301
|
+
textContent = "Process";
|
|
302
|
+
marginLeft = "auto";
|
|
303
|
+
color = @textMuted;
|
|
304
|
+
textDecoration = "none";
|
|
305
|
+
|
|
306
|
+
::attrs {
|
|
307
|
+
href = "#features";
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
contactLink {
|
|
312
|
+
textContent = "Contact";
|
|
313
|
+
color = @textMuted;
|
|
314
|
+
textDecoration = "none";
|
|
315
|
+
|
|
316
|
+
::attrs {
|
|
317
|
+
href = "#cta";
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
heroSection {
|
|
323
|
+
display = "grid";
|
|
324
|
+
gap = "1.25rem";
|
|
325
|
+
padding = "clamp(2rem, 6vw, 4.5rem)";
|
|
326
|
+
background = "linear-gradient(180deg, rgba(15, 23, 42, 0.92) 0%, rgba(15, 23, 42, 0.72) 100%)";
|
|
327
|
+
border = @cardBorder;
|
|
328
|
+
borderRadius = @radiusLarge;
|
|
329
|
+
boxShadow = @shadow;
|
|
330
|
+
|
|
331
|
+
::attrs {
|
|
332
|
+
id = "top";
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
heroEyebrow {
|
|
336
|
+
textContent = "Designer-friendly. Developer-ready.";
|
|
337
|
+
color = @accent;
|
|
338
|
+
fontWeight = 700;
|
|
339
|
+
letterSpacing = "0.08em";
|
|
340
|
+
textTransform = "uppercase";
|
|
341
|
+
fontSize = "0.85rem";
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
heroTitle {
|
|
345
|
+
textContent = "Build a polished one-page website from one WEB file.";
|
|
346
|
+
margin = "0";
|
|
347
|
+
maxWidth = "12ch";
|
|
348
|
+
fontSize = "clamp(2.8rem, 7vw, 5rem)";
|
|
349
|
+
lineHeight = 1;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
heroCopy {
|
|
353
|
+
innerHTML = "Use semantic structure, shared styles, metadata, responsive rules, and compile-time helpers without splitting your thinking across many files.";
|
|
354
|
+
margin = "0";
|
|
355
|
+
maxWidth = "62ch";
|
|
356
|
+
color = @textMuted;
|
|
357
|
+
fontSize = "1.05rem";
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Names like heroActions and featureGrid are handy layout wrappers.
|
|
361
|
+
// If a name does not map to a special built-in tag, WEB falls back to <div>.
|
|
362
|
+
heroActions {
|
|
363
|
+
display = "flex";
|
|
364
|
+
flexWrap = "wrap";
|
|
365
|
+
gap = "0.75rem";
|
|
366
|
+
marginTop = "0.5rem";
|
|
367
|
+
|
|
368
|
+
primaryLink {
|
|
369
|
+
textContent = "See the walkthrough";
|
|
370
|
+
|
|
371
|
+
::attrs {
|
|
372
|
+
href = "#features";
|
|
373
|
+
ariaLabel = "Jump to the feature walkthrough section";
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
secondaryLink {
|
|
378
|
+
textContent = "Start a project";
|
|
379
|
+
|
|
380
|
+
::attrs {
|
|
381
|
+
href = "#cta";
|
|
382
|
+
ariaLabel = "Jump to the contact section";
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
featureSection {
|
|
389
|
+
display = "grid";
|
|
390
|
+
gap = "1rem";
|
|
391
|
+
|
|
392
|
+
::attrs {
|
|
393
|
+
id = "features";
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
featureEyebrow {
|
|
397
|
+
textContent = "Why this layout works";
|
|
398
|
+
color = @accent;
|
|
399
|
+
fontWeight = 700;
|
|
400
|
+
letterSpacing = "0.08em";
|
|
401
|
+
textTransform = "uppercase";
|
|
402
|
+
fontSize = "0.85rem";
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
featureTitle {
|
|
406
|
+
textContent = "A clean one-page structure that supports both craft and handoff.";
|
|
407
|
+
margin = "0";
|
|
408
|
+
maxWidth = "16ch";
|
|
409
|
+
fontSize = "clamp(2rem, 5vw, 3.5rem)";
|
|
410
|
+
lineHeight = 1.05;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
featureCopy {
|
|
414
|
+
textContent = "The sections below show the core ideas you will use most often in WEB: shared tokens, semantic structure, HTML attributes, hover states, responsive rules, and small compile-time touches.";
|
|
415
|
+
margin = "0";
|
|
416
|
+
maxWidth = "70ch";
|
|
417
|
+
color = @textMuted;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
featureGrid {
|
|
421
|
+
display = "grid";
|
|
422
|
+
gridTemplateColumns = "repeat(3, minmax(0, 1fr))";
|
|
423
|
+
gap = "1rem";
|
|
424
|
+
marginTop = "0.75rem";
|
|
425
|
+
|
|
426
|
+
clarityCard {
|
|
427
|
+
innerHTML = "<h3>One file, less context switching</h3><p>Structure and styling stay close together, which makes early design exploration faster and calmer.</p>";
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
speedCard {
|
|
431
|
+
innerHTML = "<h3>Reusable patterns without a framework</h3><p>Base selectors and style inheritance let you keep buttons and cards consistent without repeating yourself.</p>";
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
handoffCard {
|
|
435
|
+
innerHTML = "<h3>Better developer handoff</h3><p>Metadata, attributes, responsive rules, and browser script output all stay explicit and easy to review.</p>";
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
ctaSection {
|
|
441
|
+
padding = "clamp(2rem, 5vw, 3rem)";
|
|
442
|
+
background = @surfaceSoft;
|
|
443
|
+
border = @cardBorder;
|
|
444
|
+
borderRadius = @radiusLarge;
|
|
445
|
+
boxShadow = @shadow;
|
|
446
|
+
display = "grid";
|
|
447
|
+
gap = "1rem";
|
|
448
|
+
|
|
449
|
+
::attrs {
|
|
450
|
+
id = "cta";
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
ctaTitle {
|
|
454
|
+
textContent = "Ready to adapt this into your own landing page?";
|
|
455
|
+
margin = "0";
|
|
456
|
+
fontSize = "clamp(1.8rem, 4vw, 2.8rem)";
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
ctaCopy {
|
|
460
|
+
textContent = "Swap the copy, colors, and sections first. Once the layout feels right, add more states, animations, or generated content as needed.";
|
|
461
|
+
margin = "0";
|
|
462
|
+
maxWidth = "58ch";
|
|
463
|
+
color = @textMuted;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
primaryLink {
|
|
467
|
+
textContent = "Book a kickoff call";
|
|
468
|
+
|
|
469
|
+
::attrs {
|
|
470
|
+
href = "mailto:hello@northstar.studio";
|
|
471
|
+
ariaLabel = "Email Northstar Studio to start a project";
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
pageFooter {
|
|
477
|
+
paddingTop = "0.5rem";
|
|
478
|
+
borderTop = @cardBorder;
|
|
479
|
+
|
|
480
|
+
footerNote {
|
|
481
|
+
textContent = js`
|
|
482
|
+
const year = new Date().getFullYear();
|
|
483
|
+
return "Prototype generated in " + year + " with WEB.";
|
|
484
|
+
`;
|
|
485
|
+
margin = "0";
|
|
486
|
+
color = @textMuted;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// CSS-only styling for cards inside the grid.
|
|
492
|
+
featureGrid {
|
|
493
|
+
styles {
|
|
494
|
+
featureCard {
|
|
495
|
+
padding = "1.5rem";
|
|
496
|
+
background = @surfaceSoft;
|
|
497
|
+
border = @cardBorder;
|
|
498
|
+
borderRadius = "22px";
|
|
499
|
+
minHeight = "220px";
|
|
500
|
+
boxShadow = @shadow;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Responsive behavior.
|
|
506
|
+
::media (max-width: 820px) {
|
|
507
|
+
pageMain {
|
|
508
|
+
width = "min(100% - 1.25rem, 1120px)";
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
pageMain.siteNav {
|
|
512
|
+
flexWrap = "wrap";
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
pageMain.heroSection {
|
|
516
|
+
padding = "1.5rem";
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
pageMain.heroSection.heroActions {
|
|
520
|
+
flexDirection = "column";
|
|
521
|
+
alignItems = "stretch";
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
pageMain.featureSection.featureGrid {
|
|
525
|
+
gridTemplateColumns = "1fr";
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
buttonBase {
|
|
529
|
+
width = "100%";
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
This example was checked against the current compiler, so it is meant to be copy-pasteable.
|
|
535
|
+
|
|
536
|
+
## 5. How The Example Is Structured
|
|
537
|
+
|
|
538
|
+
### `define { ... }` gives you vocabulary
|
|
539
|
+
|
|
540
|
+
Why it matters:
|
|
541
|
+
|
|
542
|
+
- designers get a simple place for tokens like colors, radius, and spacing
|
|
543
|
+
- developers get semantic names that resolve to real HTML tags
|
|
544
|
+
- both sides get a shared vocabulary for the page
|
|
545
|
+
|
|
546
|
+
The `define` block does three important jobs:
|
|
547
|
+
|
|
548
|
+
1. stores reusable values like `@accent` and `@shadow`
|
|
549
|
+
2. declares semantic objects like `Main pageMain;` and `Section heroSection;`
|
|
550
|
+
3. creates shared style families like `buttonBase`, `primaryLink`, and `secondaryLink`
|
|
551
|
+
|
|
552
|
+
If you remember one thing, remember this: `define` is where you name the system before you build the page.
|
|
553
|
+
|
|
554
|
+
### `::head` handles page metadata
|
|
555
|
+
|
|
556
|
+
Why it matters:
|
|
557
|
+
|
|
558
|
+
- designers care about how the page presents itself in tabs, previews, and browser chrome
|
|
559
|
+
- developers need a clean place for metadata that belongs in `<head>`
|
|
560
|
+
|
|
561
|
+
In this example, `::head` adds:
|
|
562
|
+
|
|
563
|
+
- a description
|
|
564
|
+
- a theme color
|
|
565
|
+
|
|
566
|
+
Use `::head` when information belongs to the document itself, not to a specific section of the page.
|
|
567
|
+
|
|
568
|
+
### Global blocks set the canvas
|
|
569
|
+
|
|
570
|
+
Why it matters:
|
|
571
|
+
|
|
572
|
+
- `* { ... }` is useful for universal layout corrections like `box-sizing`
|
|
573
|
+
- `html { ... }` is your whole-page canvas for background, type, and general feel
|
|
574
|
+
|
|
575
|
+
This is where the page starts to feel designed instead of just structured.
|
|
576
|
+
|
|
577
|
+
### Normal blocks create the page structure
|
|
578
|
+
|
|
579
|
+
Why it matters:
|
|
580
|
+
|
|
581
|
+
- this is the core WEB authoring model
|
|
582
|
+
- your page structure and much of your styling are expressed together
|
|
583
|
+
|
|
584
|
+
Inside `pageMain`, we build:
|
|
585
|
+
|
|
586
|
+
- `siteNav`
|
|
587
|
+
- `heroSection`
|
|
588
|
+
- `featureSection`
|
|
589
|
+
- `ctaSection`
|
|
590
|
+
- `pageFooter`
|
|
591
|
+
|
|
592
|
+
That keeps the page easy to scan because the source file mirrors the visual hierarchy.
|
|
593
|
+
|
|
594
|
+
### `::attrs` adds real HTML behavior
|
|
595
|
+
|
|
596
|
+
Why it matters:
|
|
597
|
+
|
|
598
|
+
- designers often need anchor jumps, labels, and IDs for navigation
|
|
599
|
+
- developers need real HTML attributes, not just visual styling
|
|
600
|
+
|
|
601
|
+
We use `::attrs` for:
|
|
602
|
+
|
|
603
|
+
- `href`
|
|
604
|
+
- `ariaLabel`
|
|
605
|
+
- `id`
|
|
606
|
+
|
|
607
|
+
This is the bridge between a styled layout and an actually usable webpage.
|
|
608
|
+
|
|
609
|
+
### Shared styles reduce repetition
|
|
610
|
+
|
|
611
|
+
Why it matters:
|
|
612
|
+
|
|
613
|
+
- design systems live or die on consistency
|
|
614
|
+
- repeated button and card rules are a maintenance trap
|
|
615
|
+
|
|
616
|
+
The example uses:
|
|
617
|
+
|
|
618
|
+
- `buttonBase` for shared button-like link styles
|
|
619
|
+
- `primaryLink extends buttonBase;`
|
|
620
|
+
- `secondaryLink extends buttonBase;`
|
|
621
|
+
- `featureCard` as a shared card family
|
|
622
|
+
|
|
623
|
+
That means you style the pattern once, then specialize the variants.
|
|
624
|
+
|
|
625
|
+
### `styles { ... }` is great for selector-only styling
|
|
626
|
+
|
|
627
|
+
Why it matters:
|
|
628
|
+
|
|
629
|
+
- sometimes you want styling that depends on where something lives
|
|
630
|
+
- you may not want to attach every shared style directly to the node declaration itself
|
|
631
|
+
|
|
632
|
+
In the example:
|
|
633
|
+
|
|
634
|
+
```web
|
|
635
|
+
featureGrid {
|
|
636
|
+
styles {
|
|
637
|
+
featureCard {
|
|
638
|
+
...
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
This says: “when a `featureCard` appears inside `featureGrid`, style it like this.”
|
|
645
|
+
|
|
646
|
+
That is useful when a pattern changes by context.
|
|
647
|
+
|
|
648
|
+
### `::hover` adds state without leaving WEB
|
|
649
|
+
|
|
650
|
+
Why it matters:
|
|
651
|
+
|
|
652
|
+
- interactive polish matters even on a small one-page layout
|
|
653
|
+
- hover states help communicate hierarchy and clickability
|
|
654
|
+
|
|
655
|
+
We use `::hover` for:
|
|
656
|
+
|
|
657
|
+
- button lift and color changes
|
|
658
|
+
- card lift and border emphasis
|
|
659
|
+
|
|
660
|
+
This gives the page enough interaction to feel intentional without adding heavy complexity.
|
|
661
|
+
|
|
662
|
+
### `raw` is your escape hatch
|
|
663
|
+
|
|
664
|
+
Why it matters:
|
|
665
|
+
|
|
666
|
+
- sometimes the cleanest WEB syntax is not enough for one detail
|
|
667
|
+
- you still need a safe way to express real CSS when necessary
|
|
668
|
+
|
|
669
|
+
The example uses `raw` for:
|
|
670
|
+
|
|
671
|
+
- `:focus-visible` styling on the shared button base
|
|
672
|
+
|
|
673
|
+
That is a good example of when `raw` is appropriate: a small, targeted enhancement that would be awkward to express otherwise.
|
|
674
|
+
|
|
675
|
+
### `::media` keeps the layout responsive
|
|
676
|
+
|
|
677
|
+
Why it matters:
|
|
678
|
+
|
|
679
|
+
- a one-page site has to work on phones, not just desktops
|
|
680
|
+
- responsive rules should be easy to find
|
|
681
|
+
|
|
682
|
+
In the example, the media block:
|
|
683
|
+
|
|
684
|
+
- wraps the nav if needed
|
|
685
|
+
- reduces hero padding
|
|
686
|
+
- stacks the hero actions
|
|
687
|
+
- collapses the feature grid to one column
|
|
688
|
+
- makes button patterns full-width on smaller screens
|
|
689
|
+
|
|
690
|
+
This is the moment where the page becomes production-minded rather than just visually nice.
|
|
691
|
+
|
|
692
|
+
### Compile-Time `js` Literals Help With Small Generated Values
|
|
693
|
+
|
|
694
|
+
Why it matters:
|
|
695
|
+
|
|
696
|
+
- sometimes you want a tiny generated value without introducing browser runtime logic
|
|
697
|
+
|
|
698
|
+
The footer note uses:
|
|
699
|
+
|
|
700
|
+
```web
|
|
701
|
+
footerNote.textContent = js`
|
|
702
|
+
const year = new Date().getFullYear();
|
|
703
|
+
return "Prototype generated in " + year + " with WEB.";
|
|
704
|
+
`;
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
That runs at compile time, not in the browser.
|
|
708
|
+
|
|
709
|
+
Use this for:
|
|
710
|
+
|
|
711
|
+
- small generated strings
|
|
712
|
+
- simple content loops
|
|
713
|
+
- compile-time fragments
|
|
714
|
+
|
|
715
|
+
Do not use it when you actually need runtime browser behavior.
|
|
716
|
+
|
|
717
|
+
### `::script` is for emitted browser JavaScript
|
|
718
|
+
|
|
719
|
+
Why it matters:
|
|
720
|
+
|
|
721
|
+
- compile-time JavaScript and browser JavaScript are different concerns
|
|
722
|
+
|
|
723
|
+
In this example, `::script` adds a small browser-side enhancement:
|
|
724
|
+
|
|
725
|
+
```web
|
|
726
|
+
::script {
|
|
727
|
+
code {
|
|
728
|
+
document.documentElement.dataset.webReady = "true";
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
That script ends up in the generated page output.
|
|
734
|
+
|
|
735
|
+
Use `::script` when the browser should run the code after the page loads.
|
|
736
|
+
|
|
737
|
+
## 6. What To Change First
|
|
738
|
+
|
|
739
|
+
If you are a designer, start by changing:
|
|
740
|
+
|
|
741
|
+
- the copy
|
|
742
|
+
- the accent color
|
|
743
|
+
- the background treatment
|
|
744
|
+
- card spacing and radius
|
|
745
|
+
- section order
|
|
746
|
+
|
|
747
|
+
If you are a developer, start by changing:
|
|
748
|
+
|
|
749
|
+
- the `::head` metadata
|
|
750
|
+
- `href` and `ariaLabel` values
|
|
751
|
+
- the responsive breakpoint in `::media`
|
|
752
|
+
- the inline script in `::script`
|
|
753
|
+
- the reusable base selectors like `buttonBase` and `featureCard`
|
|
754
|
+
|
|
755
|
+
## 7. A Good Mental Model For Editing
|
|
756
|
+
|
|
757
|
+
When you are changing a WEB file, ask:
|
|
758
|
+
|
|
759
|
+
1. Is this document metadata? Then use `::head`.
|
|
760
|
+
2. Is this a real HTML attribute? Then use `::attrs`.
|
|
761
|
+
3. Is this page structure or content? Then use a normal block.
|
|
762
|
+
4. Is this CSS-only styling by selector context? Then use `styles { ... }`.
|
|
763
|
+
5. Is this a state like hover? Then use `::pseudo`.
|
|
764
|
+
6. Is this responsive behavior? Then use `::media`.
|
|
765
|
+
7. Is this an unusual CSS detail? Then use `raw`.
|
|
766
|
+
8. Is this generated at compile time? Then use a `js` literal.
|
|
767
|
+
9. Is this browser runtime logic? Then use `::script`.
|
|
768
|
+
|
|
769
|
+
That single decision tree will keep most projects clean.
|
|
770
|
+
|
|
771
|
+
## 8. Common Beginner Mistakes
|
|
772
|
+
|
|
773
|
+
These are the most common first-run issues:
|
|
774
|
+
|
|
775
|
+
- forgetting `;` at the end of a statement
|
|
776
|
+
- placing `define { ... }` after normal page rules
|
|
777
|
+
- putting CSS properties inside `::attrs`
|
|
778
|
+
- nesting `::head` or `::script` inside a page section
|
|
779
|
+
- forgetting quotes around values like `#0f172a` or `/docs`
|
|
780
|
+
- expecting a `js` literal to run in the browser
|
|
781
|
+
|
|
782
|
+
If something goes wrong, read:
|
|
783
|
+
|
|
784
|
+
- `./error-handling.md` for troubleshooting patterns
|
|
785
|
+
- `./language-guide.md` for language rules
|
|
786
|
+
- `./cli.md` for CLI usage
|
|
787
|
+
|
|
788
|
+
For day-to-day workflow, a good rhythm is:
|
|
789
|
+
|
|
790
|
+
1. use `web init` when you want a ready-made starter page plus a bundled `web-lang-agents.md` context file
|
|
791
|
+
2. use `web studio.web` when you want a one-off compile
|
|
792
|
+
3. use `web screenshot studio.web` when you want a visual artifact to review or share
|
|
793
|
+
4. use `web watch studio.web -s` when you want a live working session with periodic visual snapshots
|
|
794
|
+
|
|
795
|
+
## 9. Where To Go Next
|
|
796
|
+
|
|
797
|
+
After this walkthrough, a natural next step is:
|
|
798
|
+
|
|
799
|
+
1. duplicate the example into a second `.web` file
|
|
800
|
+
2. replace the copy and tokens to match your own brand
|
|
801
|
+
3. add another section such as testimonials, pricing, or a contact form
|
|
802
|
+
4. introduce a second responsive breakpoint
|
|
803
|
+
5. add one carefully chosen `raw` enhancement only if normal WEB syntax stops being enough
|
|
804
|
+
|
|
805
|
+
That is usually the fastest path from “I understand the language” to “I can ship with it.”
|