uniweb 0.11.0 → 0.12.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/README.md CHANGED
@@ -18,7 +18,7 @@ npm install
18
18
  npm run dev
19
19
  ```
20
20
 
21
- Edit files in `site/pages/` and `foundation/src/sections/` to see changes instantly.
21
+ Edit files in `site/pages/` and `src/sections/` to see changes instantly.
22
22
 
23
23
  > **pnpm ready** — `pnpm create uniweb` works out of the box. Projects include both `pnpm-workspace.yaml` and npm workspaces.
24
24
 
@@ -62,10 +62,10 @@ The `build` command outputs to `site/dist/`. With pre-rendering enabled (the def
62
62
 
63
63
  Every project starts as a workspace with two packages:
64
64
 
65
- - **`site/`** — Content, pages, entry point
66
- - **`foundation/`** — React components
65
+ - **`site/`** — Content, pages, entry point (package name: `site`)
66
+ - **`src/`** — React components, the site's source code (package name: `src`)
67
67
 
68
- Content authors work in markdown. Component authors work in React. Neither can break the other's work. As your project grows, use `uniweb add` to add more foundations, sites, and extensions.
68
+ A site is pure content. A foundation is the site's source code — that's why it lives in `src/`. Content authors work in markdown. Component authors work in React. Neither can break the other's work. As your project grows, use `uniweb add` to add more foundations, sites, and extensions.
69
69
 
70
70
  ```
71
71
  my-project/
@@ -75,19 +75,21 @@ my-project/
75
75
  │ │ ├── page.yml # Page metadata
76
76
  │ │ └── hero.md # Section content
77
77
  │ ├── locales/ # i18n (hash-based translations)
78
- │ ├── main.js # Entry point (~6 lines)
78
+ │ ├── entry.js # Entry point (~6 lines)
79
79
  │ ├── vite.config.js # 3-line config
80
80
  │ └── public/ # Static assets
81
81
 
82
- └── foundation/ # Your components
83
- ├── src/
84
- │ ├── sections/ # Section types (addressable from markdown)
85
- │ ├── Hero.jsx # Bare file → section type (no meta.js needed)
86
- │ └── Features/
87
- ├── meta.js # Content interface (params, presets)
88
- │ │ └── Features.jsx
89
- │ └── components/ # Regular React components
90
- │ └── Button.jsx
82
+ └── src/ # Foundation package (the site's source)
83
+ ├── sections/ # Section types (addressable from markdown)
84
+ │ ├── Hero.jsx # Bare file → section type (no meta.js needed)
85
+ └── Features/
86
+ ├── meta.js # Content interface (params, presets)
87
+ └── Features.jsx
88
+ ├── components/ # Regular React components
89
+ │ └── Button.jsx
90
+ ├── main.js # Package exports (vars, defaultLayout, props)
91
+ ├── styles.css # Tailwind v4 + CSS tokens
92
+ ├── package.json # name: "src"
91
93
  ├── vite.config.js # 3-line config
92
94
  └── dist/ # Built output
93
95
  ```
@@ -204,7 +206,7 @@ After creating your project:
204
206
 
205
207
  3. **Learn the configuration** — Run `uniweb docs site` or `uniweb docs page` for quick reference on configuration options.
206
208
 
207
- 4. **Create a section type** — Add a file to `foundation/src/sections/` (e.g., `Banner.jsx`) and rebuild. Bare files at the root are discovered automatically — no `meta.js` needed. Add `meta.js` when you want to declare params or presets. See the [Component Metadata Reference](https://github.com/uniweb/docs/blob/main/reference/component-metadata.md) for the full schema.
209
+ 4. **Create a section type** — Add a file to `src/sections/` (e.g., `Banner.jsx`) and rebuild. Bare files at the root are discovered automatically — no `meta.js` needed. Add `meta.js` when you want to declare params or presets. See the [Component Metadata Reference](https://github.com/uniweb/docs/blob/main/reference/component-metadata.md) for the full schema.
208
210
 
209
211
  The `meta.js` file defines what content and parameters a component accepts. The runtime uses this metadata to apply defaults and guarantee content structure—no defensive null checks needed in your component code.
210
212
 
@@ -228,7 +230,7 @@ Save and see the change instantly in your browser.
228
230
 
229
231
  ### Your First Component Change
230
232
 
231
- Open `foundation/src/sections/Hero.jsx`. The component receives parsed content:
233
+ Open `src/sections/Hero.jsx`. The component receives parsed content:
232
234
 
233
235
  ```jsx
234
236
  export default function Hero({ content }) {
@@ -241,7 +243,7 @@ The parser extracts semantic elements from markdown—`title` from the first hea
241
243
 
242
244
  ## Foundations Are Portable
243
245
 
244
- 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.
246
+ The `src/` folder (your project's foundation) 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.
245
247
 
246
248
  **Three ways to use a foundation:**
247
249
 
@@ -251,7 +253,7 @@ The `foundation/` folder ships with your project as a convenience, but a foundat
251
253
  | **npm package** | `npm add @acme/foundation` | Distributing via standard package tooling |
252
254
  | **Runtime link** | Foundation loads from a URL | Independent release cycles, platform-managed sites |
253
255
 
254
- 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.
256
+ You can delete the `src/` 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.
255
257
 
256
258
  **This enables two development patterns:**
257
259
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,14 +41,14 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/core": "0.7.8",
45
- "@uniweb/kit": "0.9.8",
46
- "@uniweb/runtime": "0.8.9"
44
+ "@uniweb/core": "0.7.9",
45
+ "@uniweb/kit": "0.9.9",
46
+ "@uniweb/runtime": "0.8.10"
47
47
  },
48
48
  "peerDependencies": {
49
- "@uniweb/build": "0.12.0",
49
+ "@uniweb/build": "0.13.1",
50
50
  "@uniweb/content-reader": "1.1.9",
51
- "@uniweb/semantic-parser": "1.1.15"
51
+ "@uniweb/semantic-parser": "1.1.16"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "@uniweb/build": {
@@ -56,21 +56,23 @@ Most projects start as a workspace with two packages:
56
56
 
57
57
  ```
58
58
  project/
59
- ├── foundation/ # Component developer's domain
59
+ ├── src/ # Component developer's domain (the foundation package)
60
60
  ├── site/ # Content author's domain
61
61
  └── pnpm-workspace.yaml
62
62
  ```
63
63
 
64
- - **Foundation** (developer): React components. Those in `src/sections` and `src/layouts` are *section types* selectable by content authors via `type:` in frontmatter, or used for site-level layout areas (header, footer, panel). Most have a `meta.js` with metadata in them. Everything in `src/components` (or elsewhere) is ordinary React the developer's workbench for helper components that section types import and compose internally.
65
- - **Site** (content author): Markdown content + configuration. Each section file references a section type. Authors work here without touching foundation code. It may also contain collections of structured content and/or references to external data sources.
64
+ A site is pure content. A foundation is the site's source codethat's why it lives in `src/`. The foundation's `package.json::name` is `src` (a unique workspace package name; symmetric with `site`).
66
65
 
67
- **The composition boundary:** Authors compose pages from finished section types — choosing types, writing content, setting params. Developers compose section types from building blocks importing helpers from `src/components/`, using libraries, writing JSX. These are two different levels of composition. The section type is the boundary between them. Don't expose building-block composition to authors; build complete, self-contained section types that handle their own internal structure.
66
+ - **Foundation** (developer, in `src/`): React components. Those in `src/sections` and `src/layouts` are *section types*selectable by content authors via `type:` in frontmatter, or used for site-level layout areas (header, footer, panel). Most have a `meta.js` with metadata in them. Everything in `src/components` (or elsewhere) is ordinary React the developer's workbench for helper components that section types import and compose internally.
67
+ - **Site** (content author, in `site/`): Markdown content + configuration. Each section file references a section type. Authors work here without touching foundation code. It may also contain collections of structured content and/or references to external data sources.
68
68
 
69
- > Multi-site projects use sub-folders with site/foundation pairs in them, or segregate foundations and sites into separate folders (`foundations/`, `sites/`).
69
+ **The composition boundary:** Authors compose pages from finished section types — choosing types, writing content, setting params. Developers compose section types from building blocks — importing helpers from `components/`, using libraries, writing JSX. These are two different levels of composition. The section type is the boundary between them. Don't expose building-block composition to authors; build complete, self-contained section types that handle their own internal structure.
70
+
71
+ > Multi-site projects use sub-folders with site/foundation pairs in them (each project gets its own `src/` + `site/`), or segregate foundations and sites into separate folders (`foundations/`, `sites/`).
70
72
 
71
73
  ## Project Setup
72
74
 
73
- Always use the CLI to scaffold projects — never write `package.json`, `vite.config.js`, `main.js`, or `index.html` manually. The CLI resolves correct versions and structure.
75
+ Always use the CLI to scaffold projects — never write `package.json`, `vite.config.js`, `entry.js`, or `index.html` manually. The CLI resolves correct versions and structure.
74
76
 
75
77
  **npm or pnpm.** Projects include both `pnpm-workspace.yaml` and npm workspaces. Replace `pnpm` with `npm` in any command below.
76
78
 
@@ -89,18 +91,26 @@ uniweb add project docs
89
91
  pnpm install
90
92
  ```
91
93
 
92
- This creates `docs/foundation/` + `docs/site/` with package names `docs-foundation` and `docs-site`. Use `--from <template>` to apply template content to both packages.
94
+ This creates `docs/src/` + `docs/site/` with package names `docs-src` and `docs-site`. Use `--from <template>` to apply template content to both packages.
93
95
 
94
96
  ### Adding individual packages
95
97
 
98
+ The CLI creates exactly the folder you ask for. No silent nesting under `foundations/` or `sites/` — the framework doesn't require a particular folder layout, so the CLI doesn't impose one.
99
+
96
100
  ```bash
97
- uniweb add foundation # First foundation → ./foundation/
98
- uniweb add foundation ui # Named → ./ui/
99
- uniweb add site # First site ./site/
100
- uniweb add site blog # Named → ./blog/
101
+ uniweb add foundation # No name → ./src/ (package: src)
102
+ uniweb add foundation ui # Bare name → ./ui/ (package: ui)
103
+ uniweb add foundation foundations/effects # Slashfolder is the path (package: effects)
104
+ uniweb add foundation marketing --path libs # name + parent → ./libs/marketing/ (package: marketing)
105
+
106
+ uniweb add site # No name → ./site/ (package: site)
107
+ uniweb add site blog # Bare name → ./blog/ (package: blog)
108
+ uniweb add site sites/store # Slash → folder is the path (package: store)
101
109
  ```
102
110
 
103
- The name is both the directory name and the package name. Use `--project <n>` to co-locate under a project directory (e.g., `--project docs` `docs/foundation/`).
111
+ If the target folder already exists, or the package name is already taken by another package in the workspace, the CLI stops with a precise error and suggests alternatives. The check uses the same `classifyPackage` logic the build uses, so cross-type collisions are caught (you can't `add site marketing` if a foundation named `marketing` is already in the workspace).
112
+
113
+ Use `--project <n>` to co-locate a foundation+site pair under a project directory: `--project docs` → `docs/src/` (package `docs-src`) + `docs/site/` (package `docs-site`). The `-src` / `-site` suffix is the convention for co-located projects only — single-foundation workspaces use bare `src`.
104
114
 
105
115
  ### Adding section types
106
116
 
@@ -109,19 +119,19 @@ uniweb add section Hero
109
119
  uniweb add section Hero --foundation ui # When multiple foundations exist
110
120
  ```
111
121
 
112
- Creates `src/sections/Hero/index.jsx` and `meta.js` with a minimal CCA-proper starter. The dev server picks it up automatically — no build or install needed.
122
+ Creates `sections/Hero/index.jsx` and `meta.js` with a minimal CCA-proper starter. The dev server picks it up automatically — no build or install needed.
113
123
 
114
124
  ### What the CLI generates
115
125
 
116
- **Foundation** (`vite.config.js`, `package.json`, `src/foundation.js`, `src/styles.css`):
126
+ **Foundation** (`vite.config.js`, `package.json`, `main.js`, `styles.css`):
117
127
  - `defineFoundationConfig()` in vite.config.js
118
128
  - Dependencies pinned to current npm versions
119
129
  - `@import "@uniweb/kit/theme-tokens.css"` in styles.css
120
130
 
121
- **Site** (`vite.config.js`, `package.json`, `main.js`, `index.html`, `site.yml`):
131
+ **Site** (`vite.config.js`, `package.json`, `entry.js`, `index.html`, `site.yml`):
122
132
  - `defineSiteConfig()` in vite.config.js
123
133
  - `react-router-dom` in devDependencies (required by pnpm strict mode)
124
- - Standard `start()` call in main.js
134
+ - Standard `start()` call in entry.js
125
135
 
126
136
  ## Commands
127
137
 
@@ -134,6 +144,116 @@ pnpm preview # Preview production build (SSG + SPA)
134
144
 
135
145
  ---
136
146
 
147
+ ## `package.json` `uniweb` configuration
148
+
149
+ The `uniweb` block in `package.json` carries platform-specific configuration that doesn't belong in the npm-standard fields. All fields are optional; defaults apply when omitted.
150
+
151
+ ```json
152
+ {
153
+ "name": "src",
154
+ "version": "1.0.0",
155
+ "uniweb": {
156
+ "id": "marketing",
157
+ "runtimePolicy": "auto-minor"
158
+ },
159
+ "dependencies": {
160
+ "@uniweb/core": "0.7.8",
161
+ "@uniweb/runtime": "0.8.9"
162
+ }
163
+ }
164
+ ```
165
+
166
+ | Field | Where used | Default | Purpose |
167
+ |---|---|---|---|
168
+ | `id` | `uniweb publish` | (set on first publish via the prompt, or via `--name`) | The foundation's published id — the bare-name segment in `~handle/<id>` or `@org/<id>`. Decoupled from `package.json::name` (a workspace concern), so renaming the foundation on the registry doesn't ripple through site dependencies. |
169
+ | `namespace` | `uniweb publish` | (none — see scope resolution) | Legacy explicit org-namespace override. Equivalent to using a scoped `package.json::name` (`"@myorg/foundation"`). Rarely needed in modern foundations. |
170
+ | `runtimePolicy` | `dist/runtime-pin.json` (foundation build) | `"auto-minor"` | Controls how sites using this foundation receive runtime updates. Three values: `"exact"`, `"auto-patch"`, `"auto-minor"`. See "Foundation runtime policy" below. |
171
+
172
+ **On the split between `package.json::name` and `uniweb.id`:** the workspace name is what pnpm uses for `file:` linking and what `site.yml::foundation` references. The published id is what the registry stores. Keeping them separate means renaming on the registry (e.g. `marketing` → `marketing-pro`) is a one-shot `uniweb publish --name marketing-pro` — it persists to `uniweb.id` without touching the workspace.
173
+
174
+ These are the only fields the platform consumes today. Future platform features that need static configuration will land here too.
175
+
176
+ ---
177
+
178
+ ## Foundation runtime policy
179
+
180
+ (Foundation authors only — sites don't set this.)
181
+
182
+ When a foundation builds, it pins the `@uniweb/runtime` version it was built against (from its own dependencies) into `dist/runtime-pin.json`. Foundations can also declare a *policy* that controls how the runtime version moves forward on already-published sites:
183
+
184
+ ```json
185
+ // foundation's package.json
186
+ {
187
+ "name": "@uniweb/votiverse",
188
+ "version": "0.1.1",
189
+ "uniweb": {
190
+ "runtimePolicy": "auto-minor"
191
+ },
192
+ "dependencies": {
193
+ "@uniweb/core": "0.7.8"
194
+ }
195
+ }
196
+ ```
197
+
198
+ Three valid values:
199
+
200
+ | Value | Meaning |
201
+ |---|---|
202
+ | `exact` | The site stays on exactly the runtime version this foundation built against. Newer runtime versions are not auto-applied. |
203
+ | `auto-patch` | The site auto-updates within the same `MAJOR.MINOR.x` (e.g. `0.8.9` → `0.8.10`). Conservative; matches typical npm patch semantics. |
204
+ | `auto-minor` | The site auto-updates within the same `MAJOR.x.y` (e.g. `0.8.9` → `0.9.0`). |
205
+
206
+ **Default when unset:** `auto-minor`. Most foundations don't need to set this field — the platform's runtime is internally backwards-compatible at the minor level by convention, and `auto-minor` lets your foundation's sites pick up bug fixes and additive features without rebuilding the foundation.
207
+
208
+ Set `exact` if your foundation depends on undocumented runtime internals or has been audited against one specific runtime release and you don't want to allow drift.
209
+
210
+ The field is read by `@uniweb/build` at build time and emitted into `dist/runtime-pin.json` next to the runtime version:
211
+
212
+ ```json
213
+ // dist/runtime-pin.json (auto-generated)
214
+ { "runtime": "0.8.9", "policy": "auto-minor" }
215
+ ```
216
+
217
+ Sites using your foundation will see this pin + policy when they're served. Site owners cannot override your policy choice — this is the foundation author's contract with the platform.
218
+
219
+ ### Where does the pinned runtime version come from?
220
+
221
+ You may notice your foundation's `dist/runtime-pin.json` reports a runtime version (e.g. `0.8.9`) without your `package.json` declaring `@uniweb/runtime` anywhere. This is intentional.
222
+
223
+ `@uniweb/runtime` is pulled in **transitively** through `@uniweb/build` (which every foundation has as a devDependency). The runtime version baked into your foundation's pin is whichever version your `@uniweb/build` version pulled in at install time:
224
+
225
+ ```
226
+ your foundation
227
+ └─ devDependencies: "@uniweb/build": "0.12.0"
228
+ └─ pulls in @uniweb/runtime (whatever version that build version locks)
229
+ → resolved at install time → version goes into dist/runtime-pin.json
230
+ ```
231
+
232
+ Practical implications:
233
+
234
+ - **You don't need to add `@uniweb/runtime` to your foundation's dependencies.** Runtime is the host environment, not a foundation import.
235
+ - **To bump the runtime version your foundation pins, bump your `@uniweb/build` dep.** When a newer build version ships pulling in a newer runtime, updating your devDependency is how you adopt it.
236
+ - **You can override by adding `@uniweb/runtime` directly to your foundation's `dependencies`** — but this is rarely needed and creates two sources of truth for the runtime version. Don't do this unless you have a specific reason.
237
+ - **Whichever runtime version is pinned, your `runtimePolicy` controls how sites can move forward beyond it.** Pinning `0.8.9` with `auto-minor` lets sites pick up `0.9.0` or higher (within the same major); pinning with `exact` locks them at `0.8.9` until you rebuild your foundation.
238
+
239
+ ### What happens when fields aren't set
240
+
241
+ The system has multi-layer fallbacks so missing or partial information is always handled gracefully:
242
+
243
+ | Scenario | What happens |
244
+ |---|---|
245
+ | **`uniweb.runtimePolicy` not set in `package.json`** | `dist/runtime-pin.json` is emitted with the runtime version but no `policy` field. At serve time the platform applies `auto-minor` as the implicit default. Most foundations don't need to set `runtimePolicy` — leaving it unset is the correct choice when you want default behavior. |
246
+ | **`@uniweb/runtime` not resolvable at build time** | The build silently skips emitting `runtime-pin.json`. This was the pre-Strategy-S behavior — your foundation falls back to the legacy self-contained build path. New foundations created with `npx uniweb create` always have `@uniweb/runtime` as a dependency, so this only affects unusual workspace setups. |
247
+ | **`runtime-pin.json` is missing or malformed** | The platform's edge dispatcher detects the absence and serves the foundation through the legacy bundling path (the foundation's own `ssr-worker-bundle.js` is used). Your sites still work; they just don't participate in runtime propagation. |
248
+ | **`runtime-pin.json` has a `runtime` version that's not actually deployed to the platform** | The site publish flow rejects the publish with a clear error message asking you to deploy the pinned runtime version first. This is caught at publish time, not at serve time. |
249
+ | **You set `policy: "auto-minor"` but no compatible newer version exists** | The site stays on the version you pinned. The resolver only moves forward when a newer version satisfying the policy is actually available. |
250
+
251
+ Bottom line: a foundation that doesn't set `runtimePolicy` gets `auto-minor` behavior automatically. A foundation that doesn't ship `runtime-pin.json` at all (e.g. a legacy build) still serves correctly through the platform's compatibility path — you just don't get the propagation benefits. Set `runtimePolicy` explicitly only when you want to override the default (typically to `exact` for stability-critical builds).
252
+
253
+ (For platform operators interested in how propagation walks consume this field, see `kb/platform/operations/version-propagation.md` in the private docs.)
254
+
255
+ ---
256
+
137
257
  ## Content Authoring
138
258
 
139
259
  The decision rule: **would a content author need to change this?** Yes → it belongs in markdown, frontmatter, or a tagged data block. No → it belongs in component code.
@@ -833,7 +953,7 @@ Foundation-level CSS variables are for values that must stay consistent **across
833
953
 
834
954
  If you need them, declare vars in two places:
835
955
 
836
- **`foundation.js`** — metadata for the editor and schema:
956
+ **`main.js`** — metadata for the editor and schema:
837
957
 
838
958
  ```js
839
959
  export const vars = {
@@ -853,7 +973,7 @@ export const vars = {
853
973
  }
854
974
  ```
855
975
 
856
- The `styles.css` declaration ensures defaults are present in the foundation's CSS output and enables Tailwind shorthand (`rounded-(--radius)` instead of `rounded-[var(--radius)]`). The `foundation.js` declaration provides descriptions and types for the visual editor. Sites override values in `theme.yml` under `vars:` — the site's theme CSS takes priority over the foundation defaults.
976
+ The `styles.css` declaration ensures defaults are present in the foundation's CSS output and enables Tailwind shorthand (`rounded-(--radius)` instead of `rounded-[var(--radius)]`). The `main.js` declaration provides descriptions and types for the visual editor. Sites override values in `theme.yml` under `vars:` — the site's theme CSS takes priority over the foundation defaults.
857
977
 
858
978
  **Common mistake:** Using foundation vars for values that belong to a specific component. A header height is a layout param, not a foundation var — the layout component owns it. A sidebar width is a layout param too. Foundation vars are for values that multiple unrelated components share — radii, spacing, shadows.
859
979
 
@@ -862,7 +982,7 @@ The `styles.css` declaration ensures defaults are present in the foundation's CS
862
982
  Tokens handle context adaptation — the hard problem. **They are a floor, not a ceiling.** A great foundation adds design vocabulary on top:
863
983
 
864
984
  ```css
865
- /* foundation/src/styles.css */
985
+ /* styles.css */
866
986
  .border-subtle { border-color: color-mix(in oklch, var(--border), transparent 50%); }
867
987
  .border-strong { border-color: color-mix(in oklch, var(--border), var(--heading) 30%); }
868
988
  .text-tertiary { color: color-mix(in oklch, var(--body), var(--subtle) 50%); }
@@ -1245,10 +1365,10 @@ This is the system-building pattern at its clearest: **section types are the pub
1245
1365
 
1246
1366
  ### Section components are composites
1247
1367
 
1248
- A section component is rarely a single flat render. It imports helper components from `src/components/` and utilities from `src/utils/` to build a complex UI while presenting a single `type:` to the content author. These directories are the developer's workbench — ordinary React and JS, not selectable by authors, not auto-discovered.
1368
+ A section component is rarely a single flat render. It imports helper components from `components/` and utilities from `utils/` to build a complex UI while presenting a single `type:` to the content author. These directories are the developer's workbench — ordinary React and JS, not selectable by authors, not auto-discovered.
1249
1369
 
1250
1370
  ```jsx
1251
- // src/sections/Pricing/index.jsx
1371
+ // sections/Pricing/index.jsx
1252
1372
  import PricingCard from '#components/PricingCard'
1253
1373
  import formatPrice from '#utils/formatPrice'
1254
1374
 
@@ -1264,7 +1384,7 @@ export default function Pricing({ content, params }) {
1264
1384
  }
1265
1385
  ```
1266
1386
 
1267
- The content author writes `type: Pricing` and defines tiers as content items. The section component maps items to cards using a helper component from `src/components/` and a formatting utility from `src/utils/`. Neither is selectable by authors — they're implementation details behind the section type boundary.
1387
+ The content author writes `type: Pricing` and defines tiers as content items. The section component maps items to cards using a helper component from `components/` and a formatting utility from `utils/`. Neither is selectable by authors — they're implementation details behind the section type boundary.
1268
1388
 
1269
1389
  **When to reach for this pattern:** When a page type has consistent structural elements (header bars, navigation footers, contextual sidebars) that the content author shouldn't need to add as separate sections. If the author would have to add the same boilerplate sections to every page of a certain type, the section component should compose them internally.
1270
1390
 
@@ -1273,7 +1393,7 @@ The content author writes `type: Pricing` and defines tiers as content items. Th
1273
1393
  ### Foundation Organization
1274
1394
 
1275
1395
  ```
1276
- foundation/src/
1396
+ src/ # the foundation package (folder name is `src`)
1277
1397
  ├── sections/ # Section types (auto-discovered)
1278
1398
  │ ├── Hero.jsx # Bare file — no folder needed
1279
1399
  │ ├── Features/ # Folder when you need meta.js
@@ -1293,17 +1413,21 @@ foundation/src/
1293
1413
  │ └── Card.jsx
1294
1414
  ├── utils/ # Helper functions, non-React logic
1295
1415
  │ └── splitContent.js
1296
- └── styles.css
1416
+ ├── main.js
1417
+ ├── styles.css
1418
+ └── package.json # name: "src"
1297
1419
  ```
1298
1420
 
1299
- **Discovery:** PascalCase files/folders at root of `sections/` are auto-discovered. Nested levels require `meta.js`. Lowercase directories are organizational only. `hidden: true` excludes a component entirely. Everything outside `sections/` is ordinary React.
1421
+ **Discovery:** PascalCase files/folders at root of `src/sections/` are auto-discovered. Nested levels require `meta.js`. Lowercase directories are organizational only. `hidden: true` excludes a component entirely. Everything outside `src/sections/` is ordinary React.
1422
+
1423
+ **Source root.** The foundation package's source files live at the package root — the `src/` folder *is* the foundation. The build reads `package.json::main` to know that (for new scaffolds, `main: "./_entry.generated.js"`). Older foundations may use an even-more-nested layout where the source lives in `foundation/src/` and `main` points to `./src/_entry.generated.js`; both shapes work through the same code path.
1300
1424
 
1301
- **Import aliases:** Foundations include subpath imports in `package.json` that map to `src/` subdirectories. Use them instead of brittle relative paths:
1425
+ **Import aliases:** Foundations include subpath imports in `package.json` for shared internals. Use them instead of brittle relative paths:
1302
1426
 
1303
1427
  | Alias | Maps to | Use for |
1304
1428
  |-------|---------|---------|
1305
- | `#components/*` | `./src/components/*` | Shared React components |
1306
- | `#utils/*` | `./src/utils/*` | Helper functions, non-React logic |
1429
+ | `#components/*` | `./components/*` | Shared React components |
1430
+ | `#utils/*` | `./utils/*` | Helper functions, non-React logic |
1307
1431
 
1308
1432
  ```jsx
1309
1433
  // ✅ Clean — use aliases
@@ -1316,7 +1440,7 @@ import LessonHeader from '../../components/LessonHeader'
1316
1440
 
1317
1441
  Within the same directory (e.g., one component importing a sibling), use normal relative imports (`./AIFeedbackCard`).
1318
1442
 
1319
- **Foundation entry shape (`src/foundation.js`).** A single `export default { … }` whose top-level keys are the capabilities the foundation provides — e.g. `name`, `description`, `defaultLayout`, `defaultSection`, `viewTransitions`, `props`, `defaultInsets`, `xref`, `outputs`, `handlers`. Optionally a named `vars` export for theme-variable metadata (see *Foundation variables*). Everything else (section types, layouts) is auto-discovered from `src/sections/` and `src/layouts/` and merged in by `@uniweb/build`. The build wraps your default export under `default.capabilities` in the produced `dist/foundation.js`; you don't write that wrapper yourself, and most foundation code never sees it. The one place it matters: when you import your **own** `src/foundation.js` from a foundation component (e.g., a download button calling `compileDocument(website, { foundation })`), you get the bare default object — pass it through directly, Press handles both shapes.
1443
+ **Foundation entry shape (`main.js`).** A single `export default { … }` whose top-level keys are the capabilities the foundation provides — e.g. `name`, `description`, `defaultLayout`, `defaultSection`, `viewTransitions`, `props`, `defaultInsets`, `xref`, `outputs`, `handlers`. Optionally a named `vars` export for theme-variable metadata (see *Foundation variables*). Everything else (section types, layouts) is auto-discovered from `sections/` and `layouts/` and merged in by `@uniweb/build`. The build wraps your default export under `default.capabilities` in the produced `dist/foundation.js`; you don't write that wrapper yourself, and most foundation code never sees it. The one place it matters: when you import your **own** `main.js` from a foundation component (e.g., a download button calling `compileDocument(website, { foundation })`), you get the bare default object — pass it through directly, Press handles both shapes.
1320
1444
 
1321
1445
  ### Website and Page APIs
1322
1446
 
@@ -1438,10 +1562,10 @@ Other navigation methods: `block.getPrevBlockInfo()`, `block.page.getFirstBodyBl
1438
1562
 
1439
1563
  ### Custom Layouts
1440
1564
 
1441
- Layouts live in `foundation/src/layouts/` and are auto-discovered:
1565
+ Layouts live in `layouts/` (inside the foundation package) and are auto-discovered:
1442
1566
 
1443
1567
  ```js
1444
- // foundation/src/foundation.js
1568
+ // main.js
1445
1569
  export default {
1446
1570
  name: 'My Template',
1447
1571
  description: 'A brief description',
@@ -1450,7 +1574,7 @@ export default {
1450
1574
  ```
1451
1575
 
1452
1576
  ```jsx
1453
- // foundation/src/layouts/DocsLayout/index.jsx
1577
+ // layouts/DocsLayout/index.jsx
1454
1578
  export default function DocsLayout({ header, body, footer, left, right, params }) {
1455
1579
  return (
1456
1580
  <div className="min-h-screen flex flex-col">
@@ -1485,14 +1609,14 @@ Named subdirectories are self-contained — no inheritance. Layout cascade: `pag
1485
1609
 
1486
1610
  ## Content Handlers
1487
1611
 
1488
- Content handlers are a transform layer that runs between data assembly and the component. They're declared in `foundation.js` and apply to every section in the foundation. The standard content shape (title, paragraphs, items, sequence) is the default — handlers can reshape it.
1612
+ Content handlers are a transform layer that runs between data assembly and the component. They're declared in `main.js` and apply to every section in the foundation. The standard content shape (title, paragraphs, items, sequence) is the default — handlers can reshape it.
1489
1613
 
1490
1614
  ### The three hooks
1491
1615
 
1492
1616
  The foundation declares handlers as an object in its default export:
1493
1617
 
1494
1618
  ```js
1495
- // foundation.js
1619
+ // main.js
1496
1620
  export default {
1497
1621
  handlers: {
1498
1622
  data: (data, block) => { /* ... */ },
@@ -1598,7 +1722,7 @@ Don't port line-by-line. Study the source, then rebuild from first principles. O
1598
1722
 
1599
1723
  2. **Use named layouts** for different page groups — marketing layout for landing pages, docs layout for `/docs/*`.
1600
1724
 
1601
- 3. **Dump legacy components under `src/components/`** — they're not section types. Import from section types during transition.
1725
+ 3. **Dump legacy components under `components/`** — they're not section types. Import from section types during transition.
1602
1726
 
1603
1727
  4. **Create section types one at a time.** Migration levels:
1604
1728
  - **Level 0**: Paste the original as one section type. Routing and dev tooling work immediately.
@@ -1610,13 +1734,13 @@ Don't port line-by-line. Study the source, then rebuild from first principles. O
1610
1734
 
1611
1735
  6. **Name by purpose, not content** — `TheModel` → `SplitContent`, `WorkModes` → `FeatureColumns`.
1612
1736
 
1613
- 7. **UI helpers → `components/`** — Buttons, badges, cards in `src/components/` (no `meta.js`, not selectable by authors).
1737
+ 7. **UI helpers → `components/`** — Buttons, badges, cards in `components/` (no `meta.js`, not selectable by authors).
1614
1738
 
1615
1739
  ---
1616
1740
 
1617
1741
  ## Tailwind CSS v4
1618
1742
 
1619
- Foundation styles in `foundation/src/styles.css`:
1743
+ Foundation styles in `styles.css`:
1620
1744
 
1621
1745
  ```css
1622
1746
  @import "tailwindcss";
@@ -1660,10 +1784,10 @@ uniweb add project marketing --from marketing
1660
1784
  pnpm install
1661
1785
  ```
1662
1786
 
1663
- This creates `marketing/foundation/` + `marketing/site/` alongside your existing project. You don't need to build or run it — just read the source files to see how working components handle content, params, theming, and data.
1787
+ This creates `marketing/src/` (the foundation, package `marketing-src`) + `marketing/site/` (the site, package `marketing-site`) alongside your existing project. You don't need to build or run it — just read the source files to see how working components handle content, params, theming, and data.
1664
1788
 
1665
1789
  **What to study:**
1666
- - `{name}/foundation/src/sections/` — components with meta.js (content expectations, params, presets)
1790
+ - `{name}/src/sections/` — components with meta.js (content expectations, params, presets)
1667
1791
  - `{name}/site/pages/` — real content files showing markdown → component mapping
1668
1792
  - `{name}/site/theme.yml` + `site.yml` — theming and configuration patterns
1669
1793