uniweb 0.8.5 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/partials/agents.md +134 -26
- package/starter/foundation/src/foundation.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"js-yaml": "^4.1.0",
|
|
42
42
|
"prompts": "^2.4.2",
|
|
43
43
|
"tar": "^7.0.0",
|
|
44
|
-
"@uniweb/
|
|
44
|
+
"@uniweb/build": "0.8.5",
|
|
45
45
|
"@uniweb/kit": "0.7.3",
|
|
46
|
-
"@uniweb/
|
|
47
|
-
"@uniweb/
|
|
46
|
+
"@uniweb/core": "0.5.4",
|
|
47
|
+
"@uniweb/runtime": "0.6.4"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/partials/agents.md
CHANGED
|
@@ -67,8 +67,11 @@ The name is both the directory name and the package name. Use `--project <name>`
|
|
|
67
67
|
pnpm install # Install dependencies
|
|
68
68
|
pnpm dev # Start dev server
|
|
69
69
|
pnpm build # Build for production
|
|
70
|
+
pnpm preview # Preview production build (SSG + SPA)
|
|
70
71
|
```
|
|
71
72
|
|
|
73
|
+
> **npm works too.** Projects include both `pnpm-workspace.yaml` and npm workspaces. Replace `pnpm` with `npm` in any command above.
|
|
74
|
+
|
|
72
75
|
## Content Authoring
|
|
73
76
|
|
|
74
77
|
### Section Format
|
|
@@ -112,7 +115,7 @@ content = {
|
|
|
112
115
|
insets: [], // Inline @Component references — { refId }
|
|
113
116
|
lists: [], // [[{ paragraphs, links, lists, ... }]] — each list item is an object, not a string
|
|
114
117
|
quotes: [], // Blockquotes
|
|
115
|
-
data: {}, // From tagged code blocks (```yaml:tagname)
|
|
118
|
+
data: {}, // From tagged code blocks (```yaml:tagname) and (```js:tagname)
|
|
116
119
|
headings: [], // Overflow headings after subtitle2
|
|
117
120
|
items: [], // Each has the same flat structure — from headings after body content
|
|
118
121
|
sequence: [], // All elements in document order
|
|
@@ -127,12 +130,16 @@ content = {
|
|
|
127
130
|
We built this for you. ← paragraph
|
|
128
131
|
|
|
129
132
|
### Fast ← items[0].title
|
|
133
|
+
 ← items[0].icons[0]
|
|
130
134
|
Lightning quick. ← items[0].paragraphs[0]
|
|
131
135
|
|
|
132
136
|
### Secure ← items[1].title
|
|
137
|
+
 ← items[1].icons[0]
|
|
133
138
|
Enterprise-grade. ← items[1].paragraphs[0]
|
|
134
139
|
```
|
|
135
140
|
|
|
141
|
+
Each item has the same content shape as the top level — `title`, `paragraphs`, `icons`, `links`, `lists`, etc. are all available per item.
|
|
142
|
+
|
|
136
143
|
**Lists** contain bullet or ordered list items. Each list item is an object with the same content shape — not a plain string:
|
|
137
144
|
|
|
138
145
|
```markdown
|
|
@@ -186,6 +193,7 @@ The three parts carry distinct information:
|
|
|
186
193
|
{variant=compact}
|
|
187
194
|
{period=30d}
|
|
188
195
|
{position=top-right}
|
|
196
|
+
{note="Vite + React + Routing — ready to go"}
|
|
189
197
|
```
|
|
190
198
|
|
|
191
199
|
Inset components must declare `inset: true` in their `meta.js`. They render at the exact position in the content flow where the author placed them. See meta.js section below for details.
|
|
@@ -198,6 +206,8 @@ Inset components must declare `inset: true` in their `meta.js`. They render at t
|
|
|
198
206
|
{role=banner} <!-- Role determines array: imgs, icons, or videos -->
|
|
199
207
|
```
|
|
200
208
|
|
|
209
|
+
**Quote values that contain spaces:** `{note="Ready to go"}` not `{note=Ready to go}`. Unquoted values end at the first space.
|
|
210
|
+
|
|
201
211
|
Standalone links (alone on a line) become buttons. Inline links stay as text links.
|
|
202
212
|
|
|
203
213
|
### Structured Data
|
|
@@ -215,19 +225,33 @@ submitLabel: Send
|
|
|
215
225
|
|
|
216
226
|
Access: `content.data?.form` → `{ fields: [...], submitLabel: "Send" }`
|
|
217
227
|
|
|
228
|
+
**Code blocks need tags too.** Untagged code blocks (plain ```js) are only visible to sequential-rendering components like Article or DocSection. If a component needs to access code blocks by name, tag them:
|
|
229
|
+
|
|
230
|
+
````markdown
|
|
231
|
+
```jsx:before
|
|
232
|
+
const old = fetch('/api')
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```jsx:after
|
|
236
|
+
const data = useData()
|
|
237
|
+
```
|
|
238
|
+
````
|
|
239
|
+
|
|
240
|
+
Access: `content.data?.before`, `content.data?.after` → raw code strings.
|
|
241
|
+
|
|
218
242
|
### Section Backgrounds
|
|
219
243
|
|
|
220
|
-
Set `background` in frontmatter — the runtime renders it automatically:
|
|
244
|
+
Set `background` in frontmatter — the runtime renders it automatically. The string form auto-detects the type:
|
|
221
245
|
|
|
222
246
|
```yaml
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
background:
|
|
227
|
-
|
|
247
|
+
background: /images/hero.jpg # Image (by extension)
|
|
248
|
+
background: /videos/hero.mp4 # Video (by extension)
|
|
249
|
+
background: linear-gradient(135deg, #667eea, #764ba2) # CSS gradient
|
|
250
|
+
background: '#1a1a2e' # Color (hex — quote in YAML)
|
|
251
|
+
background: var(--primary-900) # Color (CSS variable)
|
|
228
252
|
```
|
|
229
253
|
|
|
230
|
-
|
|
254
|
+
The object form gives more control:
|
|
231
255
|
|
|
232
256
|
```yaml
|
|
233
257
|
background:
|
|
@@ -235,6 +259,8 @@ background:
|
|
|
235
259
|
overlay: { enabled: true, type: dark, opacity: 0.5 }
|
|
236
260
|
```
|
|
237
261
|
|
|
262
|
+
Overlay shorthand — `overlay: 0.5` is equivalent to `{ enabled: true, type: dark, opacity: 0.5 }`.
|
|
263
|
+
|
|
238
264
|
Components that render their own background declare `background: 'self'` in `meta.js`.
|
|
239
265
|
|
|
240
266
|
### Page Organization
|
|
@@ -417,7 +443,7 @@ The runtime does significant work that other frameworks push onto components. Un
|
|
|
417
443
|
| `isDark ? 'text-white' : 'text-gray-900'` | Just write `text-heading` — it adapts |
|
|
418
444
|
| Background rendering code | Declare `background:` in frontmatter instead |
|
|
419
445
|
| Color constants / tokens files | Colors come from `theme.yml` |
|
|
420
|
-
|
|
|
446
|
+
| Parallel color system (`--ink`, `--paper`) that duplicates what tokens already provide | Map source color roles to `theme.yml` colors/neutral. The build generates `--primary-50` through `--primary-950`, `--neutral-50` through `--neutral-950`, etc. Use palette shades directly (`var(--primary-300)`) for specific tones. Additive design classes that BUILD ON tokens are fine — a parallel system that REPLACES them bypasses context adaptation. |
|
|
421
447
|
|
|
422
448
|
**What to hardcode** (not semantic — same in every context): layout (`grid`, `flex`, `max-w-6xl`), spacing (`p-6`, `gap-8`), typography scale (`text-3xl`, `font-bold`), animations, border-radius.
|
|
423
449
|
|
|
@@ -430,6 +456,33 @@ theme: dark ← sets context-dark, all tokens resolve to dark values
|
|
|
430
456
|
---
|
|
431
457
|
```
|
|
432
458
|
|
|
459
|
+
Alternate between `light` (default), `medium`, and `dark` across sections for visual rhythm — no CSS needed. A typical marketing page:
|
|
460
|
+
|
|
461
|
+
```markdown
|
|
462
|
+
<!-- 1-hero.md -->
|
|
463
|
+
theme: dark
|
|
464
|
+
|
|
465
|
+
<!-- 2-features.md -->
|
|
466
|
+
(no theme — defaults to light)
|
|
467
|
+
|
|
468
|
+
<!-- 3-testimonials.md -->
|
|
469
|
+
theme: medium
|
|
470
|
+
|
|
471
|
+
<!-- 4-cta.md -->
|
|
472
|
+
theme: dark
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Per-section token overrides** — the object form lets authors fine-tune individual tokens for a specific section:
|
|
476
|
+
|
|
477
|
+
```yaml
|
|
478
|
+
theme:
|
|
479
|
+
mode: light
|
|
480
|
+
primary: var(--neutral-900) # Dark buttons in a light section
|
|
481
|
+
primary-hover: var(--neutral-800)
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Any semantic token (`section`, `heading`, `body`, `primary`, `link`, etc.) can be overridden this way. The overrides are applied as inline CSS custom properties on the section wrapper — components don't need to know about them.
|
|
485
|
+
|
|
433
486
|
**Site controls the palette** in `theme.yml`. The same foundation looks different across sites because tokens resolve from the site's color configuration, not from component code.
|
|
434
487
|
|
|
435
488
|
### theme.yml
|
|
@@ -437,18 +490,14 @@ theme: dark ← sets context-dark, all tokens resolve to dark values
|
|
|
437
490
|
```yaml
|
|
438
491
|
# site/theme.yml
|
|
439
492
|
colors:
|
|
440
|
-
primary:
|
|
441
|
-
base: '#3b82f6'
|
|
442
|
-
exactMatch: true # Use this exact hex at the 500 shade
|
|
493
|
+
primary: '#3b82f6' # Your exact hex appears at shade 500
|
|
443
494
|
secondary: '#64748b'
|
|
444
495
|
accent: '#8b5cf6'
|
|
445
|
-
neutral: stone
|
|
496
|
+
neutral: stone # Named preset: stone, zinc, gray, slate, neutral
|
|
446
497
|
|
|
447
498
|
contexts:
|
|
448
499
|
light:
|
|
449
|
-
section: '#fafaf9'
|
|
450
|
-
primary: var(--primary-500)
|
|
451
|
-
primary-hover: var(--primary-600)
|
|
500
|
+
section: '#fafaf9' # Override individual tokens per context
|
|
452
501
|
|
|
453
502
|
fonts:
|
|
454
503
|
import:
|
|
@@ -467,23 +516,78 @@ vars: # Override foundation-declared variables
|
|
|
467
516
|
|
|
468
517
|
Each color generates 11 OKLCH shades (50–950). The `neutral` palette is special — use a named preset (`stone` for warm) rather than a hex value. Three contexts are built-in: `light` (default), `medium`, `dark`. Context override keys match token names — `section:` not `bg:`, `primary:` not `btn-primary-bg:`.
|
|
469
518
|
|
|
519
|
+
### How colors reach components
|
|
520
|
+
|
|
521
|
+
Your hex color → 11 shades (50–950) → semantic tokens → components.
|
|
522
|
+
|
|
523
|
+
**Shade 500 = your exact input color.** The build generates lighter shades (50–400) above it and darker shades (600–950) below it, redistributing lightness proportionally to maintain a smooth scale. Set `exactMatch: false` on a color to opt out and use fixed lightness values instead.
|
|
524
|
+
|
|
525
|
+
Semantic tokens map shades to roles. The defaults for light/medium contexts:
|
|
526
|
+
|
|
527
|
+
| Token | Shade | Purpose |
|
|
528
|
+
|-------|-------|---------|
|
|
529
|
+
| `--primary` | 600 | Button background |
|
|
530
|
+
| `--primary-hover` | 700 | Button hover |
|
|
531
|
+
| `--link` | 600 | Link color |
|
|
532
|
+
| `--ring` | 500 | Focus ring |
|
|
533
|
+
|
|
534
|
+
In dark contexts, `--primary` uses shade 500 and `--link` uses shade 400.
|
|
535
|
+
|
|
536
|
+
**Buttons and links use shade 600 — darker than your input.** This is an accessibility choice: shade 600 provides better contrast with white button text. For medium-bright brand colors like orange, buttons will be noticeably darker than the brand color.
|
|
537
|
+
|
|
538
|
+
**Recipe — brand-exact buttons:**
|
|
539
|
+
|
|
540
|
+
```yaml
|
|
541
|
+
colors:
|
|
542
|
+
primary: "#E35D25"
|
|
543
|
+
|
|
544
|
+
contexts:
|
|
545
|
+
light:
|
|
546
|
+
primary: primary-500 # Your exact color on buttons
|
|
547
|
+
primary-hover: primary-600 # Darker on hover
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
> **Contrast warning:** Bright brand colors (orange, yellow, light green) at shade 500 may not meet WCAG contrast (4.5:1) with white foreground text. Test buttons for readability — if contrast is insufficient, keep the default shade 600 mapping or darken your base color.
|
|
551
|
+
|
|
470
552
|
### Foundation variables
|
|
471
553
|
|
|
472
|
-
Foundations declare customizable layout/spacing values in `foundation.js
|
|
554
|
+
Foundations declare customizable layout/spacing values in `foundation.js`. The starter includes:
|
|
473
555
|
|
|
474
556
|
```js
|
|
475
|
-
export
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
},
|
|
557
|
+
export const vars = {
|
|
558
|
+
'header-height': { default: '4rem', description: 'Fixed header height' },
|
|
559
|
+
'max-content-width': { default: '80rem', description: 'Maximum content width' },
|
|
560
|
+
'section-padding-y': { default: 'clamp(4rem, 6vw, 7rem)', description: 'Vertical padding for sections' },
|
|
480
561
|
}
|
|
481
562
|
```
|
|
482
563
|
|
|
483
|
-
Sites override them in `theme.yml` under `vars:`. Components use them
|
|
564
|
+
Sites override them in `theme.yml` under `vars:`. Components use them via Tailwind arbitrary values or CSS: `py-[var(--section-padding-y)]`, `h-[var(--header-height)]`, etc.
|
|
565
|
+
|
|
566
|
+
The `section-padding-y` default uses `clamp()` for fluid spacing — tighter on mobile, more breathing room on large screens. Use this variable for consistent section spacing instead of hardcoding padding in each component. Sites can override to a fixed value (`section-padding-y: 3rem`) or a different clamp in `theme.yml`.
|
|
484
567
|
|
|
485
568
|
**When to break the rules:** Header/footer components that float over content may need direct color logic (reading the first section's theme). Decorative elements with fixed branding (logos) use literal colors.
|
|
486
569
|
|
|
570
|
+
### Design richness beyond tokens
|
|
571
|
+
|
|
572
|
+
Semantic tokens handle context adaptation — the hard problem of making colors work in light, medium, and dark sections. **They are a floor, not a ceiling.** A great foundation adds its own design vocabulary on top.
|
|
573
|
+
|
|
574
|
+
The token set is deliberately small (24 tokens). It covers the dimensions that change per context. Everything that stays constant across contexts — border weights, shadow depth, radius scales, gradient angles, accent borders, glassmorphism, elevation layers — belongs in your foundation's `styles.css` or component code.
|
|
575
|
+
|
|
576
|
+
**Don't flatten a rich design to fit the token set.** If a source design has 4 border tones, create them:
|
|
577
|
+
|
|
578
|
+
```css
|
|
579
|
+
/* foundation/src/styles.css */
|
|
580
|
+
.border-subtle { border-color: color-mix(in oklch, var(--border), transparent 50%); }
|
|
581
|
+
.border-strong { border-color: color-mix(in oklch, var(--border), var(--heading) 30%); }
|
|
582
|
+
.border-accent { border-color: var(--primary-300); }
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
These compose with semantic tokens — they adapt per context because they reference `--border`, `--heading`, or palette shades. But they add design nuance the token set alone doesn't provide.
|
|
586
|
+
|
|
587
|
+
**The priority:** Design quality > portability > configurability. It's better to ship a foundation with beautiful, detailed design that's less configurable than to ship a generic one that looks flat. A foundation that looks great for one site is more valuable than one that looks mediocre for any site.
|
|
588
|
+
|
|
589
|
+
**When migrating from an existing design**, map every visual detail — not just the ones that have a semantic token. Shadow systems, border hierarchies, custom hover effects, accent tints: create CSS classes or Tailwind utilities in `styles.css` for anything the original has that tokens don't cover. Use palette shades directly (`var(--primary-300)`, `bg-neutral-200`) for fine-grained color control beyond the semantic tokens.
|
|
590
|
+
|
|
487
591
|
## Component Development
|
|
488
592
|
|
|
489
593
|
### Props Interface
|
|
@@ -509,13 +613,13 @@ function Hero({ content, params }) {
|
|
|
509
613
|
)
|
|
510
614
|
}
|
|
511
615
|
|
|
512
|
-
Hero.className = 'pt-32 md:pt-48' //
|
|
616
|
+
Hero.className = 'pt-32 md:pt-48' // Override spacing for hero (more top padding)
|
|
513
617
|
Hero.as = 'div' // Change wrapper element (default: 'section')
|
|
514
618
|
|
|
515
619
|
export default Hero
|
|
516
620
|
```
|
|
517
621
|
|
|
518
|
-
- `Component.className` — adds classes to the runtime's wrapper. Use for section-level
|
|
622
|
+
- `Component.className` — adds classes to the runtime's wrapper. Use for section-level spacing, borders, overflow. Set `py-[var(--section-padding-y)]` for consistent spacing from the theme variable, or override for specific sections (e.g., hero needs extra top padding). The component's own JSX handles inner layout only (`max-w-7xl mx-auto px-6`).
|
|
519
623
|
- `Component.as` — changes the wrapper element. Use `'nav'` for headers, `'footer'` for footers, `'div'` when `<section>` isn't semantically appropriate.
|
|
520
624
|
|
|
521
625
|
### meta.js Structure
|
|
@@ -577,6 +681,8 @@ import { H2, P, Span } from '@uniweb/kit'
|
|
|
577
681
|
<Text text={content.title} as="h2" className="..." /> // explicit tag
|
|
578
682
|
```
|
|
579
683
|
|
|
684
|
+
These components render their own HTML tag — don't wrap them in a matching tag. `<h2><H2 text={...} /></h2>` creates a nested `<h2><h2>...</h2></h2>`, which is invalid HTML. Just use `<H2 text={...} />` directly.
|
|
685
|
+
|
|
580
686
|
Don't render content strings with `{content.paragraphs[0]}` in JSX — that shows HTML tags as visible text. Use `<P>`, `<H2>`, `<Span>`, etc. for content strings.
|
|
581
687
|
|
|
582
688
|
**Rendering full content** (`@uniweb/kit`):
|
|
@@ -853,7 +959,7 @@ Foundation styles in `foundation/src/styles.css`:
|
|
|
853
959
|
|
|
854
960
|
Semantic color tokens (`text-heading`, `bg-section`, `bg-primary`, etc.) come from `theme-tokens.css` — which the runtime populates from the site's `theme.yml`. Don't redefine colors here that belong in `theme.yml`. Use `@theme` only for values the token system doesn't cover (custom breakpoints, animations, shadows).
|
|
855
961
|
|
|
856
|
-
**Custom CSS is
|
|
962
|
+
**Custom CSS is expected alongside Tailwind.** Your foundation's `styles.css` is the design layer — shadow systems, border hierarchies, gradient effects, accent treatments, elevation scales, glassmorphism. If the source design has a visual detail, create a class for it. Tailwind handles layout and spacing; semantic tokens handle context adaptation; `styles.css` handles everything else that makes the design rich and distinctive.
|
|
857
963
|
|
|
858
964
|
## Troubleshooting
|
|
859
965
|
|
|
@@ -863,6 +969,8 @@ Semantic color tokens (`text-heading`, `bg-section`, `bg-primary`, etc.) come fr
|
|
|
863
969
|
|
|
864
970
|
**Styles not applying** — Verify `@source` in `styles.css` includes your component paths. Check custom colors match `@theme` definitions.
|
|
865
971
|
|
|
972
|
+
**Prerender warnings about hooks/useState** — During `pnpm build`, you may see `Warning: Failed to render /: Cannot read properties of null (reading 'useState')` for pages. This is a known limitation of the SSG pipeline (dual React instances in development). The site works correctly client-side — these warnings only affect the static HTML preview, not functionality.
|
|
973
|
+
|
|
866
974
|
## Further Documentation
|
|
867
975
|
|
|
868
976
|
Full Uniweb documentation is available at **https://github.com/uniweb/docs** — raw markdown files you can fetch directly.
|
|
@@ -24,8 +24,8 @@ export const vars = {
|
|
|
24
24
|
description: 'Maximum content width (1280px)',
|
|
25
25
|
},
|
|
26
26
|
'section-padding-y': {
|
|
27
|
-
default: '
|
|
28
|
-
description: 'Vertical padding for sections',
|
|
27
|
+
default: 'clamp(4rem, 6vw, 7rem)',
|
|
28
|
+
description: 'Vertical padding for sections (fluid: adapts to viewport)',
|
|
29
29
|
},
|
|
30
30
|
}
|
|
31
31
|
|