uniweb 0.8.28 → 0.8.30

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.
Files changed (2) hide show
  1. package/package.json +5 -5
  2. package/partials/agents.md +86 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.8.28",
3
+ "version": "0.8.30",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,12 +41,12 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/core": "0.5.17",
45
- "@uniweb/kit": "0.7.18",
46
- "@uniweb/runtime": "0.6.23"
44
+ "@uniweb/core": "0.5.18",
45
+ "@uniweb/runtime": "0.6.25",
46
+ "@uniweb/kit": "0.7.19"
47
47
  },
48
48
  "peerDependencies": {
49
- "@uniweb/build": "0.8.27",
49
+ "@uniweb/build": "0.8.29",
50
50
  "@uniweb/content-reader": "1.1.4",
51
51
  "@uniweb/semantic-parser": "1.1.8"
52
52
  },
@@ -61,9 +61,11 @@ project/
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 an a `meta.js` with metadata in them. Everything in `src/components` (or elsewhere) is ordinary React.
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
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.
66
66
 
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.
68
+
67
69
  > Multi-site projects use sub-folders with site/foundation pairs in them, or segregate foundations and sites into separate folders (`foundations/`, `sites/`).
68
70
 
69
71
  ## Project Setup
@@ -553,7 +555,6 @@ description: Learn about our company
553
555
  id: about # Stable identity (for page: links, survives moves)
554
556
  order: 2 # Navigation sort position
555
557
  pages: [team, history, ...] # Child page order (... = rest). Without ... = strict (hides unlisted)
556
- index: getting-started # Which child page is the index
557
558
  redirect: academic # Redirect to child page (relative or absolute path, or URL)
558
559
  ```
559
560
 
@@ -564,6 +565,10 @@ pages: [home, about, ...] # Order pages (... = rest, first = homepage)
564
565
  pages: [home, about] # Strict: only listed pages in nav
565
566
  ```
566
567
 
568
+ **Route mapping:** Folder structure maps 1:1 to routes. Every folder keeps its natural route — `pages:` controls **order only**, not which child "becomes" the parent. The only exception is the site root: `index:` (or first in `pages:`) in site.yml sets the homepage at `/`.
569
+
570
+ **Content-less containers:** Folders with `page.yml` but no markdown are structural groups. They appear in `getPageHierarchy()` with `hasContent: false` and their own title/label. When visited directly, the runtime auto-redirects to the first descendant with content. This supports hierarchical navigation (courses → modules → lessons) at any depth.
571
+
567
572
  ### Lists as Navigation Menus
568
573
 
569
574
  Markdown lists model nav, menus, and grouped links. Each list item is a full content object with `paragraphs`, `links`, `icons`, and nested `lists`.
@@ -708,7 +713,7 @@ inline:
708
713
  font-weight: '600'
709
714
 
710
715
  vars:
711
- header-height: 5rem
716
+ radius: 0.75rem
712
717
  ```
713
718
 
714
719
  Each color generates 11 OKLCH shades (50–950). `neutral` uses a named preset rather than hex. Shade 500 = your exact input color. Context override keys match token names: `section:` not `bg:`, `primary:` not `btn-primary-bg:`.
@@ -734,19 +739,35 @@ contexts:
734
739
 
735
740
  ### Foundation variables
736
741
 
737
- Foundations declare customizable layout values in `foundation.js`:
742
+ Most customization is handled by component params. Both section components and layout components declare their own params in `meta.js` — layouts are full components with params, not just structural wrappers. A header height, for example, is typically a layout param, not a foundation var.
743
+
744
+ Foundation-level CSS variables are for values that must stay consistent **across** multiple components — shared radii, spacing scales, or additional font roles beyond the three the theming system already provides (body, heading, mono). Don't reach for foundation vars when a component or layout param would do.
745
+
746
+ If you need them, declare vars in two places:
747
+
748
+ **`foundation.js`** — metadata for the editor and schema:
738
749
 
739
750
  ```js
740
751
  export const vars = {
741
- 'header-height': { default: '4rem', description: 'Fixed header height' },
742
- 'max-content-width': { default: '80rem', description: 'Maximum content width' },
752
+ 'radius': { default: '0.5rem', description: 'Default border radius for cards and buttons' },
753
+ 'radius-lg': { default: '1rem', description: 'Large border radius' },
743
754
  'section-padding-y': { default: 'clamp(4rem, 6vw, 7rem)', description: 'Vertical section padding' },
744
755
  }
745
756
  ```
746
757
 
747
- Sites override in `theme.yml` under `vars:`. Components use: `py-[var(--section-padding-y)]`, `h-[var(--header-height)]`.
758
+ **`styles.css`** the actual CSS that ships with the foundation:
748
759
 
749
- Tailwind v4 also supports a shorthand: `py-(--section-padding-y)`. This requires registering the variable in `styles.css` via `@theme inline { --section-padding-y: <default>; }`. Use the shorthand for vars referenced across many components; otherwise the bracket syntax works without registration.
760
+ ```css
761
+ @theme inline {
762
+ --radius: 0.5rem;
763
+ --radius-lg: 1rem;
764
+ --section-padding-y: clamp(4rem, 6vw, 7rem);
765
+ }
766
+ ```
767
+
768
+ 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.
769
+
770
+ **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.
750
771
 
751
772
  ### Design richness beyond tokens
752
773
 
@@ -1083,6 +1104,33 @@ export default function Hero({ content, block, params }) {
1083
1104
 
1084
1105
  This is the system-building pattern at its clearest: **section types are the public interface** to your content system (author-friendly names, documented in `meta.js`). **Helper components are the implementation** (developer-friendly APIs, ordinary React props). The section type is the thin translation layer that connects the two worlds.
1085
1106
 
1107
+ ### Section components are composites
1108
+
1109
+ A section component is rarely a single flat render. It imports helper components from `src/components/` to build a complex UI while presenting a single `type:` to the content author. The `src/components/` directory is the developer's workbench — ordinary React components with ordinary props, not selectable by authors, not auto-discovered.
1110
+
1111
+ ```jsx
1112
+ // src/sections/Lesson/index.jsx
1113
+ import LessonHeader from '../../components/LessonHeader'
1114
+ import LessonContent from '../../components/LessonContent'
1115
+ import LessonNav from '../../components/LessonNav'
1116
+
1117
+ export default function Lesson({ content, params, block }) {
1118
+ return (
1119
+ <>
1120
+ <LessonHeader block={block} />
1121
+ <LessonContent content={content} params={params} />
1122
+ <LessonNav block={block} />
1123
+ </>
1124
+ )
1125
+ }
1126
+ ```
1127
+
1128
+ The content author writes `type: Lesson` in one markdown file. The section component handles the structural chrome — a header derived from the page hierarchy, the rendered content, a prev/next footer. The three helpers live in `src/components/` and receive whatever props make sense for their job.
1129
+
1130
+ **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.
1131
+
1132
+ **Common mistake:** Solving structural repetition at the layout level. If only some page types need a content header (lessons do, the homepage doesn't), it's a section concern, not a layout concern. The layout owns the page-wide chrome (header area, sidebar area). The section owns its own internal structure.
1133
+
1086
1134
  ### Foundation Organization
1087
1135
 
1088
1136
  ```
@@ -1111,9 +1159,26 @@ foundation/src/
1111
1159
  const { website } = useWebsite()
1112
1160
  const page = website.activePage
1113
1161
 
1114
- // Navigation
1115
- website.getPageHierarchy({ for: 'header' })
1116
- // → [{ route, navigableRoute, label, hasContent, children }]
1162
+ // Navigation — getPageHierarchy(options)
1163
+ // Returns [{ id, route, navigableRoute, translatedRoute, title, label, description, hasContent, version, children }]
1164
+ //
1165
+ // Options:
1166
+ // for: 'header' | 'footer' — filter by nav type (respects hideInHeader/hideInFooter)
1167
+ // nested: true (default) — nested hierarchy with children; false = flat list
1168
+ // includeHidden: false — include hidden pages
1169
+ // filter: (page) => bool — custom filter function
1170
+ // sort: (a, b) => number — custom sort function
1171
+ //
1172
+ // Convenience methods:
1173
+ // website.getHeaderPages() — same as getPageHierarchy({ for: 'header' })
1174
+ // website.getFooterPages() — same as getPageHierarchy({ for: 'footer' })
1175
+ // website.getAllPages() — flat list: getPageHierarchy({ nested: false })
1176
+ //
1177
+ // Common patterns:
1178
+ website.getPageHierarchy({ for: 'header' }) // Header nav (excludes hideInHeader pages)
1179
+ website.getPageHierarchy() // Full nested hierarchy (no nav filtering)
1180
+ website.getPageHierarchy({ nested: false }) // Flat list of all visible pages
1181
+ website.getPageHierarchy({ nested: false, includeHidden: true }) // Everything including hidden
1117
1182
 
1118
1183
  // Core properties
1119
1184
  website.name // Site name from site.yml
@@ -1132,9 +1197,17 @@ const { isActive, isActiveOrAncestor } = useActiveRoute()
1132
1197
  const { scheme, toggle, canToggle } = useAppearance()
1133
1198
 
1134
1199
  // Page properties
1135
- page.title, page.label, page.route
1200
+ page.title, page.label, page.route, page.description
1136
1201
  page.isHidden(), page.showInHeader(), page.showInFooter()
1137
- page.hasChildren(), page.children
1202
+ page.hasContent() // True if page has its own content (not just a folder)
1203
+ page.hasChildren(), page.children // Direct child Page instances
1204
+ page.parent // Parent Page instance (null for root pages)
1205
+ page.getNavigableRoute() // First descendant route with content (for linking)
1206
+
1207
+ // Hierarchical navigation — content-less containers are group nodes:
1208
+ // { route: '/courses/intro', title: 'Introduction', hasContent: false,
1209
+ // navigableRoute: '/courses/intro/lesson-1', children: [...] }
1210
+ // Use navigableRoute for links, title for display, hasContent to style differently.
1138
1211
  ```
1139
1212
 
1140
1213
  ### Cross-Block Communication