uniweb 1.14.1 → 1.15.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "1.14.1",
3
+ "version": "1.15.0",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,15 +41,15 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/core": "~1.3.1",
45
- "@uniweb/schemas": "~1.5.1",
46
- "@uniweb/semantic-parser": "~1.12.2",
47
- "@uniweb/content-writer": "~1.3.0"
44
+ "@uniweb/content-writer": "~1.3.0",
45
+ "@uniweb/core": "~1.4.0",
46
+ "@uniweb/schemas": "~1.6.0",
47
+ "@uniweb/semantic-parser": "~1.12.2"
48
48
  },
49
49
  "peerDependencies": {
50
+ "@uniweb/build": "~1.7.0",
50
51
  "@uniweb/content-reader": "~1.5.4",
51
- "@uniweb/semantic-parser": "~1.12.2",
52
- "@uniweb/build": "~1.6.1"
52
+ "@uniweb/semantic-parser": "~1.12.2"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@uniweb/build": {
@@ -1064,7 +1064,7 @@ function MyComponent({ content, params, block }) {
1064
1064
  |---|---|---|
1065
1065
  | `type`, `id`, `hidden` | the section itself — its section type, its id, and a draft (`hidden: true`: left out of a published build, kept by `uniweb dev`) | — |
1066
1066
  | `query`, `fetch` | the section's data | `content.data`, under the keys its `meta.js` `data:` declares |
1067
- | `theme`, `background`, `grid`, `vars` | the section's settings, which framework applies — the color context and the background around your component, the section's theme and your component's CSS variables in the page stylesheet, the child layout | `block`, when the component needs one for its own logic: `useColorContext(block)` for the context (a light vs. dark logo, say), `<SectionBackground block={block} />` to draw the background itself, `<ChildGrid from={block} />` for the grid |
1067
+ | `theme`, `background`, `grid`, `vars`, `sticky` | the section's settings, which framework applies — the color context and the background around your component, the section's theme and your component's CSS variables in the page stylesheet, the child layout, and whether the section stays in view as the page scrolls | `block`, when the component needs one for its own logic: `useColorContext(block)` for the context (a light vs. dark logo, say), `<SectionBackground block={block} />` to draw the background itself, `<ChildGrid from={block} />` for the grid, `block.sticky` for whether it sticks |
1068
1068
  | `props` | params written as one map | `params` — its keys are merged in, one by one |
1069
1069
  | `data`, `preset`, `input` | nothing: `data:` is refused (it was `query:`'s old name); `preset:` and `input:` are ignored with a warning | — |
1070
1070
 
@@ -1137,7 +1137,7 @@ Four things you won't discover by reading exports:
1137
1137
 
1138
1138
  > **Kit hooks are SSR-safe by design.** If you hit "document is not defined" during a build, don't add `typeof document` guards — reach for the hook instead (`useAppearance()` for scheme, `useScrolled()` for scroll position).
1139
1139
 
1140
- > **A modal needs `<Overlay>`, not a bigger z-index.** The runtime gives each layout area its own `view-transition-name` so header, rails and body animate independently. That makes each area a stacking context *and* a containing block for `fixed` children — so a dialog rendered from inside your Header is sealed into the header's context and paints *under* the page body no matter what z-index it carries. Raising the number looks like it should work and never does. `<Overlay onClose={close}>…</Overlay>` renders into `document.body`, where the z-index means what you expect.
1140
+ > **A modal needs `<Overlay>`, not a bigger z-index.** The runtime gives each layout area its own `view-transition-name` so header, rails and body animate independently, and that makes each area a stacking context: a `fixed` dialog inside one is still placed against the viewport, but whatever z-index it carries, it paints at its area's place in the stack. Areas sit above the body, so a dialog opened from a section paints *under* the header and footer, and one opened from your Header paints under a footer or sidebar on the header's layer that comes after it. Raising the number looks like it should work and never does. `<Overlay onClose={close}>…</Overlay>` renders into `document.body`, where the z-index means what you expect.
1141
1141
  >
1142
1142
  > It also does the part that is easy to skip: a modal overlay **contains focus** — moves focus in, cycles Tab within, marks the rest of the page `inert`, and returns focus to whatever opened it. That is what makes the `role="dialog" aria-modal="true"` on your box true rather than merely asserted; without it a keyboard user Tabs straight out into a page a screen-reader user has been told is unreachable. Plus Escape, the scrim click, the scroll lock, and a dimmed scrim you override with `className` (`bg-transparent` removes it). `modal={false}` for a toast: it escapes the stacking context and nothing else, so the page stays usable. Your dialog's markup stays yours.
1143
1143
 
@@ -1173,6 +1173,8 @@ Header.className = 'p-0'
1173
1173
  Header.as = 'header'
1174
1174
  ```
1175
1175
 
1176
+ **A header that stays in view is a setting, not a class.** `sticky top-0` on your component's markup does nothing: a sticky element moves only within its parent, and the section's wrapper and the area's are as tall as your header. Declare `sticky: true` in the type's `meta.js` — an author turns it off with `sticky: false` — and the runtime makes sticky the box that can stay in view. A section in the body sticks the same way, and `sticky: { default: true, top: 'var(--header-height)' }` keeps one below the header. A layout of your own that wraps an area in an element makes that element sticky when `page.isSticky('header')`. `uniweb dev` names a sticky element that cannot move in the console.
1177
+
1176
1178
  ### meta.js
1177
1179
 
1178
1180
  ```javascript
@@ -1781,7 +1783,7 @@ Layouts are full components with their own `params` in `meta.js`, not just struc
1781
1783
 
1782
1784
  **Layout `meta.js`** declares areas and optional scroll behavior: `{ areas: ['header', 'footer', 'left'], scroll: 'self' }`. Area names are arbitrary. `areas` tells an editor what to offer; a page renders the areas its site's `layout/` folder fills. `scroll` controls scroll restoration: unset = runtime manages `window` (default), `'self'` = the layout scrolls itself, or a CSS selector (`'main'`) = runtime manages that element.
1783
1785
 
1784
- Two optional keys tune how areas behave across a navigation. `transitions` renames or opts regions out of per-area view transitions (`{ left: null }`, or `false` for the whole layout). `layers` sets which area paints on top — every area is stacked above the body by default, so a fixed header works without declaring anything, but areas are equal to each other, so a layout whose chrome overlaps says which wins: `layers: { header: 2, left: 1 }`. Both take an object to override per region, or `false` to opt out. **A `z-index` inside an area cannot lift it past another area** — each area is its own stacking context — so reach for `layers` rather than a bigger number, and for `<Overlay>` when a modal needs to escape the area entirely.
1786
+ Two optional keys tune how areas behave across a navigation. `transitions` renames or opts regions out of per-area view transitions (`{ left: null }`, or `false` for the whole layout). `layers` sets which area paints on top — every area is stacked above the body by default, so a fixed header works without declaring anything, and a sticky area (one with a `sticky` section) above every area that does not stick, since the page scrolls under it. Otherwise areas are equal to each other, so a layout whose chrome overlaps says which wins: `layers: { header: 2, left: 1 }`, which also ranks a sticky area it names. Both take an object to override per region, or `false` to opt out. **A `z-index` inside an area cannot lift it past another area** — each area is its own stacking context — so reach for `layers` rather than a bigger number, and for `<Overlay>` when a modal needs to escape the area entirely.
1785
1787
 
1786
1788
  **Layout content** lives in `site/layout/` — `header.md`, `footer.md` for the default layout, or a named subdirectory (`site/layout/marketing/`) for named layouts. Named subdirectories are self-contained — no inheritance. Cascade: `page.yml` → `folder.yml` → `site.yml` → foundation `defaultLayout` → `"default"`. The structure alone decides what an entry is: every folder directly under `layout/` is a named layout (its name matched regardless of case and of a trailing `Layout` — `site/layout/docs/` serves the foundation's `DocsLayout`), and an area with several sections is a folder inside a layout's folder — `site/layout/default/header/1-topbar.md` for the default layout. An area has no `page.yml`; its sections render in filename order.
1787
1789
 
@@ -3080,6 +3082,8 @@ Most Uniweb failures are **silent** — the build succeeds and the page is wrong
3080
3082
 
3081
3083
  **A var in frontmatter does nothing** — component vars only apply when declared in that section type's `meta.js` `vars:`. Unknown names are ignored silently.
3082
3084
 
3085
+ **A sticky header scrolls away** — it is `sticky top-0` on the component's own markup, which has no room to move. Make the section sticky instead: `sticky: true` in its type's `meta.js`. `uniweb dev` names the element in the console.
3086
+
3083
3087
  Loud failures:
3084
3088
 
3085
3089
  **"Could not load foundation"** — check that the site's `package.json` depends on the foundation *by its workspace package name*. For the default layout that's `"src": "file:../src"`; for a co-located project (`docs/src` + `docs/site`) it's `"docs-src": "file:../src"`. The key must match the foundation's `package.json::name`, not the folder it happens to sit in.
@@ -211,10 +211,10 @@ async function findSiteRoot(target) {
211
211
  /**
212
212
  * Load site configuration
213
213
  *
214
- * Resolves locales from config:
214
+ * Resolves the locales to translate into from config — every language but the default:
215
215
  * - undefined → all available locales (from locales/*.json)
216
216
  * - '*' → explicitly all available locales
217
- * - ['es', 'fr'] → only those specific locales
217
+ * - ['en', 'es', 'fr'] → those languages
218
218
  */
219
219
  async function loadSiteConfig(siteRoot) {
220
220
  const configPath = join(siteRoot, 'site.yml')
@@ -226,10 +226,15 @@ async function loadSiteConfig(siteRoot) {
226
226
 
227
227
  // Resolve locales (undefined/'*' → all available, array → specific)
228
228
  const { resolveLocales } = await import('@uniweb/build/i18n')
229
- const locales = await resolveLocales(config.languages, localesPath)
229
+ const defaultLocale = resolveDefaultLocale(config)
230
+ // ⛔ The default language is the source, never a translation. A declared
231
+ // `languages: [en, es]` lists it, and `status` and `audit` reported "en: No translation
232
+ // file — 75 strings need translation" until 2026-10-19 (the build skips it the same way,
233
+ // `buildLocalizedContent`).
234
+ const locales = (await resolveLocales(config.languages, localesPath)).filter((l) => l !== defaultLocale)
230
235
 
231
236
  return {
232
- defaultLocale: resolveDefaultLocale(config),
237
+ defaultLocale,
233
238
  locales,
234
239
  localesDir,
235
240
  ...config.i18n
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-10-19T21:44:22.528Z",
3
+ "generatedAt": "2026-10-20T03:11:00.010Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
- "version": "1.6.1"
6
+ "version": "1.6.2"
7
7
  },
8
8
  "@uniweb/build": {
9
- "version": "1.6.1"
9
+ "version": "1.7.0"
10
10
  },
11
11
  "@uniweb/content-reader": {
12
12
  "version": "1.5.4"
@@ -15,16 +15,16 @@
15
15
  "version": "1.3.0"
16
16
  },
17
17
  "@uniweb/core": {
18
- "version": "1.3.1"
18
+ "version": "1.4.0"
19
19
  },
20
20
  "@uniweb/frame-bridge": {
21
21
  "version": "1.0.2"
22
22
  },
23
23
  "@uniweb/icons": {
24
- "version": "1.0.4"
24
+ "version": "1.0.5"
25
25
  },
26
26
  "@uniweb/kit": {
27
- "version": "1.5.2"
27
+ "version": "1.5.3"
28
28
  },
29
29
  "@uniweb/loom": {
30
30
  "version": "1.1.0"
@@ -33,16 +33,16 @@
33
33
  "version": "1.1.3"
34
34
  },
35
35
  "@uniweb/projections": {
36
- "version": "1.1.4"
36
+ "version": "1.1.6"
37
37
  },
38
38
  "@uniweb/runtime": {
39
- "version": "1.1.11"
39
+ "version": "1.1.13"
40
40
  },
41
41
  "@uniweb/scene": {
42
42
  "version": "1.0.1"
43
43
  },
44
44
  "@uniweb/schemas": {
45
- "version": "1.5.1"
45
+ "version": "1.6.0"
46
46
  },
47
47
  "@uniweb/scholar": {
48
48
  "version": "1.1.0"
@@ -54,13 +54,13 @@
54
54
  "version": "1.1.0"
55
55
  },
56
56
  "@uniweb/templates": {
57
- "version": "1.1.3"
57
+ "version": "1.1.4"
58
58
  },
59
59
  "@uniweb/theming": {
60
60
  "version": "1.2.0"
61
61
  },
62
62
  "@uniweb/unipress": {
63
- "version": "1.1.10"
63
+ "version": "1.1.11"
64
64
  }
65
65
  },
66
66
  "templates": {