uniweb 0.8.7 → 0.8.8

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.8.7",
3
+ "version": "0.8.8",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,9 +41,9 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/build": "0.8.6",
45
- "@uniweb/runtime": "0.6.5",
46
- "@uniweb/kit": "0.7.4",
47
- "@uniweb/core": "0.5.5"
44
+ "@uniweb/build": "0.8.7",
45
+ "@uniweb/kit": "0.7.5",
46
+ "@uniweb/core": "0.5.6",
47
+ "@uniweb/runtime": "0.6.6"
48
48
  }
49
49
  }
@@ -1,5 +1,7 @@
1
1
  # AGENTS.md
2
2
 
3
+ > A comprehensive guide to building with Uniweb — for developers and AI assistants alike.
4
+
3
5
  Uniweb is a Component Content Architecture (CCA). Content lives in markdown, code lives in React components, and a runtime connects them. The runtime handles section wrapping, background rendering, context theming, and token resolution — components receive pre-parsed content and render it with semantic tokens. Understanding what the runtime does (and therefore what components should *not* do) is the key to working effectively in this architecture.
4
6
 
5
7
  ## Documentation
@@ -135,9 +137,9 @@ The semantic parser extracts markdown into a flat, guaranteed structure. No null
135
137
 
136
138
  ```js
137
139
  content = {
138
- title: '', // Main heading
140
+ title: '', // Main heading (string or string[] for multi-line)
139
141
  pretitle: '', // Heading before main title (auto-detected)
140
- subtitle: '', // Heading after title
142
+ subtitle: '', // Heading after title (string or string[] for multi-line)
141
143
  subtitle2: '', // Third-level heading
142
144
  paragraphs: [], // Text blocks
143
145
  links: [], // { href, label, role } — standalone links become buttons
@@ -194,6 +196,55 @@ Enterprise-grade security. │ content.items[1].paragraphs[0] = "Enterprise
194
196
 
195
197
  Headings before the main title become `pretitle`. Headings after the main title at a lower importance become `subtitle`. Headings that appear after body content (paragraphs, links, images) start the `items` array.
196
198
 
199
+ ### Multi-Line Headings
200
+
201
+ Consecutive headings at the same level merge into a title array — a single heading split across visual lines:
202
+
203
+ ```markdown
204
+ # Build the future │ content.title = ["Build the future", "with confidence"]
205
+ # with confidence │
206
+ ```
207
+
208
+ Kit's `<H1>`, `<H2>`, etc. render arrays as a single tag with line breaks. This is how you create dramatic multi-line hero headlines.
209
+
210
+ **Works with accent styling:**
211
+
212
+ ```markdown
213
+ # Build the future │ content.title = [
214
+ # [with confidence]{accent} │ "Build the future",
215
+ │ "<span accent=\"true\">with confidence</span>"
216
+ │ ]
217
+ ```
218
+
219
+ **Works at any heading slot** — title, subtitle, items:
220
+
221
+ ```markdown
222
+ ### Our Mission │ content.pretitle = "Our Mission"
223
+ # Build the future │ content.title = ["Build the future",
224
+ # with confidence │ "with confidence"]
225
+ ## The platform for │ content.subtitle = ["The platform for",
226
+ ## modern teams │ "modern teams"]
227
+ ```
228
+
229
+ **Rule:** Same-level continuation only applies before going deeper. Once a subtitle level is reached, same-level headings start new items instead of merging:
230
+
231
+ ```markdown
232
+ # Features │ title = "Features"
233
+
234
+ We built this for you. │ paragraph
235
+
236
+ ### Fast │ items[0].title = "Fast"
237
+ ### Secure │ items[1].title = "Secure" ← new item, not merged
238
+ ```
239
+
240
+ Use `---` to force separate items when same-level headings would otherwise merge:
241
+
242
+ ```markdown
243
+ # Line one │ title = "Line one"
244
+ --- │ ← divider forces split
245
+ # Line two │ items[0].title = "Line two"
246
+ ```
247
+
197
248
  **Lists** contain bullet or ordered list items. Each list item is an object with the same content shape — not a plain string:
198
249
 
199
250
  ```markdown
@@ -280,6 +331,49 @@ Links mixed with non-link text stay as inline `<a>` tags within `content.paragra
280
331
  Check out [this](/a) and [that](/b). ← inline links in paragraph text, NOT in content.links[]
281
332
  ```
282
333
 
334
+ ### Inline Text Styling
335
+
336
+ Style specific words or phrases using bracketed spans with boolean attributes:
337
+
338
+ ```markdown
339
+ # Build [faster]{accent} with structure
340
+
341
+ This is [less important]{muted} context.
342
+ ```
343
+
344
+ The framework provides two defaults: `accent` (colored + bold) and `muted` (subtle). These adapt to context automatically — in dark sections, `accent` resolves to a lighter shade.
345
+
346
+ **What you write → what components receive:**
347
+
348
+ | Markdown | HTML in content string |
349
+ |----------|----------------------|
350
+ | `[text]{accent}` | `<span accent="true">text</span>` |
351
+ | `[text]{muted}` | `<span muted="true">text</span>` |
352
+ | `[text]{color=red}` | `<span style="color: red">text</span>` |
353
+
354
+ CSS is generated from `theme.yml`'s `inline:` section using attribute selectors (`span[accent] { ... }`). Sites can define additional named styles:
355
+
356
+ ```yaml
357
+ inline:
358
+ accent:
359
+ color: var(--link)
360
+ font-weight: '600'
361
+ callout:
362
+ color: var(--accent-600)
363
+ font-style: italic
364
+ ```
365
+
366
+ **Common pattern — accented multi-line hero heading:**
367
+
368
+ ```markdown
369
+ # Build the future
370
+ # [with confidence]{accent}
371
+ ```
372
+
373
+ This produces `content.title = ["Build the future", "<span accent=\"true\">with confidence</span>"]` — an array rendered as a single `<h1>` with visual line breaks. See [Multi-Line Headings](#multi-line-headings) for details.
374
+
375
+ Components receive HTML strings with the spans already applied. Kit's `<H1>`, `<P>`, etc. render them correctly via `dangerouslySetInnerHTML`.
376
+
283
377
  ### Structured Data
284
378
 
285
379
  Tagged code blocks pass structured data via `content.data`:
@@ -621,7 +715,7 @@ fonts:
621
715
  body: "'Inter', system-ui, sans-serif"
622
716
 
623
717
  inline:
624
- emphasis: # For [text]{emphasis} in markdown
718
+ accent: # For [text]{accent} in markdown
625
719
  color: var(--link)
626
720
  font-weight: '600'
627
721
 
@@ -834,7 +928,7 @@ export default {
834
928
  title: 'Feature Grid',
835
929
  description: 'Grid of feature cards with icons',
836
930
  category: 'marketing',
837
- // hidden: true, // Hide from content authors
931
+ // hidden: true, // Exclude from export (internal/helper component)
838
932
  // background: 'self', // Component renders its own background
839
933
  // inset: true, // Available for @ComponentName references in markdown
840
934
  // visuals: 1, // Expects 1 visual (image, video, or inset)
@@ -872,11 +966,13 @@ Content fields (`title`, `pretitle`, `paragraphs[]`, list item text) are **HTML
872
966
  **Rendering text** (`@uniweb/kit`):
873
967
 
874
968
  ```jsx
875
- import { H2, P, Span } from '@uniweb/kit'
969
+ import { H1, H2, P, Span } from '@uniweb/kit'
876
970
 
877
- <H2 text={content.title} className="text-heading text-3xl font-bold" />
878
- <P text={content.paragraphs[0]} className="text-body" />
879
- <P text={content.paragraphs} /> // array each string becomes its own <p>
971
+ <H1 text={content.title} className="text-heading text-5xl font-bold" />
972
+ // string → single <h1>, array → single <h1> with line breaks (multi-line headings)
973
+ <H2 text={content.subtitle} className="text-heading text-3xl font-bold" />
974
+ <P text={content.paragraphs} className="text-body" />
975
+ // array → each string becomes its own <p>
880
976
  <Span text={listItem.paragraphs[0]} className="text-subtle" />
881
977
  ```
882
978
 
@@ -956,7 +1052,8 @@ Only folders with `meta.js` in `sections/` (or `components/` for older foundatio
956
1052
  ### Website and Page APIs
957
1053
 
958
1054
  ```jsx
959
- const { website } = useWebsite()
1055
+ const { website } = useWebsite() // or block.website
1056
+ const page = website.activePage // or block.page
960
1057
 
961
1058
  // Navigation
962
1059
  const pages = website.getPageHierarchy({ for: 'header' }) // or 'footer'
@@ -1019,17 +1116,18 @@ function SplitContent({ content, block }) {
1019
1116
 
1020
1117
  **SSG and hooks:** Inset components that use React hooks (useState, useEffect) will trigger prerender warnings during `pnpm build`. This is expected — the SSG pipeline cannot render hooks due to dual React instances in the build. The warnings are informational; the page renders correctly client-side. If you see `"Skipped SSG for /..."` or `"Invalid hook call"`, this is the cause.
1021
1118
 
1022
- Inset components declare `inset: true` in meta.js. Use `hidden: true` for inset-only components:
1119
+ Inset components declare `inset: true` in meta.js:
1023
1120
 
1024
1121
  ```js
1025
1122
  // sections/insets/NetworkDiagram/meta.js
1026
1123
  export default {
1027
1124
  inset: true,
1028
- hidden: true,
1029
1125
  params: { variant: { type: 'select', options: ['full', 'compact'], default: 'full' } },
1030
1126
  }
1031
1127
  ```
1032
1128
 
1129
+ Whether an inset appears in a section palette is a concern of the parent component (via `children` and `insets` in its meta.js), not a property of the inset itself. Don't use `hidden: true` on insets — `hidden` means "don't export this component at all" (internal helpers, not-yet-ready components).
1130
+
1033
1131
  ### Dispatcher Pattern
1034
1132
 
1035
1133
  One section type with a `variant` param replaces multiple near-duplicates. Instead of `HeroLeft`, `HeroCentered`, `HeroSplit` — one `Hero` with `variant: left | centered | split`:
@@ -1208,7 +1306,7 @@ Semantic color tokens (`text-heading`, `bg-section`, `bg-primary`, etc.) come fr
1208
1306
 
1209
1307
  **"Could not load foundation"** — Check `site/package.json` has `"foundation": "file:../foundation"` (or `"default": "file:../../foundations/default"` for multi-site).
1210
1308
 
1211
- **Component not appearing** — Verify `meta.js` exists and doesn't have `hidden: true`. Rebuild: `cd foundation && pnpm build`.
1309
+ **Component not appearing** — Verify `meta.js` exists. Check for `hidden: true` (means component is excluded from export — only use for internal helpers). Rebuild: `cd foundation && pnpm build`.
1212
1310
 
1213
1311
  **Styles not applying** — Verify `@source` in `styles.css` includes your component paths. Check custom colors match `@theme` definitions.
1214
1312
 
@@ -1,8 +1,10 @@
1
- ## AI Assistance
1
+ ## Developer Guide
2
2
 
3
- This project includes an [AGENTS.md](./AGENTS.md) file with detailed instructions for AI coding assistants (Claude Code, Cursor, Copilot, etc.).
3
+ [AGENTS.md](./AGENTS.md) is a comprehensive guide to building with Uniweb content authoring, component development, theming, configuration, and the kit API. Despite the name, it's written for developers and AI assistants alike. Read it to get productive fast.
4
4
 
5
- ### Example Prompts
5
+ ### AI Prompts
6
+
7
+ AI coding assistants (Claude Code, Cursor, Copilot, etc.) read AGENTS.md automatically. Some prompts to try:
6
8
 
7
9
  **Converting an existing design:**
8
10
  ```
@@ -1,5 +1,6 @@
1
1
  ## Learn More
2
2
 
3
- - [Uniweb Documentation](https://github.com/uniweb/cli)
4
- - [@uniweb/kit Components](https://www.npmjs.com/package/@uniweb/kit)
3
+ - [AGENTS.md](./AGENTS.md) — comprehensive guide to building with Uniweb
4
+ - [Uniweb Documentation](https://github.com/uniweb/docs) — full reference docs
5
+ - [Uniweb CLI](https://github.com/uniweb/cli) — project scaffolding and build tools
5
6
  - [Tailwind CSS v4](https://tailwindcss.com)
@@ -1,37 +1,25 @@
1
- import React from 'react'
2
1
  import { H1, H2, P, Link, cn } from '@uniweb/kit'
3
2
 
4
3
  /**
5
4
  * Section Component
6
5
  *
7
6
  * A versatile content section that handles headings, text, and links.
8
- * This is the default component for rendering markdown content.
7
+ * Uses semantic tokens so it adapts to any theme context automatically.
9
8
  */
10
- function Section({ content, params }) {
11
- // Content fields: title, pretitle, subtitle, paragraphs, links, imgs, items
9
+ export default function Section({ content, params }) {
12
10
  const { title, pretitle, subtitle, paragraphs = [], links = [], imgs = [] } = content || {}
13
11
 
14
12
  const {
15
- theme = 'light',
16
13
  align = 'center',
17
14
  width = 'default',
18
15
  } = params || {}
19
16
 
20
- // Theme styles
21
- const themes = {
22
- light: 'bg-white text-gray-900',
23
- dark: 'bg-gray-900 text-white',
24
- primary: 'bg-primary text-white',
25
- }
26
-
27
- // Alignment styles
28
17
  const alignments = {
29
18
  left: 'text-left',
30
19
  center: 'text-center',
31
20
  right: 'text-right',
32
21
  }
33
22
 
34
- // Width styles
35
23
  const widths = {
36
24
  narrow: 'max-w-2xl',
37
25
  default: 'max-w-4xl',
@@ -40,58 +28,47 @@ function Section({ content, params }) {
40
28
  }
41
29
 
42
30
  return (
43
- <section className={cn('py-16 px-6', themes[theme])}>
44
- <div className={cn('mx-auto', widths[width], alignments[align])}>
45
- {/* Pretitle / Eyebrow */}
31
+ <div className={cn('py-16 px-6', alignments[align])}>
32
+ <div className={cn('mx-auto', widths[width])}>
46
33
  {pretitle && (
47
- <p className="text-sm font-medium text-primary mb-4 uppercase tracking-wide">
34
+ <p className="text-sm font-medium text-link mb-4 uppercase tracking-wide">
48
35
  {pretitle}
49
36
  </p>
50
37
  )}
51
38
 
52
- {/* Title */}
53
39
  {title && (
54
40
  <H1
55
41
  text={title}
56
- className="text-3xl sm:text-4xl font-bold mb-4"
42
+ className="text-heading text-3xl sm:text-4xl font-bold mb-4"
57
43
  />
58
44
  )}
59
45
 
60
- {/* Subtitle */}
61
46
  {subtitle && (
62
47
  <H2
63
48
  text={subtitle}
64
- className={cn(
65
- 'text-xl mb-6',
66
- theme === 'light' ? 'text-gray-600' : 'text-gray-300'
67
- )}
49
+ className="text-body text-xl mb-6"
68
50
  />
69
51
  )}
70
52
 
71
- {/* Paragraphs */}
72
53
  {paragraphs.map((para, index) => (
73
54
  <P
74
55
  key={index}
75
56
  text={para}
76
- className={cn(
77
- 'text-lg mb-4 leading-relaxed',
78
- theme === 'light' ? 'text-gray-700' : 'text-gray-300'
79
- )}
57
+ className="text-body text-lg mb-4 leading-relaxed"
80
58
  />
81
59
  ))}
82
60
 
83
- {/* Links */}
84
61
  {links.length > 0 && (
85
- <div className={cn('mt-8 flex gap-4 flex-wrap', alignments[align] === 'text-center' && 'justify-center')}>
62
+ <div className={cn('mt-8 flex gap-4 flex-wrap', align === 'center' && 'justify-center')}>
86
63
  {links.map((link, index) => (
87
64
  <Link
88
65
  key={index}
89
- href={link.href}
66
+ to={link.href}
90
67
  className={cn(
91
68
  'inline-flex items-center px-6 py-3 font-medium rounded-lg transition-colors',
92
69
  index === 0
93
- ? 'bg-primary-600 text-white hover:bg-primary-700'
94
- : 'border border-current hover:bg-gray-100'
70
+ ? 'bg-primary text-primary-foreground hover:bg-primary-hover'
71
+ : 'bg-secondary text-secondary-foreground hover:bg-secondary-hover'
95
72
  )}
96
73
  >
97
74
  {link.label}
@@ -100,7 +77,6 @@ function Section({ content, params }) {
100
77
  </div>
101
78
  )}
102
79
 
103
- {/* Images */}
104
80
  {imgs.length > 0 && (
105
81
  <div className="mt-8">
106
82
  {imgs.map((img, index) => (
@@ -114,8 +90,6 @@ function Section({ content, params }) {
114
90
  </div>
115
91
  )}
116
92
  </div>
117
- </section>
93
+ </div>
118
94
  )
119
95
  }
120
-
121
- export default Section
@@ -1,8 +1,3 @@
1
- /**
2
- * Section Component Metadata (v2)
3
- *
4
- * A versatile content section for headings, text, and links.
5
- */
6
1
  export default {
7
2
  title: 'Section',
8
3
  description: 'A versatile content section for headings, text, and links',
@@ -19,12 +14,6 @@ export default {
19
14
  },
20
15
 
21
16
  params: {
22
- theme: {
23
- type: 'select',
24
- label: 'Theme',
25
- options: ['light', 'dark', 'primary'],
26
- default: 'light',
27
- },
28
17
  align: {
29
18
  type: 'select',
30
19
  label: 'Alignment',
@@ -47,15 +36,11 @@ export default {
47
36
  presets: {
48
37
  default: {
49
38
  label: 'Centered',
50
- params: { theme: 'light', align: 'center' },
51
- },
52
- dark: {
53
- label: 'Dark Theme',
54
- params: { theme: 'dark', align: 'center' },
39
+ params: { align: 'center' },
55
40
  },
56
41
  left: {
57
42
  label: 'Left Aligned',
58
- params: { theme: 'light', align: 'left' },
43
+ params: { align: 'left' },
59
44
  },
60
45
  },
61
46
  }
@@ -8,7 +8,7 @@ align: center
8
8
 
9
9
  ## Your Uniweb project is ready
10
10
 
11
- This is a minimal starting point for your Uniweb project. Edit the content in `site/pages/` and customize the components in `foundation/src/components/`.
11
+ This is a minimal starting point for your Uniweb project. Edit the content in `site/pages/` and build section types in `foundation/src/sections/`.
12
12
 
13
13
  [About](/about)
14
14
  [Documentation](https://github.com/uniweb)
@@ -64,12 +64,12 @@ colors:
64
64
  # heading: white
65
65
 
66
66
  # ─── Inline Text Styles ───────────────────────────────────────────────────────
67
- # Named styles for inline text in markdown: [text]{emphasis}
67
+ # Named styles for inline text in markdown: [text]{accent}
68
68
  # Each name maps to CSS properties.
69
- # Defaults: emphasis (colored + bold), muted (subtle).
69
+ # Defaults: accent (colored + bold), muted (subtle).
70
70
 
71
71
  # inline:
72
- # emphasis:
72
+ # accent:
73
73
  # color: 'var(--link)'
74
74
  # font-weight: '600'
75
75
  # muted:
@@ -10,7 +10,7 @@ A website built with [Uniweb](https://github.com/uniweb/cli) — a component web
10
10
  {{projectName}}/
11
11
  ├── foundation/ # React component library
12
12
  │ ├── src/
13
- │ │ ├── components/ # Your components
13
+ │ │ ├── sections/ # Section types (selectable by content authors)
14
14
  │ │ └── styles.css # Tailwind CSS v4 theme
15
15
  │ └── vite.config.js # defineFoundationConfig()
16
16
 
@@ -22,7 +22,7 @@ A website built with [Uniweb](https://github.com/uniweb/cli) — a component web
22
22
  │ ├── site.yml # Site configuration
23
23
  │ └── vite.config.js # defineSiteConfig()
24
24
 
25
- └── AGENTS.md # AI assistant instructions
25
+ └── AGENTS.md # Developer guide (human + AI)
26
26
  ```
27
27
 
28
28
  ## Content Authoring
@@ -46,27 +46,25 @@ Your content here.
46
46
 
47
47
  ## Component Development
48
48
 
49
- Components live in `foundation/src/components/`:
49
+ Section types live in `foundation/src/sections/`. Each is a folder with an `index.jsx` and a `meta.js`:
50
50
 
51
51
  ```jsx
52
- // foundation/src/components/Hero/index.jsx
53
- import { H1, P, cn } from '@uniweb/kit'
52
+ // foundation/src/sections/Hero/index.jsx
53
+ import { H1, P } from '@uniweb/kit'
54
54
 
55
- export function Hero({ content, params }) {
56
- const { title } = content.main?.header || {}
57
- const { theme = 'light' } = params
55
+ export default function Hero({ content }) {
56
+ const { title, paragraphs = [] } = content || {}
58
57
 
59
58
  return (
60
- <section className={cn('py-20', theme === 'dark' && 'bg-gray-900')}>
61
- <H1 text={title} />
62
- </section>
59
+ <div className="max-w-4xl mx-auto px-6">
60
+ <H1 text={title} className="text-heading text-4xl font-bold" />
61
+ <P text={paragraphs} className="text-body mt-4" />
62
+ </div>
63
63
  )
64
64
  }
65
-
66
- export default Hero
67
65
  ```
68
66
 
69
- Exposed components (selectable via `type:` in frontmatter) need a `meta.js` file.
67
+ Components receive parsed content from markdown. Classes like `text-heading` and `text-body` are semantic tokens — the site controls their actual colors through theming, so the same component adapts to any context automatically.
70
68
 
71
69
  {{> components-docs}}
72
70