uniweb 0.10.13 → 0.12.0
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 +20 -18
- package/package.json +2 -2
- package/partials/agents.md +163 -39
- package/src/commands/add.js +152 -233
- package/src/commands/build.js +14 -42
- package/src/commands/deploy.js +262 -3
- package/src/commands/docs.js +5 -6
- package/src/commands/doctor.js +21 -22
- package/src/commands/publish.js +255 -34
- package/src/framework-index.json +3 -3
- package/src/index.js +27 -14
- package/src/utils/auth.js +82 -6
- package/src/utils/names.js +9 -2
- package/src/utils/registry.js +88 -16
- package/src/utils/scaffold.js +8 -2
- package/src/utils/workspace.js +8 -46
- package/starter/site/pages/home/1-welcome.md.hbs +1 -1
- package/templates/foundation/{src/foundation.js.hbs → main.js.hbs} +1 -1
- package/templates/foundation/package.json.hbs +6 -6
- package/templates/foundation/{src/styles.css → styles.css} +1 -1
- package/templates/site/index.html.hbs +1 -1
- package/templates/site/theme.yml +1 -1
- package/templates/workspace/README.md.hbs +9 -7
- /package/starter/foundation/{src/foundation.js → main.js} +0 -0
- /package/starter/foundation/{src/sections → sections}/Section/index.jsx +0 -0
- /package/starter/foundation/{src/sections → sections}/Section/meta.js +0 -0
- /package/templates/foundation/{src/components → components}/.gitkeep +0 -0
- /package/templates/foundation/{src/sections → sections}/.gitkeep +0 -0
- /package/templates/site/{main.js → entry.js} +0 -0
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 `
|
|
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
|
-
- **`
|
|
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
|
-
│ ├──
|
|
78
|
+
│ ├── entry.js # Entry point (~6 lines)
|
|
79
79
|
│ ├── vite.config.js # 3-line config
|
|
80
80
|
│ └── public/ # Static assets
|
|
81
81
|
│
|
|
82
|
-
└──
|
|
83
|
-
├──
|
|
84
|
-
│ ├──
|
|
85
|
-
│
|
|
86
|
-
│
|
|
87
|
-
│
|
|
88
|
-
|
|
89
|
-
│ └──
|
|
90
|
-
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@uniweb/runtime": "0.8.9"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@uniweb/build": "0.
|
|
49
|
+
"@uniweb/build": "0.13.0",
|
|
50
50
|
"@uniweb/content-reader": "1.1.9",
|
|
51
51
|
"@uniweb/semantic-parser": "1.1.15"
|
|
52
52
|
},
|
package/partials/agents.md
CHANGED
|
@@ -56,21 +56,23 @@ Most projects start as a workspace with two packages:
|
|
|
56
56
|
|
|
57
57
|
```
|
|
58
58
|
project/
|
|
59
|
-
├──
|
|
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
|
-
|
|
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 code — that'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
|
-
**
|
|
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
|
-
|
|
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`, `
|
|
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/
|
|
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 #
|
|
98
|
-
uniweb add foundation ui #
|
|
99
|
-
uniweb add
|
|
100
|
-
uniweb add
|
|
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 # Slash → folder 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
|
-
|
|
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 `
|
|
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`, `
|
|
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`, `
|
|
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
|
|
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
|
-
**`
|
|
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 `
|
|
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
|
-
/*
|
|
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 `
|
|
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
|
-
//
|
|
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 `
|
|
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
|
|
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
|
-
|
|
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`
|
|
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/*` | `./
|
|
1306
|
-
| `#utils/*` | `./
|
|
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 (`
|
|
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 `
|
|
1565
|
+
Layouts live in `layouts/` (inside the foundation package) and are auto-discovered:
|
|
1442
1566
|
|
|
1443
1567
|
```js
|
|
1444
|
-
//
|
|
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
|
-
//
|
|
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 `
|
|
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
|
-
//
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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/
|
|
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}/
|
|
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
|
|