uniweb 0.2.14 → 0.2.17
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 +133 -35
- package/package.json +4 -3
- package/src/index.js +87 -1022
- package/src/templates/processor.js +17 -3
- package/src/templates/resolver.js +2 -2
- package/templates/_shared/CLAUDE.md.hbs +146 -0
- package/templates/_shared/package.json.hbs +23 -0
- package/templates/_shared/pnpm-workspace.yaml +5 -0
- package/templates/multi/CLAUDE.md.hbs +100 -0
- package/templates/multi/README.md.hbs +96 -0
- package/templates/multi/foundations/default/package.json.hbs +37 -0
- package/templates/multi/foundations/default/src/components/Section/index.jsx +121 -0
- package/templates/multi/foundations/default/src/components/Section/meta.js +66 -0
- package/templates/multi/foundations/default/src/entry-runtime.js +3 -0
- package/templates/multi/foundations/default/src/index.js +32 -0
- package/templates/multi/foundations/default/src/meta.js.hbs +6 -0
- package/templates/multi/foundations/default/src/styles.css +11 -0
- package/templates/multi/foundations/default/vite.config.js +3 -0
- package/templates/multi/package.json.hbs +25 -0
- package/templates/multi/pnpm-workspace.yaml +5 -0
- package/templates/multi/sites/main/index.html.hbs +13 -0
- package/templates/multi/sites/main/package.json.hbs +27 -0
- package/templates/multi/sites/main/pages/home/1-welcome.md.hbs +14 -0
- package/templates/multi/sites/main/pages/home/page.yml +2 -0
- package/templates/multi/sites/main/site.yml.hbs +9 -0
- package/templates/multi/sites/main/src/main.jsx +1 -0
- package/templates/multi/sites/main/vite.config.js +7 -0
- package/templates/multi/template.json +4 -0
- package/templates/single/README.md.hbs +101 -0
- package/templates/single/foundation/package.json.hbs +37 -0
- package/templates/single/foundation/src/components/Section/index.jsx +121 -0
- package/templates/single/foundation/src/components/Section/meta.js +66 -0
- package/templates/single/foundation/src/entry-runtime.js +3 -0
- package/templates/single/foundation/src/index.js +35 -0
- package/templates/single/foundation/src/meta.js.hbs +6 -0
- package/templates/single/foundation/src/styles.css +11 -0
- package/templates/single/foundation/vite.config.js +3 -0
- package/templates/single/site/index.html.hbs +19 -0
- package/templates/single/site/package.json.hbs +27 -0
- package/templates/single/site/pages/home/1-welcome.md.hbs +14 -0
- package/templates/single/site/pages/home/page.yml +2 -0
- package/templates/single/site/site.yml.hbs +7 -0
- package/templates/single/site/src/main.jsx +1 -0
- package/templates/single/site/vite.config.js +3 -0
- package/templates/single/template.json +10 -0
- package/templates/template/README.md.hbs +65 -0
- package/templates/template/package.json.hbs +33 -0
- package/templates/template/template/CLAUDE.md.hbs +70 -0
- package/templates/template/template/foundation/package.json.hbs +37 -0
- package/templates/template/template/foundation/src/components/Features/index.jsx +75 -0
- package/templates/template/template/foundation/src/components/Features/meta.js +41 -0
- package/templates/template/template/foundation/src/components/Hero/index.jsx +89 -0
- package/templates/template/template/foundation/src/components/Hero/meta.js +47 -0
- package/templates/template/template/foundation/src/entry-runtime.js +3 -0
- package/templates/template/template/foundation/src/index.js +36 -0
- package/templates/template/template/foundation/src/meta.js.hbs +6 -0
- package/templates/template/template/foundation/src/styles.css +12 -0
- package/templates/template/template/foundation/vite.config.js +3 -0
- package/templates/template/template/package.json.hbs +14 -0
- package/templates/template/template/pnpm-workspace.yaml +3 -0
- package/templates/template/template/site/index.html.hbs +13 -0
- package/templates/template/template/site/package.json.hbs +27 -0
- package/templates/template/template/site/pages/home/1-hero.md.hbs +16 -0
- package/templates/template/template/site/pages/home/2-features.md +21 -0
- package/templates/template/template/site/pages/home/page.yml +2 -0
- package/templates/template/template/site/site.yml.hbs +8 -0
- package/templates/template/template/site/src/main.jsx +1 -0
- package/templates/template/template/site/vite.config.js +3 -0
- package/templates/template/template/template.json.hbs +7 -0
- package/templates/template/template.json +4 -0
|
@@ -153,11 +153,17 @@ async function processFile(sourcePath, targetPath, data, options = {}) {
|
|
|
153
153
|
* @param {Object} data - Template variables
|
|
154
154
|
* @param {Object} options - Processing options
|
|
155
155
|
* @param {string|null} options.variant - Template variant to use
|
|
156
|
+
* @param {string|null} options.basePath - Base template to merge with (files copied first)
|
|
156
157
|
* @param {Function} options.onWarning - Warning callback
|
|
157
158
|
* @param {Function} options.onProgress - Progress callback
|
|
158
159
|
*/
|
|
159
160
|
export async function copyTemplateDirectory(sourcePath, targetPath, data, options = {}) {
|
|
160
|
-
const { variant = null, onWarning, onProgress } = options
|
|
161
|
+
const { variant = null, basePath = null, onWarning, onProgress } = options
|
|
162
|
+
|
|
163
|
+
// If a base template is specified, copy it first (without the basePath option to avoid recursion)
|
|
164
|
+
if (basePath && existsSync(basePath)) {
|
|
165
|
+
await copyTemplateDirectory(basePath, targetPath, data, { variant, onWarning, onProgress })
|
|
166
|
+
}
|
|
161
167
|
|
|
162
168
|
await fs.mkdir(targetPath, { recursive: true })
|
|
163
169
|
const entries = await fs.readdir(sourcePath, { withFileTypes: true })
|
|
@@ -173,6 +179,9 @@ export async function copyTemplateDirectory(sourcePath, targetPath, data, option
|
|
|
173
179
|
}
|
|
174
180
|
}
|
|
175
181
|
|
|
182
|
+
// Options for recursive calls (without basePath to avoid re-copying base at each level)
|
|
183
|
+
const recursionOptions = { variant, onWarning, onProgress }
|
|
184
|
+
|
|
176
185
|
for (const entry of entries) {
|
|
177
186
|
const sourceName = entry.name
|
|
178
187
|
|
|
@@ -199,7 +208,7 @@ export async function copyTemplateDirectory(sourcePath, targetPath, data, option
|
|
|
199
208
|
const sourceFullPath = path.join(sourcePath, sourceName)
|
|
200
209
|
const targetFullPath = path.join(targetPath, baseName)
|
|
201
210
|
|
|
202
|
-
await copyTemplateDirectory(sourceFullPath, targetFullPath, data,
|
|
211
|
+
await copyTemplateDirectory(sourceFullPath, targetFullPath, data, recursionOptions)
|
|
203
212
|
} else {
|
|
204
213
|
// Regular directory - skip if a variant override exists and we're using that variant
|
|
205
214
|
if (variant && variantBases.has(sourceName)) {
|
|
@@ -210,10 +219,15 @@ export async function copyTemplateDirectory(sourcePath, targetPath, data, option
|
|
|
210
219
|
const sourceFullPath = path.join(sourcePath, sourceName)
|
|
211
220
|
const targetFullPath = path.join(targetPath, sourceName)
|
|
212
221
|
|
|
213
|
-
await copyTemplateDirectory(sourceFullPath, targetFullPath, data,
|
|
222
|
+
await copyTemplateDirectory(sourceFullPath, targetFullPath, data, recursionOptions)
|
|
214
223
|
}
|
|
215
224
|
} else {
|
|
216
225
|
// File processing
|
|
226
|
+
// Skip template.json as it's metadata for the template, not for the output
|
|
227
|
+
if (sourceName === 'template.json') {
|
|
228
|
+
continue
|
|
229
|
+
}
|
|
230
|
+
|
|
217
231
|
// Remove .hbs extension for target filename
|
|
218
232
|
const targetName = sourceName.endsWith('.hbs')
|
|
219
233
|
? sourceName.slice(0, -4)
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Template resolver - parses template identifiers and determines source type
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
// Built-in templates
|
|
6
|
-
export const BUILTIN_TEMPLATES = ['single', 'multi']
|
|
5
|
+
// Built-in templates (file-based in cli/templates/)
|
|
6
|
+
export const BUILTIN_TEMPLATES = ['single', 'multi', 'template']
|
|
7
7
|
|
|
8
8
|
// Official templates from @uniweb/templates package
|
|
9
9
|
export const OFFICIAL_TEMPLATES = ['marketing', 'docs', 'learning']
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# CLAUDE.md - AI Assistant Instructions
|
|
2
|
+
|
|
3
|
+
This file provides guidance for AI assistants working with this Uniweb project.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is a **Uniweb** project - a component web platform that separates content from code.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
{{projectName}}/
|
|
11
|
+
├── foundation/ # React component library (code)
|
|
12
|
+
├── site/ # Content and pages (markdown)
|
|
13
|
+
├── package.json # Root workspace config
|
|
14
|
+
└── pnpm-workspace.yaml
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Commands
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Install dependencies
|
|
21
|
+
pnpm install
|
|
22
|
+
|
|
23
|
+
# Start development server (site with foundation)
|
|
24
|
+
pnpm dev
|
|
25
|
+
|
|
26
|
+
# Build foundation (generates dist/foundation.js and schema.json)
|
|
27
|
+
cd foundation && pnpm build
|
|
28
|
+
|
|
29
|
+
# Build site for production (outputs to site/dist/)
|
|
30
|
+
cd site && pnpm build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Key Files
|
|
34
|
+
|
|
35
|
+
**Site:**
|
|
36
|
+
- `site/vite.config.js` - 3-line config using `defineSiteConfig()` from `@uniweb/build/site`
|
|
37
|
+
- `site/src/main.jsx` - 1-line entry: `import 'virtual:uniweb-site-entry'`
|
|
38
|
+
- `site/site.yml` - Site configuration (foundation path, title, i18n)
|
|
39
|
+
- `site/pages/` - Content pages (file-based routing)
|
|
40
|
+
|
|
41
|
+
**Foundation:**
|
|
42
|
+
- `foundation/vite.config.js` - 3-line config using `defineFoundationConfig()` from `@uniweb/build`
|
|
43
|
+
- `foundation/src/entry-runtime.js` - Runtime entry point (imports styles + components)
|
|
44
|
+
- `foundation/src/index.js` - Component exports
|
|
45
|
+
- `foundation/src/styles.css` - Tailwind CSS v4 theme
|
|
46
|
+
- `foundation/src/components/*/meta.js` - Component metadata and schema
|
|
47
|
+
|
|
48
|
+
## Architecture
|
|
49
|
+
|
|
50
|
+
### Foundation Package (`/foundation`)
|
|
51
|
+
|
|
52
|
+
The **foundation** is a React component library. Each component:
|
|
53
|
+
|
|
54
|
+
1. Receives structured content parsed from markdown
|
|
55
|
+
2. Has configurable parameters (theme, layout, etc.)
|
|
56
|
+
3. Renders the content according to its design
|
|
57
|
+
|
|
58
|
+
**Key directories:**
|
|
59
|
+
- `src/components/*/` - Component implementations
|
|
60
|
+
- `src/components/*/meta.js` - Component metadata and schema
|
|
61
|
+
- `src/styles.css` - Global Tailwind styles
|
|
62
|
+
|
|
63
|
+
### Site Package (`/site`)
|
|
64
|
+
|
|
65
|
+
The **site** contains content written in markdown. The runtime loads the foundation and renders content based on component selections.
|
|
66
|
+
|
|
67
|
+
**Key directories:**
|
|
68
|
+
- `pages/` - Content pages organized in folders
|
|
69
|
+
- `pages/[page-name]/page.yml` - Page metadata
|
|
70
|
+
- `pages/[page-name]/*.md` - Content sections
|
|
71
|
+
|
|
72
|
+
## Content Authoring
|
|
73
|
+
|
|
74
|
+
### Section File Format
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
---
|
|
78
|
+
type: ComponentName
|
|
79
|
+
theme: dark
|
|
80
|
+
layout: center
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
# Main Headline
|
|
84
|
+
|
|
85
|
+
Description paragraph.
|
|
86
|
+
|
|
87
|
+
[Call to Action](#link)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Content Structure
|
|
91
|
+
|
|
92
|
+
The semantic parser extracts content into:
|
|
93
|
+
|
|
94
|
+
- **`content.main.header`**: title, pretitle, subtitle
|
|
95
|
+
- **`content.main.body`**: paragraphs, links, imgs, lists
|
|
96
|
+
- **`content.items`**: Content groups from H3 headings
|
|
97
|
+
|
|
98
|
+
## Component Development
|
|
99
|
+
|
|
100
|
+
### Component Interface
|
|
101
|
+
|
|
102
|
+
```jsx
|
|
103
|
+
function MyComponent({ content, params, block, website }) {
|
|
104
|
+
const { title } = content.main?.header || {}
|
|
105
|
+
const { paragraphs = [] } = content.main?.body || {}
|
|
106
|
+
// ...
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Using @uniweb/kit
|
|
111
|
+
|
|
112
|
+
```jsx
|
|
113
|
+
import { H1, P, Link, cn } from '@uniweb/kit'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Component Metadata (`meta.js`)
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
export default {
|
|
120
|
+
title: 'Component Name',
|
|
121
|
+
description: 'What the component does',
|
|
122
|
+
category: 'Content',
|
|
123
|
+
elements: { /* content elements */ },
|
|
124
|
+
properties: { /* configurable params */ },
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Tailwind CSS v4
|
|
129
|
+
|
|
130
|
+
This project uses Tailwind CSS v4 with the Vite plugin.
|
|
131
|
+
|
|
132
|
+
**Theme customization** (`foundation/src/styles.css`):
|
|
133
|
+
|
|
134
|
+
```css
|
|
135
|
+
@import "tailwindcss";
|
|
136
|
+
@source "./components/**/*.{js,jsx}";
|
|
137
|
+
@theme {
|
|
138
|
+
--color-primary: #3b82f6;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Resources
|
|
143
|
+
|
|
144
|
+
- [Uniweb Documentation](https://github.com/uniweb/uniweb)
|
|
145
|
+
- [@uniweb/kit Documentation](https://www.npmjs.com/package/@uniweb/kit)
|
|
146
|
+
- [Tailwind CSS v4](https://tailwindcss.com/docs)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "pnpm --filter site dev",
|
|
8
|
+
"build": "pnpm --filter foundation build && pnpm --filter site build",
|
|
9
|
+
"preview": "pnpm --filter site preview"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@types/node": "^22.0.0"
|
|
13
|
+
},
|
|
14
|
+
"workspaces": [
|
|
15
|
+
"site",
|
|
16
|
+
"foundation",
|
|
17
|
+
"sites/*",
|
|
18
|
+
"foundations/*"
|
|
19
|
+
],
|
|
20
|
+
"pnpm": {
|
|
21
|
+
"onlyBuiltDependencies": ["esbuild", "sharp"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# CLAUDE.md - AI Assistant Instructions
|
|
2
|
+
|
|
3
|
+
This file provides guidance for AI assistants working with this Uniweb multi-site workspace.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is a **Uniweb multi-site workspace** - supporting multiple sites and foundations.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
{{projectName}}/
|
|
11
|
+
├── foundations/ # Shared component libraries
|
|
12
|
+
│ └── default/ # Default foundation
|
|
13
|
+
├── sites/ # Content sites
|
|
14
|
+
│ └── main/ # Main site
|
|
15
|
+
├── package.json # Root workspace config
|
|
16
|
+
└── pnpm-workspace.yaml
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Commands
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Install dependencies
|
|
23
|
+
pnpm install
|
|
24
|
+
|
|
25
|
+
# Start main site development
|
|
26
|
+
pnpm dev
|
|
27
|
+
|
|
28
|
+
# Start all sites in parallel
|
|
29
|
+
pnpm dev:all
|
|
30
|
+
|
|
31
|
+
# Build main site
|
|
32
|
+
pnpm build
|
|
33
|
+
|
|
34
|
+
# Build all sites
|
|
35
|
+
pnpm build:all
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Adding New Sites
|
|
39
|
+
|
|
40
|
+
1. Copy an existing site directory:
|
|
41
|
+
```bash
|
|
42
|
+
cp -r sites/main sites/newsite
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
2. Update `sites/newsite/package.json` with a unique name
|
|
46
|
+
|
|
47
|
+
3. Update `sites/newsite/site.yml` with site configuration
|
|
48
|
+
|
|
49
|
+
4. Run `pnpm install` to link the new site
|
|
50
|
+
|
|
51
|
+
## Adding New Foundations
|
|
52
|
+
|
|
53
|
+
1. Copy an existing foundation:
|
|
54
|
+
```bash
|
|
55
|
+
cp -r foundations/default foundations/newfoundation
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
2. Update `foundations/newfoundation/package.json` with a unique name
|
|
59
|
+
|
|
60
|
+
3. In your site's `site.yml`, reference the foundation:
|
|
61
|
+
```yaml
|
|
62
|
+
foundation: newfoundation
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
4. In your site's `package.json`, add the dependency:
|
|
66
|
+
```json
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"newfoundation": "file:../../foundations/newfoundation"
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Key Files
|
|
73
|
+
|
|
74
|
+
**Sites:**
|
|
75
|
+
- `sites/*/vite.config.js` - 3-line config using `defineSiteConfig()` from `@uniweb/build/site`
|
|
76
|
+
- `sites/*/src/main.jsx` - 1-line entry: `import 'virtual:uniweb-site-entry'`
|
|
77
|
+
- `sites/*/site.yml` - Site configuration (foundation reference, title, i18n)
|
|
78
|
+
- `sites/*/pages/` - Content pages (file-based routing)
|
|
79
|
+
|
|
80
|
+
**Foundations:**
|
|
81
|
+
- `foundations/*/vite.config.js` - 3-line config using `defineFoundationConfig()` from `@uniweb/build`
|
|
82
|
+
- `foundations/*/src/entry-runtime.js` - Runtime entry point
|
|
83
|
+
- `foundations/*/src/styles.css` - Tailwind CSS v4 theme
|
|
84
|
+
- `foundations/*/src/components/*/meta.js` - Component metadata
|
|
85
|
+
|
|
86
|
+
## Architecture
|
|
87
|
+
|
|
88
|
+
### Foundations (`/foundations/*`)
|
|
89
|
+
|
|
90
|
+
Each foundation is a React component library. Sites can share foundations or have their own.
|
|
91
|
+
|
|
92
|
+
### Sites (`/sites/*`)
|
|
93
|
+
|
|
94
|
+
Each site contains content in markdown. A site references one foundation for its components.
|
|
95
|
+
|
|
96
|
+
## Resources
|
|
97
|
+
|
|
98
|
+
- [Uniweb Documentation](https://github.com/uniweb/uniweb)
|
|
99
|
+
- [@uniweb/kit Documentation](https://www.npmjs.com/package/@uniweb/kit)
|
|
100
|
+
- [Tailwind CSS v4](https://tailwindcss.com/docs)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
A multi-site workspace built with [Uniweb](https://github.com/uniweb/uniweb) — a component web platform that separates content from code.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install # Install dependencies
|
|
9
|
+
pnpm dev # Start main site development
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Visit `http://localhost:5173` to see your site.
|
|
13
|
+
|
|
14
|
+
## Project Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
{{projectName}}/
|
|
18
|
+
├── foundations/ # Shared component libraries
|
|
19
|
+
│ └── default/ # Default foundation
|
|
20
|
+
│ ├── src/
|
|
21
|
+
│ │ ├── components/
|
|
22
|
+
│ │ └── styles.css
|
|
23
|
+
│ └── vite.config.js
|
|
24
|
+
│
|
|
25
|
+
├── sites/ # Content sites
|
|
26
|
+
│ └── main/ # Main site
|
|
27
|
+
│ ├── pages/
|
|
28
|
+
│ ├── site.yml
|
|
29
|
+
│ ├── src/main.jsx
|
|
30
|
+
│ └── vite.config.js
|
|
31
|
+
│
|
|
32
|
+
├── CLAUDE.md # AI assistant instructions
|
|
33
|
+
└── README.md # This file
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm dev # Start main site
|
|
40
|
+
pnpm dev:all # Start all sites in parallel
|
|
41
|
+
pnpm build # Build main site
|
|
42
|
+
pnpm build:all # Build all sites
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Adding a New Site
|
|
46
|
+
|
|
47
|
+
1. Copy an existing site:
|
|
48
|
+
```bash
|
|
49
|
+
cp -r sites/main sites/newsite
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
2. Update `sites/newsite/package.json` with a unique name
|
|
53
|
+
|
|
54
|
+
3. Update `sites/newsite/site.yml` with site configuration
|
|
55
|
+
|
|
56
|
+
4. Run `pnpm install` to link the new site
|
|
57
|
+
|
|
58
|
+
## Adding a New Foundation
|
|
59
|
+
|
|
60
|
+
1. Copy an existing foundation:
|
|
61
|
+
```bash
|
|
62
|
+
cp -r foundations/default foundations/newfoundation
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
2. Update the package.json with a unique name
|
|
66
|
+
|
|
67
|
+
3. In your site's `site.yml`, reference the foundation:
|
|
68
|
+
```yaml
|
|
69
|
+
foundation: newfoundation
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
4. In your site's `package.json`, add the dependency:
|
|
73
|
+
```json
|
|
74
|
+
"dependencies": {
|
|
75
|
+
"newfoundation": "file:../../foundations/newfoundation"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Sharing Foundations
|
|
80
|
+
|
|
81
|
+
Multiple sites can use the same foundation. Each site references the foundation in its `site.yml` and `package.json`.
|
|
82
|
+
|
|
83
|
+
This allows:
|
|
84
|
+
- Consistent components across sites
|
|
85
|
+
- Single source of truth for design
|
|
86
|
+
- Foundation updates propagate to all sites
|
|
87
|
+
|
|
88
|
+
## AI Assistance
|
|
89
|
+
|
|
90
|
+
See [CLAUDE.md](./CLAUDE.md) for detailed instructions that AI assistants can use.
|
|
91
|
+
|
|
92
|
+
## Learn More
|
|
93
|
+
|
|
94
|
+
- [Uniweb Documentation](https://github.com/uniweb/uniweb)
|
|
95
|
+
- [@uniweb/kit Components](https://www.npmjs.com/package/@uniweb/kit)
|
|
96
|
+
- [Tailwind CSS v4](https://tailwindcss.com)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "default",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./src/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.js",
|
|
8
|
+
"./styles": "./src/styles.css",
|
|
9
|
+
"./dist": "./dist/foundation.js",
|
|
10
|
+
"./dist/styles": "./dist/assets/style.css"
|
|
11
|
+
},
|
|
12
|
+
"files": ["dist", "src"],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "vite",
|
|
15
|
+
"build": "uniweb build",
|
|
16
|
+
"build:vite": "vite build",
|
|
17
|
+
"preview": "vite preview"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
21
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@uniweb/core": "{{version "@uniweb/core"}}",
|
|
25
|
+
"@uniweb/kit": "{{version "@uniweb/kit"}}"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@tailwindcss/vite": "^4.0.0",
|
|
29
|
+
"@vitejs/plugin-react": "^5.0.0",
|
|
30
|
+
"react": "^18.2.0",
|
|
31
|
+
"react-dom": "^18.2.0",
|
|
32
|
+
"tailwindcss": "^4.0.0",
|
|
33
|
+
"uniweb": "{{version "uniweb"}}",
|
|
34
|
+
"vite": "^7.0.0",
|
|
35
|
+
"vite-plugin-svgr": "^4.2.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { H1, H2, P, Link, cn } from '@uniweb/kit'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Section Component
|
|
6
|
+
*
|
|
7
|
+
* A versatile content section that handles headings, text, and links.
|
|
8
|
+
* This is the default component for rendering markdown content.
|
|
9
|
+
*/
|
|
10
|
+
export function Section({ content, params }) {
|
|
11
|
+
const { title, pretitle, subtitle } = content.main?.header || {}
|
|
12
|
+
const { paragraphs = [], links = [], imgs = [] } = content.main?.body || {}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
theme = 'light',
|
|
16
|
+
align = 'center',
|
|
17
|
+
width = 'default',
|
|
18
|
+
} = params || {}
|
|
19
|
+
|
|
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
|
+
const alignments = {
|
|
29
|
+
left: 'text-left',
|
|
30
|
+
center: 'text-center',
|
|
31
|
+
right: 'text-right',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Width styles
|
|
35
|
+
const widths = {
|
|
36
|
+
narrow: 'max-w-2xl',
|
|
37
|
+
default: 'max-w-4xl',
|
|
38
|
+
wide: 'max-w-6xl',
|
|
39
|
+
full: 'max-w-none',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<section className={cn('py-16 px-6', themes[theme])}>
|
|
44
|
+
<div className={cn('mx-auto', widths[width], alignments[align])}>
|
|
45
|
+
{/* Pretitle / Eyebrow */}
|
|
46
|
+
{pretitle && (
|
|
47
|
+
<p className="text-sm font-medium text-primary mb-4 uppercase tracking-wide">
|
|
48
|
+
{pretitle}
|
|
49
|
+
</p>
|
|
50
|
+
)}
|
|
51
|
+
|
|
52
|
+
{/* Title */}
|
|
53
|
+
{title && (
|
|
54
|
+
<H1
|
|
55
|
+
text={title}
|
|
56
|
+
className="text-3xl sm:text-4xl font-bold mb-4"
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
|
|
60
|
+
{/* Subtitle */}
|
|
61
|
+
{subtitle && (
|
|
62
|
+
<H2
|
|
63
|
+
text={subtitle}
|
|
64
|
+
className={cn(
|
|
65
|
+
'text-xl mb-6',
|
|
66
|
+
theme === 'light' ? 'text-gray-600' : 'text-gray-300'
|
|
67
|
+
)}
|
|
68
|
+
/>
|
|
69
|
+
)}
|
|
70
|
+
|
|
71
|
+
{/* Paragraphs */}
|
|
72
|
+
{paragraphs.map((para, index) => (
|
|
73
|
+
<P
|
|
74
|
+
key={index}
|
|
75
|
+
text={para}
|
|
76
|
+
className={cn(
|
|
77
|
+
'text-lg mb-4 leading-relaxed',
|
|
78
|
+
theme === 'light' ? 'text-gray-700' : 'text-gray-300'
|
|
79
|
+
)}
|
|
80
|
+
/>
|
|
81
|
+
))}
|
|
82
|
+
|
|
83
|
+
{/* Links */}
|
|
84
|
+
{links.length > 0 && (
|
|
85
|
+
<div className={cn('mt-8 flex gap-4 flex-wrap', alignments[align] === 'text-center' && 'justify-center')}>
|
|
86
|
+
{links.map((link, index) => (
|
|
87
|
+
<Link
|
|
88
|
+
key={index}
|
|
89
|
+
href={link.href}
|
|
90
|
+
className={cn(
|
|
91
|
+
'inline-flex items-center px-6 py-3 font-medium rounded-lg transition-colors',
|
|
92
|
+
index === 0
|
|
93
|
+
? 'bg-primary text-white hover:bg-primary-dark'
|
|
94
|
+
: 'border border-current hover:bg-gray-100'
|
|
95
|
+
)}
|
|
96
|
+
>
|
|
97
|
+
{link.label}
|
|
98
|
+
</Link>
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
|
|
103
|
+
{/* Images */}
|
|
104
|
+
{imgs.length > 0 && (
|
|
105
|
+
<div className="mt-8">
|
|
106
|
+
{imgs.map((img, index) => (
|
|
107
|
+
<img
|
|
108
|
+
key={index}
|
|
109
|
+
src={img.url || img.src}
|
|
110
|
+
alt={img.alt || ''}
|
|
111
|
+
className="rounded-lg shadow-lg mx-auto"
|
|
112
|
+
/>
|
|
113
|
+
))}
|
|
114
|
+
</div>
|
|
115
|
+
)}
|
|
116
|
+
</div>
|
|
117
|
+
</section>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export default Section
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
title: 'Section',
|
|
3
|
+
description: 'A versatile content section for headings, text, and links',
|
|
4
|
+
category: 'Content',
|
|
5
|
+
|
|
6
|
+
elements: {
|
|
7
|
+
pretitle: {
|
|
8
|
+
label: 'Eyebrow',
|
|
9
|
+
description: 'Small text above the title (H3 before H1)',
|
|
10
|
+
},
|
|
11
|
+
title: {
|
|
12
|
+
label: 'Title',
|
|
13
|
+
description: 'Main heading (H1)',
|
|
14
|
+
},
|
|
15
|
+
subtitle: {
|
|
16
|
+
label: 'Subtitle',
|
|
17
|
+
description: 'Secondary heading (H2 after H1)',
|
|
18
|
+
},
|
|
19
|
+
paragraphs: {
|
|
20
|
+
label: 'Content',
|
|
21
|
+
description: 'Body text paragraphs',
|
|
22
|
+
},
|
|
23
|
+
links: {
|
|
24
|
+
label: 'Links',
|
|
25
|
+
description: 'Call-to-action buttons',
|
|
26
|
+
},
|
|
27
|
+
imgs: {
|
|
28
|
+
label: 'Images',
|
|
29
|
+
description: 'Section images',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
properties: {
|
|
34
|
+
theme: {
|
|
35
|
+
type: 'select',
|
|
36
|
+
label: 'Theme',
|
|
37
|
+
options: [
|
|
38
|
+
{ value: 'light', label: 'Light' },
|
|
39
|
+
{ value: 'dark', label: 'Dark' },
|
|
40
|
+
{ value: 'primary', label: 'Primary' },
|
|
41
|
+
],
|
|
42
|
+
default: 'light',
|
|
43
|
+
},
|
|
44
|
+
align: {
|
|
45
|
+
type: 'select',
|
|
46
|
+
label: 'Alignment',
|
|
47
|
+
options: [
|
|
48
|
+
{ value: 'left', label: 'Left' },
|
|
49
|
+
{ value: 'center', label: 'Center' },
|
|
50
|
+
{ value: 'right', label: 'Right' },
|
|
51
|
+
],
|
|
52
|
+
default: 'center',
|
|
53
|
+
},
|
|
54
|
+
width: {
|
|
55
|
+
type: 'select',
|
|
56
|
+
label: 'Width',
|
|
57
|
+
options: [
|
|
58
|
+
{ value: 'narrow', label: 'Narrow' },
|
|
59
|
+
{ value: 'default', label: 'Default' },
|
|
60
|
+
{ value: 'wide', label: 'Wide' },
|
|
61
|
+
{ value: 'full', label: 'Full Width' },
|
|
62
|
+
],
|
|
63
|
+
default: 'default',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
}
|