uniweb 0.2.39 → 0.2.41

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 CHANGED
@@ -1,24 +1,39 @@
1
1
  # uniweb
2
2
 
3
- Create a well-structured Vite + React site with content/code separation, file-based routing, localization, and structured content—out of the box. The architecture scales to dynamic content management and collaborative visual editing when you need it.
3
+ A component content architecture for React. Build sites where content authors and component developers can't break each other's work—and scale from local files to visual editing without rewrites.
4
4
 
5
- ## Quick Start
5
+ Create well-structured Vite + React projects with file-based routing, localization, and clean content/code separation out of the box.
6
6
 
7
- Create a new Uniweb project:
7
+ ## Quick Start
8
8
 
9
9
  ```bash
10
- npx uniweb@latest create my-project
11
- cd my-project
10
+ npx uniweb@latest create my-site --template marketing
11
+ cd my-site
12
12
  pnpm install
13
13
  pnpm dev
14
14
  ```
15
15
 
16
- You get a workspace with two packages:
16
+ The `marketing` template includes real components (Hero, Features, Pricing, Testimonials, FAQ, and more) with sample content—a working site you can explore and modify.
17
+
18
+ **Other templates:**
19
+
20
+ ```bash
21
+ # Academic site (researcher portfolios, lab pages)
22
+ npx uniweb@latest create my-site --template academic
23
+
24
+ # Documentation site
25
+ npx uniweb@latest create my-site --template docs
26
+
27
+ # Minimal starter (build from scratch)
28
+ npx uniweb@latest create my-site
29
+ ```
30
+
31
+ Every project is a workspace with two packages:
17
32
 
18
33
  - **`site/`** — Content, pages, entry point
19
- - **`foundation/`** — Your React components
34
+ - **`foundation/`** — React components
20
35
 
21
- Content authors work in markdown. Component authors work in React. Neither can break the other's work, and component updates flow to every site using them.
36
+ Content authors work in markdown. Component authors work in React. Neither can break the other's work.
22
37
 
23
38
  ## What You Get
24
39
 
@@ -30,8 +45,6 @@ my-project/
30
45
  │ │ ├── page.yml # Page metadata
31
46
  │ │ └── 1-hero.md # Section content
32
47
  │ ├── locales/ # i18n (hash-based translations)
33
- │ │ ├── manifest.json # Auto-extracted strings
34
- │ │ └── es.json # Spanish translations
35
48
  │ ├── main.js # Entry point (~6 lines)
36
49
  │ ├── vite.config.js # 3-line config
37
50
  │ └── public/ # Static assets
@@ -84,13 +97,13 @@ Components receive validated, localized data. Natural content stays in markdown;
84
97
 
85
98
  ```jsx
86
99
  export function Hero({ content, params }) {
87
- const { title } = content.main.header;
88
- const { paragraphs, links } = content.main.body;
89
- const { theme = "light" } = params;
100
+ const { title } = content.main.header
101
+ const { paragraphs, links } = content.main.body
102
+ const { theme = 'light' } = params
90
103
 
91
104
  return (
92
105
  <section
93
- className={`py-20 text-center ${theme === "dark" ? "bg-gray-900 text-white" : ""}`}
106
+ className={`py-20 text-center ${theme === 'dark' ? 'bg-gray-900 text-white' : ''}`}
94
107
  >
95
108
  <h1 className="text-4xl font-bold">{title}</h1>
96
109
  <p className="text-xl text-gray-600">{paragraphs[0]}</p>
@@ -103,23 +116,45 @@ export function Hero({ content, params }) {
103
116
  </a>
104
117
  )}
105
118
  </section>
106
- );
119
+ )
107
120
  }
108
121
  ```
109
122
 
110
- Standard React. Standard Tailwind. The `{ content, params }` interface is only for _exposed_ components—the ones content creators select in markdown frontmatter. Internal components (the majority of your codebase) use regular React props.
123
+ Standard React. Standard Tailwind. The `{ content, params }` interface is only for _exposed_ components—the ones content creators select in markdown frontmatter. Internal components use regular React props.
124
+
125
+ ## Foundations Are Portable
126
+
127
+ The `foundation/` folder ships with your project as a convenience, but a foundation is a self-contained artifact with no dependency on any specific site. Sites reference foundations by configuration, not by folder proximity.
128
+
129
+ **Three ways to use a foundation:**
130
+
131
+ | Mode | How it works | Best for |
132
+ | ---------------- | ---------------------------------- | -------------------------------------------------- |
133
+ | **Local folder** | Foundation lives in your workspace | Developing site and components together |
134
+ | **npm package** | `pnpm add @acme/foundation` | Distributing via standard package tooling |
135
+ | **Runtime link** | Foundation loads from a URL | Independent release cycles, platform-managed sites |
136
+
137
+ You can delete the `foundation/` folder entirely and point your site at a published foundation. Or develop a foundation locally, then publish it for other sites to consume. The site doesn't care where its components come from.
138
+
139
+ **This enables two development patterns:**
140
+
141
+ _Site-first_ — You're building a website. The foundation is your component library, co-developed with the site. This is the common case.
111
142
 
112
- No framework to learn. Foundations are purpose-built component systems designed for a specific domain (marketing, documentation, learning, etc.). Sites are Vite apps that load content from markdown files.
143
+ _Foundation-first_ You're building a component system. The site is a test harness with sample content. The real sites live elsewhere—other repositories, other teams, or managed on [uniweb.app](https://uniweb.app). The `multi` template supports this workflow with multiple test sites exercising a shared foundation.
113
144
 
114
145
  ## The Bigger Picture
115
146
 
116
147
  The structure you start with scales without rewrites:
117
148
 
118
- 1. **Single project** — One site, one component library. Most projects stay here.
119
- 2. **Multi-site** — One foundation powers multiple sites. Release it once, updates propagate automatically.
120
- 3. **Full platform** — [uniweb.app](https://uniweb.app) adds visual editing, live content management, and team collaboration. Your foundation plugs in and its components become native to the editor.
149
+ 1. **Single project** — One site, one foundation. Develop and deploy together. Most projects stay here.
121
150
 
122
- Start with local markdown files deployed anywhere. Connect to [uniweb.app](https://uniweb.app) when you're ready for dynamic content and team collaboration.
151
+ 2. **Published foundation** Release your foundation as an npm package or to [uniweb.app](https://uniweb.app). Other sites can use it without copying code.
152
+
153
+ 3. **Multiple sites** — Several sites share one foundation. Update components once, every site benefits.
154
+
155
+ 4. **Platform-managed sites** — Sites built on [uniweb.app](https://uniweb.app) with visual editing tools can use your foundation. You develop components locally; content teams work in the browser.
156
+
157
+ Start with local files deployed anywhere. The same foundation works across all these scenarios.
123
158
 
124
159
  ---
125
160
 
@@ -250,123 +285,58 @@ build:
250
285
  prerender: true
251
286
  ```
252
287
 
253
- Or use the `--prerender` flag to force it on (or `--no-prerender` to skip it). This is useful for:
288
+ Or use the `--prerender` flag. This gives you:
254
289
 
255
290
  - **SEO**: Search engines see fully rendered content immediately
256
- - **Performance**: First contentful paint is instant (no JavaScript required)
291
+ - **Performance**: First contentful paint is instant
257
292
  - **Hosting**: Deploy to any static host (GitHub Pages, Netlify, S3, etc.)
258
293
 
259
- **How it works:**
260
-
261
- 1. The site build runs first, generating the JavaScript bundle and `site-content.json`
262
- 2. The prerenderer loads the foundation and site content in Node.js
263
- 3. Each page is rendered to HTML using React's `renderToString()`
264
- 4. The HTML is injected into the shell with the site content embedded
265
-
266
- **Hydration:**
267
-
268
- The pre-rendered HTML includes a `<script id="__SITE_CONTENT__">` tag with the full site data. When the page loads in the browser:
269
-
270
- 1. The static HTML displays immediately (no flash of loading state)
271
- 2. React hydrates the existing DOM instead of replacing it
272
- 3. The site becomes fully interactive with client-side routing
273
-
274
- **Usage:**
275
-
276
- ```bash
277
- # From site directory (with build.prerender: true in site.yml)
278
- cd site
279
- pnpm build
280
-
281
- # Or force pre-rendering via CLI flag
282
- cd site && uniweb build --prerender
283
- ```
294
+ The pre-rendered HTML includes embedded site content. When the page loads, React hydrates the existing DOM—no flash of loading state, then full client-side interactivity.
284
295
 
285
296
  ## Built-in Templates
286
297
 
287
298
  ### Single (Default)
288
299
 
289
- A minimal workspace with a site and foundation as sibling packages. This is the recommended starting point.
300
+ A minimal workspace with a site and foundation as sibling packages. The recommended starting point.
290
301
 
291
302
  ```
292
303
  my-project/
293
- ├── package.json # Workspace root (npm + pnpm compatible)
294
- ├── pnpm-workspace.yaml # Pre-configured for evolution (see below)
295
- ├── CLAUDE.md # AI assistant instructions
296
-
297
- ├── site/ # Site package (content + entry)
304
+ ├── package.json # Workspace root
305
+ ├── pnpm-workspace.yaml
306
+ ├── site/
298
307
  │ ├── package.json
299
- │ ├── vite.config.js # 3-line config
300
- │ ├── index.html
301
- │ ├── site.yml # Site configuration (foundation, title, i18n)
302
- ├── main.js # Entry point (~6 lines)
303
- │ ├── pages/ # Content pages (file-based routing)
304
- │ │ └── home/
305
- │ │ ├── page.yml
306
- │ │ └── 1-hero.md
307
- │ └── public/ # Static assets
308
-
309
- └── foundation/ # Foundation package (components)
308
+ │ ├── vite.config.js
309
+ │ ├── site.yml
310
+ │ ├── main.js
311
+ └── pages/
312
+ └── foundation/
310
313
  ├── package.json
311
- ├── vite.config.js # 3-line config
312
- └── src/
313
- ├── styles.css # Tailwind CSS v4
314
- ├── meta.js # Foundation metadata
315
- ├── exports.js # (optional) Custom Layout, props
316
- └── components/
317
- └── Section/
318
- ├── index.jsx
319
- └── meta.js
314
+ ├── vite.config.js
315
+ └── src/components/
320
316
  ```
321
317
 
322
- **Key characteristics:**
323
-
324
- - **Convention-compliant** — Each package has its own `src/`
325
- - **Clear dep boundaries** — Component libs → `foundation/package.json`, runtime → `site/package.json`
326
- - **Zero extraction** — `foundation/` is already a complete, publishable package
327
- - **Scales naturally** — Rename to `sites/marketing/` and `foundations/marketing/` when needed
328
-
329
318
  ### Multi
330
319
 
331
- A monorepo for multi-site or multi-foundation development.
320
+ A monorepo for foundation development or multi-site projects.
332
321
 
333
322
  ```
334
323
  my-workspace/
335
- ├── package.json # Workspace root (npm + pnpm compatible)
336
- ├── pnpm-workspace.yaml # Same config as single template
337
- ├── CLAUDE.md # AI assistant instructions
338
-
339
324
  ├── sites/
340
- │ ├── marketing/ # Main marketing site
341
- │ ├── package.json
342
- │ │ ├── vite.config.js # 3-line config
343
- │ │ ├── site.yml
344
- │ │ ├── main.js # Entry point (~6 lines)
345
- │ │ └── pages/
346
- │ └── docs/ # Documentation site
347
-
325
+ │ ├── marketing/ # Main site or test site
326
+ └── docs/ # Additional site
348
327
  └── foundations/
349
- ├── marketing/ # Marketing foundation
350
- │ ├── package.json
351
- │ ├── vite.config.js # 3-line config
352
- │ └── src/components/
353
- └── documentation/ # Documentation foundation
328
+ ├── marketing/ # Primary foundation
329
+ └── documentation/ # Additional foundation
354
330
  ```
355
331
 
356
- Use this when you need:
357
-
358
- - Multiple sites sharing foundations
359
- - Multiple foundations for different purposes
360
- - A testing site for foundation development
332
+ Use this when you need multiple sites sharing foundations, multiple foundations for different purposes, or test sites for foundation development.
361
333
 
362
334
  ## Official Templates
363
335
 
364
- Feature-rich templates that demonstrate what's possible with Uniweb. These include real components, sample content, and production-ready structure.
336
+ Feature-rich templates with real components and sample content.
365
337
 
366
338
  ### Marketing
367
339
 
368
- A complete marketing site with landing page components:
369
-
370
340
  ```bash
371
341
  uniweb create my-site --template marketing
372
342
  ```
@@ -383,8 +353,6 @@ uniweb create my-site --template marketing --variant tailwind3
383
353
 
384
354
  ### Academic
385
355
 
386
- A professional academic site for researchers, labs, and departments:
387
-
388
356
  ```bash
389
357
  uniweb create my-site --template academic
390
358
  ```
@@ -395,8 +363,6 @@ Perfect for researcher portfolios, lab websites, and academic department sites.
395
363
 
396
364
  ### Docs
397
365
 
398
- A documentation site with navigation levels:
399
-
400
366
  ```bash
401
367
  uniweb create my-site --template docs
402
368
  ```
@@ -407,7 +373,7 @@ Perfect for technical documentation, guides, and API references.
407
373
 
408
374
  ## External Templates
409
375
 
410
- You can use templates from npm or GitHub:
376
+ Use templates from npm or GitHub:
411
377
 
412
378
  ```bash
413
379
  # npm package
@@ -420,8 +386,6 @@ uniweb create my-site --template github:user/repo
420
386
  uniweb create my-site --template github:user/repo#v1.0.0
421
387
  ```
422
388
 
423
- External templates must follow the same structure as official templates. See [`@uniweb/templates`](https://github.com/uniweb/templates) for the template format specification.
424
-
425
389
  ## Dependency Management
426
390
 
427
391
  Each package manages its own dependencies:
@@ -452,14 +416,14 @@ cd foundation && pnpm add embla-carousel
452
416
  The `defineSiteConfig()` function handles all Vite configuration for sites:
453
417
 
454
418
  ```javascript
455
- import { defineSiteConfig } from "@uniweb/build/site";
419
+ import { defineSiteConfig } from '@uniweb/build/site'
456
420
 
457
421
  export default defineSiteConfig({
458
422
  // All options are optional
459
423
  tailwind: true, // Enable Tailwind CSS v4 (default: true)
460
424
  plugins: [], // Additional Vite plugins
461
425
  // ...any other Vite config options
462
- });
426
+ })
463
427
  ```
464
428
 
465
429
  ### Foundation Configuration
@@ -467,11 +431,11 @@ export default defineSiteConfig({
467
431
  The `defineFoundationConfig()` function handles all Vite configuration for foundations:
468
432
 
469
433
  ```javascript
470
- import { defineFoundationConfig } from "@uniweb/build";
434
+ import { defineFoundationConfig } from '@uniweb/build'
471
435
 
472
436
  export default defineFoundationConfig({
473
437
  // All options are optional - entry is auto-generated
474
- fileName: "foundation", // Output file name
438
+ fileName: 'foundation', // Output file name
475
439
  externals: [], // Additional packages to externalize
476
440
  includeDefaultExternals: true, // Include react, @uniweb/core, etc.
477
441
  tailwind: true, // Enable Tailwind CSS v4 Vite plugin
@@ -479,7 +443,7 @@ export default defineFoundationConfig({
479
443
  plugins: [], // Additional Vite plugins
480
444
  build: {}, // Additional Vite build options
481
445
  // ...any other Vite config options
482
- });
446
+ })
483
447
  ```
484
448
 
485
449
  For Tailwind CSS v3 projects, set `tailwind: false` and use PostCSS:
@@ -487,7 +451,7 @@ For Tailwind CSS v3 projects, set `tailwind: false` and use PostCSS:
487
451
  ```javascript
488
452
  export default defineFoundationConfig({
489
453
  tailwind: false, // Uses PostCSS instead of Vite plugin
490
- });
454
+ })
491
455
  ```
492
456
 
493
457
  ## Foundation Build Process
@@ -520,62 +484,40 @@ Both templates use the same unified workspace configuration:
520
484
  ```yaml
521
485
  # pnpm-workspace.yaml
522
486
  packages:
523
- - "site"
524
- - "foundation"
525
- - "sites/*"
526
- - "foundations/*"
487
+ - 'site'
488
+ - 'foundation'
489
+ - 'sites/*'
490
+ - 'foundations/*'
527
491
  ```
528
492
 
493
+ Also set in `package.json` for npm compatibility.
494
+
529
495
  ```json
530
- // package.json (workspaces field for npm compatibility)
531
496
  {
532
497
  "workspaces": ["site", "foundation", "sites/*", "foundations/*"]
533
498
  }
534
499
  ```
535
500
 
536
- This means **no config changes when evolving your project**.
537
-
538
- ## Evolving Your Project
539
-
540
- The workspace is pre-configured for growth. Choose your path:
541
-
542
- **Add alongside existing structure:**
501
+ This means no config changes when evolving from single to multi-site.
543
502
 
544
- ```bash
545
- # Keep site/ and foundation/, add more in sites/ and foundations/
546
- mkdir -p sites/docs
547
- mkdir -p foundations/documentation
548
- ```
503
+ ## Releasing a Foundation
549
504
 
550
- **Or migrate to multi-site structure:**
505
+ Publish your foundation to npm:
551
506
 
552
507
  ```bash
553
- # Move and rename by purpose
554
- mv site sites/marketing
555
- mv foundation foundations/marketing
556
-
557
- # Update package names and workspace dependencies in package.json files
508
+ cd foundation
509
+ npm publish
558
510
  ```
559
511
 
560
- Both patterns work simultaneously—evolve gradually as needed.
561
-
562
- ## Releasing a Foundation
563
-
564
- Publish your foundation to [uniweb.app](https://uniweb.app) to make it available for your sites:
512
+ Or to [uniweb.app](https://uniweb.app) for use with platform-managed sites:
565
513
 
566
514
  ```bash
567
515
  uniweb login # First time only
568
516
  uniweb build
569
- uniweb publish # Publishes the foundation in the current directory
517
+ uniweb publish
570
518
  ```
571
519
 
572
- Each release creates a new version. Sites link to foundations at runtime and control their own update strategy—automatic, minor-only, patch-only, or pinned to a specific version. You own your foundations and license them to sites—yours or your clients'.
573
-
574
- You can also publish to npm:
575
-
576
- ```bash
577
- npm publish
578
- ```
520
+ Sites control their own update strategy—automatic, minor-only, patch-only, or pinned to a specific version.
579
521
 
580
522
  ## FAQ
581
523
 
@@ -585,19 +527,23 @@ MDX blends markdown and JSX—content authors write code. Uniweb keeps them sepa
585
527
 
586
528
  **How is this different from Astro?**
587
529
 
588
- Astro is a static site generator. Uniweb is a content architecture that works with any deployment (static, SSR, or platform-managed). The Foundation model means components are reusable across sites and ready for visual editing.
530
+ Astro is a static site generator. Uniweb is a component content architecture that works with any deployment (static, SSR, or platform-managed). The foundation model means components are portable across sites and ready for integration with visual editors.
589
531
 
590
532
  **Do I need uniweb.app?**
591
533
 
592
534
  No. Local markdown files work great for developer-managed sites. The platform adds dynamic content, visual editing, and team collaboration when you need it.
593
535
 
536
+ **Can I use an existing component library?**
537
+
538
+ Yes. Foundations are standard React. Import any library into your foundation components. The `{ content, params }` interface only applies to exposed components—internal components use regular props.
539
+
594
540
  **Is this SEO-friendly?**
595
541
 
596
- Yes. Content is pre-embedded in the initial HTML—no fetch waterfalls, no layout shifts, instant interaction. Meta tags are generated per page for proper social sharing previews. SSG and SSR are also supported.
542
+ Yes. Content is pre-embedded in the initial HTML—no fetch waterfalls, no layout shifts. Meta tags are generated per page. SSG is supported by default.
597
543
 
598
544
  **What about dynamic routes?**
599
545
 
600
- Pages can define data sources that auto-generate subroutes. A `/blog` page can have an index (listing) and a `[slug]` template that renders each post. No manual folders for every entry.
546
+ Pages can define data sources that auto-generate subroutes. A `/blog` page can have an index and a `[slug]` template that renders each post.
601
547
 
602
548
  ## Related Packages
603
549
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.2.39",
3
+ "version": "0.2.41",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,9 +37,9 @@
37
37
  "js-yaml": "^4.1.0",
38
38
  "prompts": "^2.4.2",
39
39
  "tar": "^7.0.0",
40
- "@uniweb/build": "0.1.20",
40
+ "@uniweb/build": "0.1.22",
41
+ "@uniweb/core": "0.1.10",
41
42
  "@uniweb/kit": "0.1.5",
42
- "@uniweb/runtime": "0.2.11",
43
- "@uniweb/core": "0.1.8"
43
+ "@uniweb/runtime": "0.2.11"
44
44
  }
45
45
  }
@@ -46,3 +46,32 @@ sections:
46
46
  This is clearer than prefix notation and easier to restructure.
47
47
 
48
48
  **Advanced:** Comma prefixes (`1,1-`, `1,2-`) also denote hierarchy, but explicit arrays are recommended for readability.
49
+
50
+ ### Page Ordering and Index Pages
51
+
52
+ Parent controls which page is the "index" for its route. The index page gets the parent's route.
53
+
54
+ **In `site.yml`** (for top-level pages):
55
+ ```yaml
56
+ name: My Site
57
+ pages: [home, about, features] # First (home) is homepage at /
58
+ # Or just name the homepage:
59
+ index: home
60
+ ```
61
+
62
+ **In `page.yml`** (for child pages):
63
+ ```yaml
64
+ title: Documentation
65
+ pages: [getting-started, installation, config] # First is index for /docs
66
+ # Or just name the index:
67
+ index: getting-started
68
+ ```
69
+
70
+ **How it works:**
71
+ - `pages: [a, b, c]` — Explicit order, first item gets parent route
72
+ - `index: name` — Just set the index, auto-discover the rest
73
+ - Omit both — Sort by `order` prop, lowest becomes index
74
+
75
+ **Example:** If `getting-started` is the index for `/docs`:
76
+ - `pages/docs/getting-started/` → route `/docs`
77
+ - `pages/docs/installation/` → route `/docs/installation`
@@ -138,6 +138,20 @@ sections:
138
138
  ```
139
139
  This is clearer than prefix notation (`1,1-`, `1,2-`) and easier to restructure.
140
140
 
141
+ **Page ordering:** Parent controls which page is the index for its route:
142
+ ```yaml
143
+ # In site.yml (top-level pages)
144
+ pages: [home, about, docs] # First is homepage at /
145
+ index: home # Or just name the homepage
146
+
147
+ # In page.yml (child pages)
148
+ pages: [getting-started, installation] # First is index for this route
149
+ index: getting-started # Or just name the index
150
+ ```
151
+ - `pages: [a, b, c]` — First gets parent route (becomes index)
152
+ - `index: name` — Just set index, auto-discover rest
153
+ - Omit both — Sort by `order` prop, lowest becomes index
154
+
141
155
  ## Component Development
142
156
 
143
157
  ### Props Interface