@bytesbrains/weblocks 0.2.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/AGENT.md +214 -0
- package/CATALOG.md +387 -0
- package/CHANGELOG.md +63 -0
- package/LICENSE +21 -0
- package/README.md +294 -0
- package/catalog.json +1850 -0
- package/lib/blocks/about.d.ts +2 -0
- package/lib/blocks/about.js +50 -0
- package/lib/blocks/accordion.d.ts +2 -0
- package/lib/blocks/accordion.js +51 -0
- package/lib/blocks/announcementBar.d.ts +2 -0
- package/lib/blocks/announcementBar.js +56 -0
- package/lib/blocks/appShell.d.ts +2 -0
- package/lib/blocks/appShell.js +54 -0
- package/lib/blocks/auth.d.ts +2 -0
- package/lib/blocks/auth.js +82 -0
- package/lib/blocks/blogList.d.ts +2 -0
- package/lib/blocks/blogList.js +81 -0
- package/lib/blocks/blogPost.d.ts +2 -0
- package/lib/blocks/blogPost.js +60 -0
- package/lib/blocks/carousel.d.ts +2 -0
- package/lib/blocks/carousel.js +56 -0
- package/lib/blocks/contactDetails.d.ts +2 -0
- package/lib/blocks/contactDetails.js +50 -0
- package/lib/blocks/contactForm.d.ts +2 -0
- package/lib/blocks/contactForm.js +100 -0
- package/lib/blocks/cta.d.ts +2 -0
- package/lib/blocks/cta.js +40 -0
- package/lib/blocks/divider.d.ts +2 -0
- package/lib/blocks/divider.js +28 -0
- package/lib/blocks/faq.d.ts +2 -0
- package/lib/blocks/faq.js +52 -0
- package/lib/blocks/features.d.ts +2 -0
- package/lib/blocks/features.js +58 -0
- package/lib/blocks/feed.d.ts +2 -0
- package/lib/blocks/feed.js +81 -0
- package/lib/blocks/footer.d.ts +2 -0
- package/lib/blocks/footer.js +40 -0
- package/lib/blocks/gallery.d.ts +2 -0
- package/lib/blocks/gallery.js +64 -0
- package/lib/blocks/hero.d.ts +2 -0
- package/lib/blocks/hero.js +48 -0
- package/lib/blocks/heroApp.d.ts +2 -0
- package/lib/blocks/heroApp.js +65 -0
- package/lib/blocks/logos.d.ts +2 -0
- package/lib/blocks/logos.js +59 -0
- package/lib/blocks/map.d.ts +2 -0
- package/lib/blocks/map.js +43 -0
- package/lib/blocks/nav.d.ts +2 -0
- package/lib/blocks/nav.js +39 -0
- package/lib/blocks/newsletter.d.ts +2 -0
- package/lib/blocks/newsletter.js +56 -0
- package/lib/blocks/pricing.d.ts +2 -0
- package/lib/blocks/pricing.js +89 -0
- package/lib/blocks/prose.d.ts +5 -0
- package/lib/blocks/prose.js +46 -0
- package/lib/blocks/richText.d.ts +2 -0
- package/lib/blocks/richText.js +46 -0
- package/lib/blocks/services.d.ts +2 -0
- package/lib/blocks/services.js +65 -0
- package/lib/blocks/sidebar.d.ts +2 -0
- package/lib/blocks/sidebar.js +58 -0
- package/lib/blocks/socialLinks.d.ts +2 -0
- package/lib/blocks/socialLinks.js +53 -0
- package/lib/blocks/spacer.d.ts +2 -0
- package/lib/blocks/spacer.js +25 -0
- package/lib/blocks/split.d.ts +2 -0
- package/lib/blocks/split.js +67 -0
- package/lib/blocks/stats.d.ts +2 -0
- package/lib/blocks/stats.js +64 -0
- package/lib/blocks/steps.d.ts +2 -0
- package/lib/blocks/steps.js +63 -0
- package/lib/blocks/tabs.d.ts +2 -0
- package/lib/blocks/tabs.js +57 -0
- package/lib/blocks/team.d.ts +2 -0
- package/lib/blocks/team.js +94 -0
- package/lib/blocks/testimonials.d.ts +2 -0
- package/lib/blocks/testimonials.js +54 -0
- package/lib/blocks/timeline.d.ts +2 -0
- package/lib/blocks/timeline.js +53 -0
- package/lib/blocks/video.d.ts +2 -0
- package/lib/blocks/video.js +61 -0
- package/lib/catalog.d.ts +26 -0
- package/lib/catalog.js +62 -0
- package/lib/generate.d.ts +35 -0
- package/lib/generate.js +160 -0
- package/lib/index.d.ts +19 -0
- package/lib/index.js +11 -0
- package/lib/ops.d.ts +85 -0
- package/lib/ops.js +248 -0
- package/lib/presets.d.ts +16 -0
- package/lib/presets.js +50 -0
- package/lib/pwa.d.ts +30 -0
- package/lib/pwa.js +79 -0
- package/lib/registry.d.ts +53 -0
- package/lib/registry.js +81 -0
- package/lib/render.d.ts +8 -0
- package/lib/render.js +105 -0
- package/lib/runtime.d.ts +68 -0
- package/lib/runtime.js +46 -0
- package/lib/schema.d.ts +67 -0
- package/lib/schema.js +150 -0
- package/lib/tokens.d.ts +37 -0
- package/lib/tokens.js +136 -0
- package/lib/types.d.ts +108 -0
- package/lib/types.js +10 -0
- package/lib/validate.d.ts +10 -0
- package/lib/validate.js +48 -0
- package/package.json +65 -0
package/AGENT.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# AGENT.md — using `@bytesbrains/weblocks` from an AI
|
|
2
|
+
|
|
3
|
+
This guide is written for a **model or agent** that will build and edit web apps
|
|
4
|
+
with this package. Hand it to an LLM (as context or a system prompt), or read it
|
|
5
|
+
as the coding agent integrating the package. For the *why*, see
|
|
6
|
+
[`VISION.md`](./VISION.md); for internals, [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## What you are doing
|
|
11
|
+
|
|
12
|
+
You do **not** write HTML, CSS, or JavaScript. You produce a **`SiteManifest`** — a
|
|
13
|
+
JSON document of design tokens plus an ordered list of typed **blocks** chosen
|
|
14
|
+
from a fixed **catalog**. The engine validates your manifest and renders it to one
|
|
15
|
+
self-contained static HTML document. It cannot render a broken page, so your job
|
|
16
|
+
is to compose *good, specific* content — validity is guaranteed for you.
|
|
17
|
+
|
|
18
|
+
Two things you produce:
|
|
19
|
+
|
|
20
|
+
1. **Compose** — a full `SiteManifest` from a brief (one shot).
|
|
21
|
+
2. **Edit** — a JSON array of **edit ops** that change an existing manifest.
|
|
22
|
+
|
|
23
|
+
## Prime directives
|
|
24
|
+
|
|
25
|
+
1. **Use only catalog block types and config keys.** Never invent a `type` or a
|
|
26
|
+
field. The catalog (`catalog.json` / `catalogPrompt()`) is your complete,
|
|
27
|
+
closed API. If a request doesn’t fit, get as close as you can with existing
|
|
28
|
+
blocks — do not fabricate markup.
|
|
29
|
+
2. **Never output raw HTML/CSS/JS.** There is no “custom HTML” block by design.
|
|
30
|
+
For freeform prose use the `rich-text` block’s *typed* nodes.
|
|
31
|
+
3. **Write real, specific copy** for the described subject — no lorem ipsum, no
|
|
32
|
+
`[placeholder]`.
|
|
33
|
+
4. **Order blocks top-to-bottom** as a visitor reads the page (`nav` first,
|
|
34
|
+
`footer` last).
|
|
35
|
+
5. **Theme with tokens, not per-block colors.** Set `design` once; the whole app
|
|
36
|
+
inherits it. Never hand-set text colors on buttons — contrast is automatic.
|
|
37
|
+
6. **Output only the JSON** asked for (a manifest object, or an ops array). No
|
|
38
|
+
prose, no code fences.
|
|
39
|
+
|
|
40
|
+
## The catalog is your contract
|
|
41
|
+
|
|
42
|
+
Always work from the shipped catalog — it lists every block, its description, and
|
|
43
|
+
its exact config schema:
|
|
44
|
+
|
|
45
|
+
- `catalog.json` — JSON Schema per block (ideal as a function-calling schema).
|
|
46
|
+
- `catalogPrompt()` — a compact text menu for a system prompt.
|
|
47
|
+
- `CATALOG.md` — the same, human-readable.
|
|
48
|
+
|
|
49
|
+
The 37 block types, by group:
|
|
50
|
+
|
|
51
|
+
- **Chrome/app-shell:** `nav`, `app-shell`, `sidebar`, `announcement-bar`, `footer`
|
|
52
|
+
- **Heroes:** `hero`, `hero-app`
|
|
53
|
+
- **Content:** `features`, `about`, `rich-text`, `split`, `steps`, `stats`,
|
|
54
|
+
`services-catalogue`, `pricing`, `logos`, `team`
|
|
55
|
+
- **Media:** `gallery`, `carousel`, `video`, `map`
|
|
56
|
+
- **Structured:** `timeline`, `tabs`, `accordion`, `testimonials`, `faq`
|
|
57
|
+
- **Collections:** `blog-list`, `blog-post`, `feed`
|
|
58
|
+
- **Dynamic (powered):** `contact-form`, `newsletter`, `auth`
|
|
59
|
+
- **Conversion/rhythm:** `cta`, `social-links`, `contact-details`, `divider`, `spacer`
|
|
60
|
+
|
|
61
|
+
## Composing a manifest
|
|
62
|
+
|
|
63
|
+
Emit exactly this shape (a block’s `id` is optional — the engine assigns one):
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"meta": { "title": "…", "description": "…", "lang": "en" },
|
|
68
|
+
"design": {
|
|
69
|
+
"mode": "light",
|
|
70
|
+
"palette": { "bg": "#…", "surface": "#…", "text": "#…", "muted": "#…", "primary": "#…", "accent": "#…" },
|
|
71
|
+
"typography": { "fontStack": "system-ui, sans-serif", "scale": "default" },
|
|
72
|
+
"radius": "soft", "spacing": "default", "motion": "subtle"
|
|
73
|
+
},
|
|
74
|
+
"blocks": [
|
|
75
|
+
{ "type": "nav", "config": { "brand": "…", "links": [ { "label": "…", "href": "#…" } ] } },
|
|
76
|
+
{ "type": "hero", "config": { "headline": "…", "subhead": "…", "cta": { "label": "…", "href": "#…" } } }
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Worked example — a bakery landing page:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"meta": { "title": "Loam & Ember — wood-fired bakery", "description": "Naturally-leavened bread from a neighbourhood oven.", "lang": "en" },
|
|
86
|
+
"design": {
|
|
87
|
+
"mode": "auto",
|
|
88
|
+
"palette": { "bg": "#fbf5ec", "surface": "#ffffff", "text": "#2a201a", "muted": "#7c6a58", "primary": "#b04d2b", "accent": "#c8892f" },
|
|
89
|
+
"typography": { "fontStack": "system-ui, sans-serif", "scale": "expressive" },
|
|
90
|
+
"radius": "round", "spacing": "airy", "motion": "subtle"
|
|
91
|
+
},
|
|
92
|
+
"blocks": [
|
|
93
|
+
{ "type": "nav", "config": { "brand": "Loam & Ember", "links": [ { "label": "Bread", "href": "#bread" }, { "label": "Classes", "href": "#classes" } ], "cta": { "label": "Order", "href": "#order" } } },
|
|
94
|
+
{ "type": "hero", "config": { "eyebrow": "Neighbourhood bakery", "headline": "Bread with a long, slow memory", "subhead": "Naturally leavened, wood-fired at dawn.", "align": "center", "cta": { "label": "See this week’s bakes", "href": "#bread" } } },
|
|
95
|
+
{ "type": "stats", "config": { "title": "From the oven", "columns": 3, "items": [ { "value": "36", "suffix": "h", "label": "Fermentation" }, { "value": "480", "suffix": "°C", "label": "Hearth" }, { "value": "3", "label": "Ingredients" } ] } },
|
|
96
|
+
{ "type": "pricing", "config": { "title": "This week’s bakes", "plans": [ { "name": "Country sourdough", "price": "$9", "period": "/ loaf", "features": ["1kg boule", "Blistered crust"], "ctaLabel": "Add to order", "ctaHref": "#order" } ] } },
|
|
97
|
+
{ "type": "contact-details", "config": { "title": "Visit", "address": "42 Kiln Row", "hours": "Wed–Sun · 8am–2pm" } },
|
|
98
|
+
{ "type": "footer", "config": { "brand": "Loam & Ember", "copyright": "© 2026 Loam & Ember" } }
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Design tokens & theming
|
|
104
|
+
|
|
105
|
+
Set `design` once; every block styles from it.
|
|
106
|
+
|
|
107
|
+
- `mode`: `light` | `dark` | `auto`. Use **`auto`** to follow the viewer’s OS
|
|
108
|
+
theme; optionally add a `darkPalette` (same keys as `palette`) for the dark side.
|
|
109
|
+
- `palette`: 6 hex roles — `bg`, `surface`, `text`, `muted`, `primary`, `accent`.
|
|
110
|
+
- `typography.scale`: `compact` | `default` | `expressive`; `fontStack`: any CSS
|
|
111
|
+
font stack (no web font is loaded for you).
|
|
112
|
+
- `radius`: `sharp` | `soft` | `round`; `spacing`: `tight` | `default` | `airy`;
|
|
113
|
+
`motion`: `none` | `subtle` | `lively`.
|
|
114
|
+
- **Presets:** instead of hand-mixing hex, base the palette on a named preset —
|
|
115
|
+
`sand`, `midnight`, `forest`, `mono`, `candy`, `ocean` — or apply one via the
|
|
116
|
+
`applyPreset` op when editing.
|
|
117
|
+
- **Do not set text colors on buttons.** `--on-primary` / `--on-accent` are
|
|
118
|
+
derived automatically and stay legible on any palette.
|
|
119
|
+
|
|
120
|
+
## Editing with ops
|
|
121
|
+
|
|
122
|
+
To change an existing manifest, emit a **JSON array of ops**. Each is validated
|
|
123
|
+
before it applies; a bad op is skipped (not a corruption), and the manifest
|
|
124
|
+
`version` bumps per accepted edit.
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
[
|
|
128
|
+
{ "op": "updateBlock", "id": "hero-1", "config": { "headline": "New headline" } },
|
|
129
|
+
{ "op": "applyPreset", "name": "midnight" },
|
|
130
|
+
{ "op": "addBlock", "type": "faq", "at": 4, "config": { "items": [ { "question": "…", "answer": "…" } ] } }
|
|
131
|
+
]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Op vocabulary:
|
|
135
|
+
|
|
136
|
+
| Op | Purpose |
|
|
137
|
+
|---|---|
|
|
138
|
+
| `addBlock` `{ type, config?, at?, id? }` | insert a block |
|
|
139
|
+
| `updateBlock` `{ id, config }` | patch a block’s config (only changed keys) |
|
|
140
|
+
| `removeBlock` `{ id }` / `moveBlock` `{ id, to }` / `setVisible` `{ id, visible }` | block-level edits |
|
|
141
|
+
| `addItem` / `updateItem` / `removeItem` / `moveItem` `{ id, field, … }` | edit **one item** of an array field (e.g. one `features` item) without rewriting the block |
|
|
142
|
+
| `setDesignTokens` `{ patch }` / `applyPreset` `{ name }` | site-wide theming |
|
|
143
|
+
| `setOverrides` `{ id, overrides }` | per-section palette / `radius` / `spacing` (or `null` to clear) |
|
|
144
|
+
| `setMeta` `{ patch }` | title / description / lang |
|
|
145
|
+
|
|
146
|
+
Prefer **array-item ops** for “change one thing” requests — they’re surgical and
|
|
147
|
+
cheap:
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
[ { "op": "updateItem", "id": "features-1", "field": "items", "index": 0, "patch": { "text": "Now twice as fast" } } ]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Powered (dynamic) blocks
|
|
154
|
+
|
|
155
|
+
`contact-form`, `newsletter`, and `auth` need a backend. You still only fill their
|
|
156
|
+
typed config (fields, labels, providers). The block declares the runtime
|
|
157
|
+
capability it needs; the **host** wires the endpoint. If no runtime is wired, the
|
|
158
|
+
block renders inert-but-valid — that is expected, not an error. Never put secrets,
|
|
159
|
+
endpoints, captcha keys, or backend logic in the config.
|
|
160
|
+
|
|
161
|
+
## PWA & SEO (optional, additive)
|
|
162
|
+
|
|
163
|
+
Add top-level fields to make the app installable / discoverable:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{ "pwa": { "name": "My App", "shortName": "App", "offline": true },
|
|
167
|
+
"seo": { "ogImage": "https://…/card.png", "twitterCard": "summary_large_image" } }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Programmatic integration (for the app developer)
|
|
171
|
+
|
|
172
|
+
The engine is provider-agnostic — you inject the model call:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { generateSite, editSite, renderSite } from '@bytesbrains/weblocks';
|
|
176
|
+
|
|
177
|
+
const callModel = async ({ system, user }) => /* call any LLM */ '…';
|
|
178
|
+
|
|
179
|
+
const { ok, manifest } = await generateSite(brief, callModel); // brief → manifest
|
|
180
|
+
const html = renderSite(manifest); // manifest → static HTML
|
|
181
|
+
const { manifest: next } = await editSite(manifest, 'go dark', callModel); // NL → validated ops
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`buildGenerationPrompt(brief)` and `buildEditPrompt(manifest, message)` return the
|
|
185
|
+
exact `{ system, user }` prompts (with the catalog embedded) if you want to call
|
|
186
|
+
the model yourself; `parseManifestResponse` / `parseOpsResponse` turn a raw reply
|
|
187
|
+
into a validated result.
|
|
188
|
+
|
|
189
|
+
## Guarantees you can rely on
|
|
190
|
+
|
|
191
|
+
- **Validity ⟂ your quality.** Whatever you emit is schema-validated and totally
|
|
192
|
+
rendered. A malformed or partial reply degrades gracefully — worst case is a
|
|
193
|
+
rejected op or a defaulted field, never a broken page or an injection.
|
|
194
|
+
- **Non-breaking.** The catalog and manifest shape are versioned; existing
|
|
195
|
+
manifests keep rendering. Grow by adding typed blocks/fields, never escape
|
|
196
|
+
hatches.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## For agents extending the package
|
|
201
|
+
|
|
202
|
+
If you are modifying this repo (not just using it):
|
|
203
|
+
|
|
204
|
+
- A block is a `BlockSpec` (`type`, `description`, `schema`, `css`, `render`,
|
|
205
|
+
optional `island` / `runtime`) in `src/blocks/`, registered in
|
|
206
|
+
`src/registry.ts`.
|
|
207
|
+
- **Definition of done:** typed schema (no raw-HTML field), consumes shared design
|
|
208
|
+
tokens, total render (default every field, escape all text via
|
|
209
|
+
`escapeHtml`/`escapeAttr`, sanitize URLs via `sanitizeUrl`, never throw), a
|
|
210
|
+
single top-level landmark, additive & non-breaking.
|
|
211
|
+
- After changes: `npm run build && npm test && npm run emit:catalog` (the shared
|
|
212
|
+
suite in `src/blocks.test.ts` enforces the DoD for every block), and update the
|
|
213
|
+
closed-vocabulary list in `blocks.test.ts`.
|
|
214
|
+
- Keep everything **consumer-neutral** — no host, backend, or downstream names.
|
package/CATALOG.md
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# @bytesbrains/weblocks — Block Catalog (v0.2.0)
|
|
2
|
+
|
|
3
|
+
The AI composes a `SiteManifest` (`{ meta, design, blocks[] }`) using **only** the block types below, then the engine validates + renders it to static HTML. This file is generated from the code (`npm run emit:catalog`) — do not edit by hand.
|
|
4
|
+
|
|
5
|
+
**Block types:** `app-shell` · `nav` · `announcement-bar` · `sidebar` · `hero` · `hero-app` · `features` · `about` · `rich-text` · `split` · `steps` · `stats` · `services-catalogue` · `pricing` · `logos` · `team` · `gallery` · `carousel` · `video` · `map` · `timeline` · `tabs` · `accordion` · `testimonials` · `faq` · `blog-list` · `blog-post` · `feed` · `contact-form` · `newsletter` · `auth` · `cta` · `social-links` · `contact-details` · `divider` · `spacer` · `footer`
|
|
6
|
+
|
|
7
|
+
## `app-shell`
|
|
8
|
+
|
|
9
|
+
A persistent bottom tab bar for app-like navigation, each tab an optional icon plus a label linking to a view.
|
|
10
|
+
|
|
11
|
+
| field | type | required | notes |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| `brand` | string | | |
|
|
14
|
+
| `tabs` | array | | |
|
|
15
|
+
|
|
16
|
+
## `nav`
|
|
17
|
+
|
|
18
|
+
Top navigation bar: brand/logo text, a row of links, and an optional call-to-action button. Place first.
|
|
19
|
+
|
|
20
|
+
| field | type | required | notes |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| `brand` | string | yes | |
|
|
23
|
+
| `sticky` | boolean | | |
|
|
24
|
+
| `links` | array | | |
|
|
25
|
+
| `cta` | object | | |
|
|
26
|
+
|
|
27
|
+
## `announcement-bar`
|
|
28
|
+
|
|
29
|
+
A dismissible full-width strip for a short promo or notice, with an optional inline link and info/promo/warning tone.
|
|
30
|
+
|
|
31
|
+
| field | type | required | notes |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| `text` | string | yes | |
|
|
34
|
+
| `linkLabel` | string | | |
|
|
35
|
+
| `href` | string | | |
|
|
36
|
+
| `tone` | undefined (info\|promo\|warning) | | |
|
|
37
|
+
| `dismissible` | boolean | | |
|
|
38
|
+
|
|
39
|
+
## `sidebar`
|
|
40
|
+
|
|
41
|
+
A vertical section/drawer navigation for multi-view apps: an optional title over a list of icon-and-label links.
|
|
42
|
+
|
|
43
|
+
| field | type | required | notes |
|
|
44
|
+
|---|---|---|---|
|
|
45
|
+
| `title` | string | | |
|
|
46
|
+
| `links` | array | | |
|
|
47
|
+
|
|
48
|
+
## `hero`
|
|
49
|
+
|
|
50
|
+
Top-of-page banner: a big headline with an optional eyebrow, subheading, and one call-to-action button.
|
|
51
|
+
|
|
52
|
+
| field | type | required | notes |
|
|
53
|
+
|---|---|---|---|
|
|
54
|
+
| `eyebrow` | string | | |
|
|
55
|
+
| `headline` | string | yes | |
|
|
56
|
+
| `subhead` | string | | |
|
|
57
|
+
| `align` | undefined (center\|left) | | |
|
|
58
|
+
| `cta` | object | | |
|
|
59
|
+
|
|
60
|
+
## `hero-app`
|
|
61
|
+
|
|
62
|
+
An app landing hero: a headline and subhead over a primary install button, with a horizontal scroll row of app screenshots.
|
|
63
|
+
|
|
64
|
+
| field | type | required | notes |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| `headline` | string | yes | |
|
|
67
|
+
| `subhead` | string | | |
|
|
68
|
+
| `installLabel` | string | | |
|
|
69
|
+
| `installHref` | string | | |
|
|
70
|
+
| `screenshots` | array | | |
|
|
71
|
+
|
|
72
|
+
## `features`
|
|
73
|
+
|
|
74
|
+
A grid of value propositions, each with an optional icon/emoji, a title, and a short line of text.
|
|
75
|
+
|
|
76
|
+
| field | type | required | notes |
|
|
77
|
+
|---|---|---|---|
|
|
78
|
+
| `title` | string | | |
|
|
79
|
+
| `subtitle` | string | | |
|
|
80
|
+
| `columns` | integer (2\|3\|4) | | |
|
|
81
|
+
| `items` | array | | |
|
|
82
|
+
|
|
83
|
+
## `about`
|
|
84
|
+
|
|
85
|
+
A story/mission section: eyebrow, title, prose body, and an optional side image (choose which side).
|
|
86
|
+
|
|
87
|
+
| field | type | required | notes |
|
|
88
|
+
|---|---|---|---|
|
|
89
|
+
| `eyebrow` | string | | |
|
|
90
|
+
| `title` | string | | |
|
|
91
|
+
| `body` | string | | |
|
|
92
|
+
| `image` | string | | |
|
|
93
|
+
| `imageAlt` | string | | |
|
|
94
|
+
| `imageSide` | undefined (left\|right) | | |
|
|
95
|
+
|
|
96
|
+
## `rich-text`
|
|
97
|
+
|
|
98
|
+
A typed prose section with ordered headings, paragraphs, quotes, and bullet/numbered lists — a safe freeform-content block, never raw HTML.
|
|
99
|
+
|
|
100
|
+
| field | type | required | notes |
|
|
101
|
+
|---|---|---|---|
|
|
102
|
+
| `blocks` | array | | |
|
|
103
|
+
|
|
104
|
+
## `split`
|
|
105
|
+
|
|
106
|
+
Alternating image-and-text rows; the image side flips each row, and rows without an image collapse to centered text.
|
|
107
|
+
|
|
108
|
+
| field | type | required | notes |
|
|
109
|
+
|---|---|---|---|
|
|
110
|
+
| `rows` | array | | |
|
|
111
|
+
|
|
112
|
+
## `steps`
|
|
113
|
+
|
|
114
|
+
A numbered how-it-works / process list: an optional title and subtitle over auto-numbered step cards, each with a title and short text.
|
|
115
|
+
|
|
116
|
+
| field | type | required | notes |
|
|
117
|
+
|---|---|---|---|
|
|
118
|
+
| `title` | string | | |
|
|
119
|
+
| `subtitle` | string | | |
|
|
120
|
+
| `items` | array | | |
|
|
121
|
+
|
|
122
|
+
## `stats`
|
|
123
|
+
|
|
124
|
+
A grid of metric counters: each stat shows an optional prefix/suffix around a big value with a muted label, with an optional count-up animation.
|
|
125
|
+
|
|
126
|
+
| field | type | required | notes |
|
|
127
|
+
|---|---|---|---|
|
|
128
|
+
| `title` | string | | |
|
|
129
|
+
| `columns` | integer (2\|3\|4) | | |
|
|
130
|
+
| `items` | array | | |
|
|
131
|
+
|
|
132
|
+
## `services-catalogue`
|
|
133
|
+
|
|
134
|
+
A grid of services or products, each with a name, optional price, description, and link. Use for "what we offer".
|
|
135
|
+
|
|
136
|
+
| field | type | required | notes |
|
|
137
|
+
|---|---|---|---|
|
|
138
|
+
| `title` | string | | |
|
|
139
|
+
| `subtitle` | string | | |
|
|
140
|
+
| `items` | array | | |
|
|
141
|
+
|
|
142
|
+
## `pricing`
|
|
143
|
+
|
|
144
|
+
A row of pricing/tier cards, each with a name, price, feature list, and a call-to-action button; one plan can be highlighted.
|
|
145
|
+
|
|
146
|
+
| field | type | required | notes |
|
|
147
|
+
|---|---|---|---|
|
|
148
|
+
| `title` | string | | |
|
|
149
|
+
| `subtitle` | string | | |
|
|
150
|
+
| `plans` | array | | |
|
|
151
|
+
|
|
152
|
+
## `logos`
|
|
153
|
+
|
|
154
|
+
A centered strip of client or partner logos (grayscale until hover), each optionally linking out, under a short heading.
|
|
155
|
+
|
|
156
|
+
| field | type | required | notes |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| `title` | string | | |
|
|
159
|
+
| `items` | array | | |
|
|
160
|
+
|
|
161
|
+
## `team`
|
|
162
|
+
|
|
163
|
+
A grid of people cards with photo (or initials), name, role, a short bio, and optional social links.
|
|
164
|
+
|
|
165
|
+
| field | type | required | notes |
|
|
166
|
+
|---|---|---|---|
|
|
167
|
+
| `title` | string | | |
|
|
168
|
+
| `subtitle` | string | | |
|
|
169
|
+
| `columns` | integer (2\|3\|4) | | |
|
|
170
|
+
| `members` | array | | |
|
|
171
|
+
|
|
172
|
+
## `gallery`
|
|
173
|
+
|
|
174
|
+
A responsive image grid (grid/masonry/carousel) with an optional click-to-zoom lightbox. Each item needs a src and alt text.
|
|
175
|
+
|
|
176
|
+
| field | type | required | notes |
|
|
177
|
+
|---|---|---|---|
|
|
178
|
+
| `layout` | undefined (grid\|masonry\|carousel) | | |
|
|
179
|
+
| `columns` | integer (2\|3\|4) | | |
|
|
180
|
+
| `gap` | undefined (sm\|md\|lg) | | |
|
|
181
|
+
| `lightbox` | boolean | | |
|
|
182
|
+
| `items` | array | | |
|
|
183
|
+
|
|
184
|
+
## `carousel`
|
|
185
|
+
|
|
186
|
+
A horizontal, swipeable carousel of image slides with optional captions; scrolls natively and can be enhanced with arrows/autoplay.
|
|
187
|
+
|
|
188
|
+
| field | type | required | notes |
|
|
189
|
+
|---|---|---|---|
|
|
190
|
+
| `title` | string | | |
|
|
191
|
+
| `items` | array | | |
|
|
192
|
+
| `autoplay` | boolean | | |
|
|
193
|
+
|
|
194
|
+
## `video`
|
|
195
|
+
|
|
196
|
+
A responsive embedded video from YouTube or Vimeo (by id or URL) or a self-hosted file, with an optional caption.
|
|
197
|
+
|
|
198
|
+
| field | type | required | notes |
|
|
199
|
+
|---|---|---|---|
|
|
200
|
+
| `provider` | undefined (youtube\|vimeo\|file) | | |
|
|
201
|
+
| `src` | string | yes | |
|
|
202
|
+
| `title` | string | | |
|
|
203
|
+
| `poster` | string | | |
|
|
204
|
+
| `caption` | string | | |
|
|
205
|
+
|
|
206
|
+
## `map`
|
|
207
|
+
|
|
208
|
+
An embedded location map for a place or address query, with configurable zoom and height and a link to the full map.
|
|
209
|
+
|
|
210
|
+
| field | type | required | notes |
|
|
211
|
+
|---|---|---|---|
|
|
212
|
+
| `query` | string | yes | |
|
|
213
|
+
| `zoom` | integer | | |
|
|
214
|
+
| `height` | integer | | |
|
|
215
|
+
| `label` | string | | |
|
|
216
|
+
|
|
217
|
+
## `timeline`
|
|
218
|
+
|
|
219
|
+
A vertical timeline of chronological milestones, each with an optional date, a title, and supporting text.
|
|
220
|
+
|
|
221
|
+
| field | type | required | notes |
|
|
222
|
+
|---|---|---|---|
|
|
223
|
+
| `title` | string | | |
|
|
224
|
+
| `items` | array | | |
|
|
225
|
+
|
|
226
|
+
## `tabs`
|
|
227
|
+
|
|
228
|
+
Tabbed content panels that switch with no JavaScript; each tab has a label and a text panel.
|
|
229
|
+
|
|
230
|
+
| field | type | required | notes |
|
|
231
|
+
|---|---|---|---|
|
|
232
|
+
| `items` | array | | |
|
|
233
|
+
|
|
234
|
+
## `accordion`
|
|
235
|
+
|
|
236
|
+
Generic collapsible disclosure panels (native details/summary, no JavaScript); each panel has a heading and body.
|
|
237
|
+
|
|
238
|
+
| field | type | required | notes |
|
|
239
|
+
|---|---|---|---|
|
|
240
|
+
| `title` | string | | |
|
|
241
|
+
| `items` | array | | |
|
|
242
|
+
|
|
243
|
+
## `testimonials`
|
|
244
|
+
|
|
245
|
+
A grid of customer quotes, each with the quote text and an optional author name and role.
|
|
246
|
+
|
|
247
|
+
| field | type | required | notes |
|
|
248
|
+
|---|---|---|---|
|
|
249
|
+
| `title` | string | | |
|
|
250
|
+
| `items` | array | | |
|
|
251
|
+
|
|
252
|
+
## `faq`
|
|
253
|
+
|
|
254
|
+
An accordion of question/answer pairs. Expands/collapses natively (no JS).
|
|
255
|
+
|
|
256
|
+
| field | type | required | notes |
|
|
257
|
+
|---|---|---|---|
|
|
258
|
+
| `title` | string | | |
|
|
259
|
+
| `items` | array | | |
|
|
260
|
+
|
|
261
|
+
## `blog-list`
|
|
262
|
+
|
|
263
|
+
A post index as a grid of cards, each with an optional cover image, tag, date, title, and excerpt linking to the post.
|
|
264
|
+
|
|
265
|
+
| field | type | required | notes |
|
|
266
|
+
|---|---|---|---|
|
|
267
|
+
| `title` | string | | |
|
|
268
|
+
| `columns` | integer (2\|3) | | |
|
|
269
|
+
| `posts` | array | | |
|
|
270
|
+
|
|
271
|
+
## `blog-post`
|
|
272
|
+
|
|
273
|
+
A single article layout with an optional cover, title, author/date, and a typed body (headings, paragraphs, quotes, bullets).
|
|
274
|
+
|
|
275
|
+
| field | type | required | notes |
|
|
276
|
+
|---|---|---|---|
|
|
277
|
+
| `title` | string | yes | |
|
|
278
|
+
| `date` | string | | |
|
|
279
|
+
| `author` | string | | |
|
|
280
|
+
| `cover` | string | | |
|
|
281
|
+
| `coverAlt` | string | | |
|
|
282
|
+
| `body` | array | | |
|
|
283
|
+
|
|
284
|
+
## `feed`
|
|
285
|
+
|
|
286
|
+
A generic data-driven list of items in a list or card layout, each with a title, optional subtitle/text, badge, image, and link.
|
|
287
|
+
|
|
288
|
+
| field | type | required | notes |
|
|
289
|
+
|---|---|---|---|
|
|
290
|
+
| `title` | string | | |
|
|
291
|
+
| `layout` | undefined (list\|cards) | | |
|
|
292
|
+
| `items` | array | | |
|
|
293
|
+
|
|
294
|
+
## `contact-form`
|
|
295
|
+
|
|
296
|
+
A configurable contact form with typed fields (text, email, tel, textarea, select, checkbox) that posts to a host-provided runtime. No raw HTML.
|
|
297
|
+
|
|
298
|
+
| field | type | required | notes |
|
|
299
|
+
|---|---|---|---|
|
|
300
|
+
| `title` | string | | |
|
|
301
|
+
| `intro` | string | | |
|
|
302
|
+
| `submitLabel` | string | | |
|
|
303
|
+
| `successMessage` | string | | |
|
|
304
|
+
| `fields` | array | | |
|
|
305
|
+
|
|
306
|
+
## `newsletter`
|
|
307
|
+
|
|
308
|
+
An email-capture form that posts to a host-provided runtime; renders inert until a runtime is wired.
|
|
309
|
+
|
|
310
|
+
| field | type | required | notes |
|
|
311
|
+
|---|---|---|---|
|
|
312
|
+
| `title` | string | | |
|
|
313
|
+
| `intro` | string | | |
|
|
314
|
+
| `placeholder` | string | | |
|
|
315
|
+
| `submitLabel` | string | | |
|
|
316
|
+
| `successMessage` | string | | |
|
|
317
|
+
|
|
318
|
+
## `auth`
|
|
319
|
+
|
|
320
|
+
A provider-agnostic sign in / sign up panel: social provider buttons and an optional email form that start auth via a host runtime.
|
|
321
|
+
|
|
322
|
+
| field | type | required | notes |
|
|
323
|
+
|---|---|---|---|
|
|
324
|
+
| `mode` | undefined (signin\|signup) | | |
|
|
325
|
+
| `title` | string | | |
|
|
326
|
+
| `providers` | array | | |
|
|
327
|
+
| `showEmail` | boolean | | |
|
|
328
|
+
|
|
329
|
+
## `cta`
|
|
330
|
+
|
|
331
|
+
A full-width call-to-action band: a headline, optional subheading, and one button. Use near the bottom.
|
|
332
|
+
|
|
333
|
+
| field | type | required | notes |
|
|
334
|
+
|---|---|---|---|
|
|
335
|
+
| `headline` | string | yes | |
|
|
336
|
+
| `subhead` | string | | |
|
|
337
|
+
| `background` | undefined (primary\|accent\|surface) | | |
|
|
338
|
+
| `button` | object | | |
|
|
339
|
+
|
|
340
|
+
## `social-links`
|
|
341
|
+
|
|
342
|
+
A centered row of links to social or external profiles, each an optional icon plus a label.
|
|
343
|
+
|
|
344
|
+
| field | type | required | notes |
|
|
345
|
+
|---|---|---|---|
|
|
346
|
+
| `title` | string | | |
|
|
347
|
+
| `links` | array | | |
|
|
348
|
+
|
|
349
|
+
## `contact-details`
|
|
350
|
+
|
|
351
|
+
A contact info block: address, phone, email, and opening hours (each optional). Read-only — no form.
|
|
352
|
+
|
|
353
|
+
| field | type | required | notes |
|
|
354
|
+
|---|---|---|---|
|
|
355
|
+
| `title` | string | | |
|
|
356
|
+
| `address` | string | | |
|
|
357
|
+
| `phone` | string | | |
|
|
358
|
+
| `email` | string | | |
|
|
359
|
+
| `hours` | string | | |
|
|
360
|
+
|
|
361
|
+
## `divider`
|
|
362
|
+
|
|
363
|
+
A visual section break rendered as a thin line, a dotted rule, or a gradient bar.
|
|
364
|
+
|
|
365
|
+
| field | type | required | notes |
|
|
366
|
+
|---|---|---|---|
|
|
367
|
+
| `style` | undefined (line\|dots\|gradient) | | |
|
|
368
|
+
|
|
369
|
+
## `spacer`
|
|
370
|
+
|
|
371
|
+
Deliberate vertical whitespace between sections, in one of four sizes (sm, md, lg, xl).
|
|
372
|
+
|
|
373
|
+
| field | type | required | notes |
|
|
374
|
+
|---|---|---|---|
|
|
375
|
+
| `size` | undefined (sm\|md\|lg\|xl) | | |
|
|
376
|
+
|
|
377
|
+
## `footer`
|
|
378
|
+
|
|
379
|
+
Page footer: brand, tagline, a row of links, and a copyright line. Place last.
|
|
380
|
+
|
|
381
|
+
| field | type | required | notes |
|
|
382
|
+
|---|---|---|---|
|
|
383
|
+
| `brand` | string | | |
|
|
384
|
+
| `tagline` | string | | |
|
|
385
|
+
| `links` | array | | |
|
|
386
|
+
| `copyright` | string | | |
|
|
387
|
+
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@bytesbrains/weblocks` are documented here. The package
|
|
4
|
+
follows [semantic versioning](https://semver.org): the **block catalog** and the
|
|
5
|
+
**`SiteManifest` shape** are the public contract — additive block/field changes
|
|
6
|
+
are minor, breaking changes to either are major.
|
|
7
|
+
|
|
8
|
+
## 0.2.0 — 2026-07-16
|
|
9
|
+
|
|
10
|
+
Rich, app-like PWAs. Additive and **non-breaking**: every `0.1.0` manifest still
|
|
11
|
+
validates and renders identically.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **27 new blocks** (catalog now 37), all typed + totally-rendered:
|
|
15
|
+
- chrome / app-shell: `app-shell`, `sidebar`, `announcement-bar`, `hero-app`
|
|
16
|
+
- content: `about`, `rich-text`, `split`, `steps`, `stats`, `pricing`,
|
|
17
|
+
`logos`, `team`
|
|
18
|
+
- media: `carousel`, `video`, `map`
|
|
19
|
+
- structured: `timeline`, `tabs`, `accordion`
|
|
20
|
+
- collections: `blog-list`, `blog-post`, `feed`
|
|
21
|
+
- dynamic (powered): `contact-form`, `newsletter`, `auth`
|
|
22
|
+
- rhythm / links: `social-links`, `divider`, `spacer`
|
|
23
|
+
- `rich-text` / `blog-post` use **typed content nodes** (never raw HTML).
|
|
24
|
+
- **Dynamic-block runtime contract** (`runtime.ts`): powered bricks declare
|
|
25
|
+
`runtime.capabilities`; hosts wire them via a `RuntimeAdapter`
|
|
26
|
+
(`NOOP_RUNTIME`, `pathRuntime`, `runtimeNeeds`). The engine bundles no backend.
|
|
27
|
+
- **Array-item edit ops** (`addItem` / `updateItem` / `removeItem` / `moveItem`)
|
|
28
|
+
for surgical, single-item edits within a block's array field.
|
|
29
|
+
- **Theming**: named `DesignTokens` presets (`presets.ts` — `applyPreset`,
|
|
30
|
+
`presetNames`, `getPreset`) and opt-in per-section `setOverrides` (palette
|
|
31
|
+
**plus** `radius` / `spacing`).
|
|
32
|
+
- **Automatic light/dark**: `mode: 'auto'` now emits a
|
|
33
|
+
`@media (prefers-color-scheme: dark)` palette that follows the viewer's OS
|
|
34
|
+
theme; supply an optional `darkPalette` for the dark side (else a built-in
|
|
35
|
+
default). `mode: 'light' | 'dark'` render single-palette as before.
|
|
36
|
+
- **Contrast-safe fills**: derived `--on-primary` / `--on-accent` tokens (via a
|
|
37
|
+
WCAG-luminance `readableOn`) replace hardcoded `#fff` button text, so contrast
|
|
38
|
+
holds on any palette. `readableOn` and `sectionOverrideCss` are exported.
|
|
39
|
+
- **PWA layer** (`pwa.ts`): derive `manifest.webmanifest` + a service worker from
|
|
40
|
+
a `SiteManifest`; optional `pwa` / `seo` manifest fields; `renderSite` emits the
|
|
41
|
+
matching `<head>` tags when opted in.
|
|
42
|
+
- `renderSite(manifest, { runtime })` to supply a host runtime.
|
|
43
|
+
- `VISION.md`; `docs/ARCHITECTURE.md` rewritten to cover only the engine.
|
|
44
|
+
|
|
45
|
+
## 0.1.0 — first shareable release
|
|
46
|
+
|
|
47
|
+
First release packaged for reuse across repositories.
|
|
48
|
+
|
|
49
|
+
- **Block engine**: compose a `SiteManifest` (design tokens + ordered typed
|
|
50
|
+
blocks) → validate (strict) → total-render → one self-contained static HTML
|
|
51
|
+
document. Rendering is a total function (never throws; unknown/invalid blocks
|
|
52
|
+
are skipped).
|
|
53
|
+
- **10 blocks**: `nav`, `hero`, `features`, `services-catalogue`, `gallery`,
|
|
54
|
+
`testimonials`, `faq`, `cta`, `contact-details`, `footer`.
|
|
55
|
+
- **AI contract**: `catalog()` / `catalogPrompt()` expose the block vocabulary as
|
|
56
|
+
JSON Schema (for function-calling) and a compact prompt menu. `generateSite()`
|
|
57
|
+
composes a manifest from a brief via an injected `ModelCall`; `editSite()`
|
|
58
|
+
applies JSON edit ops. Validity is independent of model quality.
|
|
59
|
+
- **Design tokens**: a theme is a `DesignTokens` object (palette + scale/radius/
|
|
60
|
+
spacing/motion); every block styles itself from CSS variables, so one token
|
|
61
|
+
edit restyles the whole site.
|
|
62
|
+
- **Shareable artifacts**: `catalog.json` (the machine-readable contract) and
|
|
63
|
+
`CATALOG.md` are generated on publish (`npm run emit:catalog`).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 bytesbrains
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|