uniweb 0.12.57 → 0.12.59

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": "0.12.57",
3
+ "version": "0.12.59",
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.23",
45
- "@uniweb/kit": "0.9.30",
46
- "@uniweb/runtime": "0.8.29"
44
+ "@uniweb/kit": "0.9.32",
45
+ "@uniweb/runtime": "0.8.31",
46
+ "@uniweb/core": "0.7.25"
47
47
  },
48
48
  "peerDependencies": {
49
- "@uniweb/build": "0.14.37",
50
49
  "@uniweb/content-reader": "1.1.14",
51
- "@uniweb/semantic-parser": "1.1.18"
50
+ "@uniweb/semantic-parser": "1.1.18",
51
+ "@uniweb/build": "0.14.39"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "@uniweb/build": {
@@ -217,7 +217,7 @@ The `uniweb` block in `package.json` carries platform-specific configuration tha
217
217
  | `namespace` | `uniweb register` | (none — see scope resolution) | Legacy explicit org-namespace override. Equivalent to using a scoped `package.json::name` (`"@myorg/foundation"`). Rarely needed in modern foundations. |
218
218
  | `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. |
219
219
 
220
- **How a foundation reaches the catalog.** Foundations on Uniweb hosting always live in the catalog as `@org/name@version`. Two ways to get one there, both from the same `dist/foundation.js` artifact:
220
+ **How a foundation reaches the catalog.** Foundations on Uniweb hosting always live in the catalog as `@org/name@version`. Two ways to get one there, both from the same `dist/entry.js` artifact:
221
221
 
222
222
  - **Brought along by `uniweb publish`.** When a foundation powers a single site, don't run `uniweb register` yourself. Run `uniweb publish` from the site directory — it releases the site's local foundation to the catalog under your `@org` (when its code changed) and then goes live, in one step. No separate register ceremony.
223
223
  - **Registered deliberately with `uniweb register --scope @org`.** When the foundation is a product meant for multiple sites — listed in the catalog, consumable by other developers' sites — register it on its own schedule. Consuming sites then pin a versioned ref (`foundation: '@org/name@1.2.3'`).
@@ -553,7 +553,7 @@ Check out [this](/a) link. ← inline → stays in paragraphs as <a> tag
553
553
  This is [less important]{muted} context.
554
554
  ```
555
555
 
556
- `accent` (link-colored + bold), `callout` (accent-colored + bold), and `muted` (subtle) are built-in defaults that adapt to context automatically. Components receive HTML strings with spans applied: `<span accent="true">faster</span>`.
556
+ `accent` and `callout` (both accent-colored + bold) and `muted` (subtle) are built-in defaults that adapt to context automatically. `--accent` is your brand color unless you declare a separate `colors.accent`, and resolves to the shade you authored — not a darkened one, since accent is decorative emphasis rather than body-size link text. Components receive HTML strings with spans applied: `<span accent="true">faster</span>`.
557
557
 
558
558
  Sites can override these or define additional named styles in `theme.yml`'s `inline:` section.
559
559
 
@@ -978,6 +978,10 @@ contexts:
978
978
  light:
979
979
  section: '#fafaf9' # Override individual tokens per context
980
980
 
981
+ appearance:
982
+ default: light # 'light' | 'dark' | 'system'; see Light/dark appearance below
983
+ allowToggle: true # offer a visitor light/dark switch
984
+
981
985
  fonts:
982
986
  import:
983
987
  - url: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'
@@ -1017,6 +1021,60 @@ contexts:
1017
1021
 
1018
1022
  > **Contrast warning:** Bright brand colors (orange, yellow, light green) at shade 500 may not meet WCAG contrast (4.5:1) with white foreground text. Test buttons for readability — if contrast is insufficient, keep the default shade 600 mapping.
1019
1023
 
1024
+ ### Light/dark appearance (scheme vs. context)
1025
+
1026
+ Uniweb splits what other frameworks fuse into one "dark mode" into two independent ideas:
1027
+
1028
+ - **Section context** — the `theme:` field on a section (`light` / `medium` / `dark`). It sets the color tokens for *that one section*, so a single page can move through light, dark, and colored sections. Per-section, author-controlled, covered above.
1029
+ - **Site scheme** — the global light/dark preference, configured under `appearance:` in `theme.yml`. This is the site-wide toggle a visitor flips (or that follows their OS).
1030
+
1031
+ They compose rather than fight: a section with **no `theme:`** inherits the site scheme, so it follows the toggle; a section that **pins** `theme: dark` stays dark in either scheme. A dark-scheme site can still carry one bright white CTA; a light-scheme site can carry one dramatic dark hero. You never write `isDark ? … : …` — components built on semantic tokens (`text-heading`, `bg-card`, `border`, …) adapt to whichever scheme and context resolve around them.
1032
+
1033
+ Configure the scheme in `theme.yml`:
1034
+
1035
+ ```yaml
1036
+ appearance:
1037
+ default: light # 'light' | 'dark' | 'system' (start by following the OS)
1038
+ allowToggle: true # offer a visitor switch (also generates the dark tokens)
1039
+ respectSystemPreference: true # first visit follows the OS; set false to pin `default`
1040
+ schemes: [light, dark] # optional — declares the available set
1041
+ ```
1042
+
1043
+ Shorthand strings work too: `appearance: light` / `appearance: dark` (fixed, no toggle), `appearance: system` (follow the OS).
1044
+
1045
+ A site **has dark mode** whenever it offers a toggle, defaults to `dark`/`system`, or lists `dark` in `schemes:`. Any such site follows the visitor's OS on first visit (unless `respectSystemPreference: false`) and remembers a manual choice. Precedence: a stored choice wins over the OS, which wins over `default:`.
1046
+
1047
+ Common intents:
1048
+
1049
+ | Goal | `theme.yml` |
1050
+ |---|---|
1051
+ | Always light, no switch | `appearance: light` |
1052
+ | Always dark, no switch | `appearance: dark` |
1053
+ | Toggle, follow the OS on first visit | `appearance: { default: system, allowToggle: true }` |
1054
+ | Toggle, but always start light | `appearance: { default: light, allowToggle: true, respectSystemPreference: false }` |
1055
+
1056
+ Note that `default: light` is a *fallback*, not a guarantee: a dark OS still wins on first visit unless you set `respectSystemPreference: false` (the last row). `default: system` is just the honest way to say "there is no fixed default — use the OS."
1057
+
1058
+ **Rendering a toggle (foundation side).** The runtime resolves the scheme and applies it to `<html>` (`scheme-dark` / `scheme-light`) *before the page paints* — you never touch `localStorage` or `document` yourself, and there's no flash of the wrong scheme. A section type only renders the button:
1059
+
1060
+ ```jsx
1061
+ import { useAppearance } from '@uniweb/kit'
1062
+
1063
+ function SchemeToggle() {
1064
+ const { scheme, toggle, canToggle } = useAppearance()
1065
+ if (!canToggle) return null // hidden unless the site enables toggling
1066
+ return <button onClick={toggle}>{scheme === 'dark' ? 'Light' : 'Dark'}</button>
1067
+ }
1068
+ ```
1069
+
1070
+ **Tailwind `dark:` variant.** If your foundation uses Tailwind's `dark:` utilities, bind them to the site scheme (not the OS media query) so they track the toggle. In `styles.css`:
1071
+
1072
+ ```css
1073
+ @custom-variant dark (&:where(.scheme-dark, .scheme-dark *));
1074
+ ```
1075
+
1076
+ Without this, `dark:` defaults to `@media (prefers-color-scheme: dark)` and ignores the site's own light/dark setting. Prefer semantic tokens over `dark:` where you can — they adapt to per-section context, which a global `dark:` cannot.
1077
+
1020
1078
  ### Fonts
1021
1079
 
1022
1080
  Font families are a **site** setting, configured in `theme.yml` under `fonts:`. A foundation never installs a font package (no `@fontsource/*`) and never hardcodes a family in its code — if you reach for either, you're off the paved path. The framework provides three roles and wires each onto the standard elements for you:
@@ -1342,6 +1400,8 @@ import { Visual } from '@uniweb/kit'
1342
1400
 
1343
1401
  **Utilities:** `cn()` (Tailwind class merge — `cn('px-4', condition && 'bg-primary')` resolves conflicts), `Link`, `Image`, `Asset`, `SafeHtml`, `SocialIcon`, `filterSocialLinks(links)`, `getSocialPlatform(url)`
1344
1402
 
1403
+ > **`cn()` gotcha — a later `text-<size>` silently drops an earlier `leading-*`.** Tailwind's size classes set line height too, so `cn()` treats the size as replacing the leading: `cn('leading-[1.1] text-4xl')` → `text-4xl`. Put the size first, or fold the leading into it (`text-[clamp(2rem,5vw,4rem)]/[1.1]`). Most likely to bite when the size comes from a lookup and the leading sits in a shared base string.
1404
+
1345
1405
  **Other styled:** `SidebarLayout`, `Prose`, `Article`, `Code`, `Alert`, `Table`, `Details`, `Divider`, `Disclaimer`
1346
1406
 
1347
1407
  ### Hook Signatures
@@ -1356,7 +1416,7 @@ useThemeData() → Theme // programmatic color
1356
1416
  useColorContext(block) → 'light' | 'medium' | 'dark' // current section context
1357
1417
  ```
1358
1418
 
1359
- `isActive` and `isActiveOrAncestor` accept a Page object or a route string. `useAppearance` reads `appearance:` from `theme.yml` — `scheme` is `'light'`|`'dark'`, `canToggle` reflects `allowToggle` config. Stores preference in localStorage, respects system preference.
1419
+ `isActive` and `isActiveOrAncestor` accept a Page object or a route string. `useAppearance` reflects the site scheme from `appearance:` in `theme.yml` — `scheme` is `'light'`|`'dark'`, `canToggle` reflects `allowToggle`. The runtime applies the scheme to `<html>` before paint; the hook reads it, `toggle`/`setScheme` flip it and persist the choice to localStorage. First visit follows the OS when the site has dark mode. Full model: *Light/dark appearance* above.
1360
1420
 
1361
1421
  ### Icon Component
1362
1422
 
@@ -1624,7 +1684,7 @@ import LessonHeader from '../../components/LessonHeader'
1624
1684
 
1625
1685
  Within the same directory (e.g., one component importing a sibling), use normal relative imports (`./AIFeedbackCard`).
1626
1686
 
1627
- **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.
1687
+ **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/entry.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.
1628
1688
 
1629
1689
  ### Website and Page APIs
1630
1690
 
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-07-23T17:37:49.259Z",
3
+ "generatedAt": "2026-07-24T20:43:27.316Z",
4
4
  "packages": {
5
5
  "@uniweb/build": {
6
- "version": "0.14.37",
6
+ "version": "0.14.39",
7
7
  "path": "framework/build",
8
8
  "deps": [
9
9
  "@uniweb/content-reader",
@@ -25,7 +25,7 @@
25
25
  "deps": []
26
26
  },
27
27
  "@uniweb/core": {
28
- "version": "0.7.23",
28
+ "version": "0.7.25",
29
29
  "path": "framework/core",
30
30
  "deps": [
31
31
  "@uniweb/semantic-parser",
@@ -43,7 +43,7 @@
43
43
  "deps": []
44
44
  },
45
45
  "@uniweb/kit": {
46
- "version": "0.9.30",
46
+ "version": "0.9.32",
47
47
  "path": "framework/kit",
48
48
  "deps": [
49
49
  "@uniweb/core",
@@ -61,7 +61,7 @@
61
61
  "deps": []
62
62
  },
63
63
  "@uniweb/runtime": {
64
- "version": "0.8.29",
64
+ "version": "0.8.31",
65
65
  "path": "framework/runtime",
66
66
  "deps": [
67
67
  "@uniweb/core",
@@ -94,12 +94,12 @@
94
94
  "deps": []
95
95
  },
96
96
  "@uniweb/theming": {
97
- "version": "0.1.9",
97
+ "version": "0.1.11",
98
98
  "path": "framework/theming",
99
99
  "deps": []
100
100
  },
101
101
  "@uniweb/unipress": {
102
- "version": "0.4.44",
102
+ "version": "0.4.46",
103
103
  "path": "framework/unipress",
104
104
  "deps": [
105
105
  "@uniweb/build",
@@ -9,7 +9,7 @@
9
9
  colors:
10
10
  primary: '#3b82f6' # Brand color — buttons, links, focus rings
11
11
  # secondary: '#64748b' # Secondary actions
12
- # accent: '#8b5cf6' # Highlights, decorative elements
12
+ # accent: '#8b5cf6' # Highlights, decorative elements (defaults to primary)
13
13
  # neutral: stone # Gray family — stone, zinc, gray, slate, neutral
14
14
 
15
15
  # ─── Fonts ─────────────────────────────────────────────────────────────────────