AI Block Schema
Bronbestand:
packages/shared/src/ai/block-schema.tsGebruikt door:apps/api/src/routes/ai.ts(system prompt) + Zod output validatie
Overzicht
Section titled “Overzicht”Het AI Block Schema beschrijft elk block type op een manier die de AI helpt om:
- Het juiste block te kiezen voor een pagina-onderdeel
- Content te genereren die past bij het block-formaat
- Paginastructuur te bepalen (welke blocks in welke volgorde)
De AI genereert alleen content — styling (spacing, background, corners, textColorMode) wordt automatisch toegepast via enrichBlocksWithDefaults().
Block Types voor AI
Section titled “Block Types voor AI”7 block types zijn beschikbaar voor AI-generatie. group is uitgesloten — dat is een editor-container die niet door AI wordt aangemaakt.
Overzichtstabel
Section titled “Overzichtstabel”| Block | Positie | Max/pagina | Past bij | Tone |
|---|---|---|---|---|
| hero | first | 1 | features, text, columns | Impactvol, beknopt |
| features | any | 2 | hero, cta | Scanbaar, bullet-achtig |
| cta | last | 2 | hero, features, faq | Urgent, actiegericht |
| text | any | 3 | hero, columns, cta | Verhalend, informatief |
| faq | any | 1 | cta, text | Behulpzaam, helder |
| columns | any | 2 | hero, cta | Mix tekst + visueel |
| buttons | any | 2 | text, columns | Kort, actiegericht |
Paginastructuur Patronen
Section titled “Paginastructuur Patronen”De AI gebruikt deze patronen als richtlijn (niet als verplichting):
| Pagina-type | Structuur |
|---|---|
| Diensten | hero → features → text → cta |
| Over ons | hero → columns → features → cta |
| Landing | hero → features → faq → cta |
| Contact | hero → columns → faq |
| Brand | hero → text (missie) → text (visie) → features (waarden) → cta |
| Simpel | hero → text → cta |
Per Block Type
Section titled “Per Block Type”Beschrijving: Header sectie met paginatitel, ondertitel en call-to-action buttons.
Wanneer gebruiken: Altijd als eerste block op een pagina. Bevat de hoofdboodschap en primaire CTA.
Wanneer NIET gebruiken: Niet als 2e/3e block. Niet voor tussentekst of secundaire secties.
| Metadata | Waarde |
|---|---|
| Positie | first (altijd bovenaan) |
| Max per pagina | 1 |
| Past bij | features, text, columns |
| Alternatief | Voor alleen tekst zonder buttons: gebruik text block |
Content velden:
| Veld | Type | Richtlijn | Verplicht |
|---|---|---|---|
layout | center | left | Tekstuitlijning | default: center |
title.text | string | 3-8 woorden, krachtig en duidelijk | Ja |
title.tag | h1 | Alleen h1 in hero | default: h1 |
title.size | xl | — | default: xl |
subtitle.text | string | 1-2 zinnen, max 25 woorden | Ja |
buttons.items[].label | string | 2-4 woorden, actiegericht | Ja |
buttons.items[].href | string | URL of # | default: # |
buttons.items[].style | solid | outline | Eerste button solid, tweede outline | default: solid |
Voorbeeld output:
{ "type": "hero", "props": { "layout": "center", "title": { "text": "Uw tuin, ons vakmanschap", "tag": "h1", "size": "xl" }, "subtitle": { "text": "Al 25 jaar creëren wij groene oases in de regio Utrecht." }, "buttons": { "items": [ { "label": "Gratis adviesgesprek", "href": "#contact", "style": "solid" }, { "label": "Bekijk projecten", "href": "#projecten", "style": "outline" } ]} }}Verbeterpunten:
- Afbeelding/video achtergrond: hero kan een background image/video hebben maar de AI genereert dit nog niet (vereist media library koppeling)
- Brand CTA’s: als de
BrandStoryeenprimaryCtabevat, zou de AI die automatisch als eerste button label moeten gebruiken
features
Section titled “features”Beschrijving: Grid van features, diensten of voordelen met iconen.
Wanneer gebruiken: Om meerdere gelijkwaardige items te tonen: diensten, voordelen, kenmerken, teamleden.
Wanneer NIET gebruiken: Niet voor lange tekst. Niet als er maar 1 item is — gebruik dan text block.
| Metadata | Waarde |
|---|---|
| Positie | any |
| Max per pagina | 2 |
| Past bij | hero, cta |
| Alternatief | Voor gedetailleerde tekst per onderwerp: meerdere text blocks |
Content velden:
| Veld | Type | Richtlijn | Verplicht |
|---|---|---|---|
columns | 2 | 3 | 4 | Aantal kolommen | default: 3 |
title.text | string | 2-6 woorden | Ja |
subtitle.text | string | 1 zin, optioneel | Nee |
features.items[].icon | string | Lucide icon naam | default: star |
features.items[].title | string | 1-4 woorden | Ja |
features.items[].description | string | 1-2 zinnen, max 20 woorden | Ja |
Beschikbare iconen: zap, shield, heart, star, clock, users, check, sparkles, leaf, pencil-ruler, phone, mail, map-pin, award, target, lightbulb (en alle andere Lucide iconen)
Voorbeeld output:
{ "type": "features", "props": { "columns": "3", "title": { "text": "Wat wij voor u doen", "tag": "h2", "size": "lg" }, "subtitle": { "text": "Drie specialisaties, één doel." }, "features": { "items": [ { "icon": "leaf", "title": "Tuinonderhoud", "description": "Seizoensgebonden onderhoud zodat uw tuin er het hele jaar verzorgd uitziet." }, { "icon": "pencil-ruler", "title": "Tuinontwerp", "description": "Een doordacht ontwerp dat past bij uw woning en budget." }, { "icon": "award", "title": "Aanleg", "description": "Van bestrating tot beplanting — uw droomtuin van A tot Z." } ]} }}Verbeterpunten:
- Icon matching: AI kiest nu uit een beperkte set iconen. Zou alle ~1500 Lucide iconen moeten kennen
- Columns automatisch: AI zou
columnsmoeten baseren op het aantal items (2 items →2, 3 →3, 4+ →4) - Afbeeldingen per feature: features hebben nu alleen iconen, geen afbeeldingen (toekomstige block-uitbreiding)
Beschrijving: Call-to-action sectie die bezoekers aanzet tot actie.
Wanneer gebruiken: Als afsluiter van een pagina of sectie. Na informatie (features, text, faq) om de bezoeker te converteren.
Wanneer NIET gebruiken: Niet als openingsblock. Niet direct na een andere CTA.
| Metadata | Waarde |
|---|---|
| Positie | last (afsluiter) |
| Max per pagina | 2 |
| Past bij | hero, features, faq |
| Alternatief | Voor alleen buttons: gebruik buttons block |
Content velden:
| Veld | Type | Richtlijn | Verplicht |
|---|---|---|---|
layout | center | side-by-side | — | default: center |
title.text | string | 4-10 woorden, vraag of uitnodiging | Ja |
subtitle.text | string | 1 zin, verlaag de drempel | Ja |
buttons.items[].label | string | 2-4 woorden, actiegericht | Ja |
Verbeterpunten:
- Brand CTA koppeling:
primaryCtaensecondaryCtauit BrandStory automatisch als button labels - Achtergrondkleur: CTA gebruikt nu de default button kleur als achtergrond, AI kan dit niet beïnvloeden
Beschrijving: Vrije tekst sectie met rich HTML content.
Wanneer gebruiken: Voor langere tekst: werkwijze, over ons, missie, toelichting, verhaal.
Wanneer NIET gebruiken: Niet voor opsommingen (gebruik features). Niet voor Q&A (gebruik faq).
| Metadata | Waarde |
|---|---|
| Positie | any |
| Max per pagina | 3 |
| Past bij | hero, columns, cta |
| Alternatief | Voor tekst naast afbeelding: gebruik columns block |
Content velden:
| Veld | Type | Richtlijn | Verplicht |
|---|---|---|---|
maxWidth | prose | md | lg | full | Tekstbreedte | default: prose |
alignment | left | center | — | default: left |
content.html | HTML string | <h2>, <h3>, <p>, <ul>, <li>, <strong>, <em>. Geen <h1>. | Ja |
Verbeterpunten:
- HTML sanitization: AI-gegenereerde HTML wordt nog niet door DOMPurify gehaald voor insert
- Heading hiërarchie: AI moet weten welke headings al op de pagina zijn (h1 in hero → h2 in text)
Beschrijving: Veelgestelde vragen in accordion-formaat.
Wanneer gebruiken: Om bezoekersvragen te beantwoorden en bezwaren weg te nemen.
Wanneer NIET gebruiken: Niet als er minder dan 3 vragen zijn.
| Metadata | Waarde |
|---|---|
| Positie | any |
| Max per pagina | 1 |
| Past bij | cta, text |
| Alternatief | Voor een lijstje punten: gebruik features block |
Content velden:
| Veld | Type | Richtlijn | Verplicht |
|---|---|---|---|
title.text | string | 2-5 woorden | Ja |
items.items[].question | string | Directe vraag vanuit klantperspectief | Ja |
items.items[].answer | HTML string | 1-3 zinnen als <p>, concreet | Ja |
Verbeterpunten:
- Branche-specifieke vragen: AI zou typische vragen per branche moeten kennen (kapper → “Moet ik een afspraak maken?”)
- SEO structured data: FAQ blocks zouden automatisch
FAQPageschema.org markup moeten genereren
columns
Section titled “columns”Beschrijving: Twee kolommen layout met tekst en/of afbeelding.
Wanneer gebruiken: Om tekst naast een afbeelding te tonen, of twee content-blokken naast elkaar.
Wanneer NIET gebruiken: Niet als beide kolommen alleen tekst bevatten.
| Metadata | Waarde |
|---|---|
| Positie | any |
| Max per pagina | 2 |
| Past bij | hero, cta |
| Alternatief | Voor alleen tekst: text block. Voor 3+ items: features block |
Content velden:
| Veld | Type | Richtlijn | Verplicht |
|---|---|---|---|
left.type | text | image | Type linkerkolom | default: text |
left.text.html | HTML string | Max 2-3 alinea’s met <h2> kop | Als type=text |
right.type | text | image | Type rechterkolom | default: image |
right.image.alt | string | Beschrijvende alt-tekst | Als type=image |
Verbeterpunten:
- Afbeelding selectie: AI kan nu geen afbeeldingen uit de media library koppelen — genereert lege
url - Grid layout: AI kan de kolomverhoudingen niet beïnvloeden (default 50/50)
- Meer dan 2 kolommen: huidige block ondersteunt alleen 2 kolommen
buttons
Section titled “buttons”Beschrijving: Standalone buttons zonder omringende tekst.
Wanneer gebruiken: Voor navigatie-links midden op de pagina, los van hero of CTA.
Wanneer NIET gebruiken: Niet als de CTA al buttons heeft.
| Metadata | Waarde |
|---|---|
| Positie | any |
| Max per pagina | 2 |
| Past bij | text, columns |
| Alternatief | Voor tekst + buttons: gebruik cta block |
Content velden:
| Veld | Type | Richtlijn | Verplicht |
|---|---|---|---|
alignment | left | center | right | — | default: center |
buttons.items[].label | string | 2-4 woorden, actiegericht | Ja |
Zod Validatie
Section titled “Zod Validatie”AI output wordt gevalideerd via een Zod discriminated union per block type:
import { aiBlockSchema, aiGeneratePageSchema } from '@beam/shared/ai'
// Valideer een enkel blockconst result = aiBlockSchema.safeParse(block)
// Valideer een volledige pagina-responseconst pageResult = aiGeneratePageSchema.safeParse(response)Bij validatiefouten: 1x retry met foutmelding als feedback aan Claude. Na 2e fout: 502 naar client.
Enrichment
Section titled “Enrichment”Na validatie voegt enrichBlocksWithDefaults() automatisch toe:
id(viacrypto.randomUUID())spacing(default:{ top: 'medium', bottom: 'medium' })background(default:{ type: 'color', color: '#ffffff' })corners(default:{ top: false, bottom: false })textColorMode(default:'auto')color+colorToken: 'primary'op buttons- Unieke
id’s op geneste items (features, FAQ items, buttons)
Globale Verbeterpunten
Section titled “Globale Verbeterpunten”Hoge Prioriteit
Section titled “Hoge Prioriteit”- Media library koppeling — AI kan nu geen afbeeldingen selecteren. Vereist: media items met AI-beschrijvingen, matching algoritme
- Brand CTA automatisch —
primaryCtaensecondaryCtauit BrandStory als default button labels - Full page generatie — Huidige PoC genereert alleen hero varianten. Block schema + Zod is klaar voor full page
Medium Prioriteit
Section titled “Medium Prioriteit”- Per-block regeneratie — ✨ knop per block in editor, hergebruikt zelfde AI service
- Content kwaliteitscheck — Lorem ipsum detectie, cliché check, heading hiërarchie, lege strings
- HTML sanitization — DOMPurify op AI-gegenereerde HTML voor text/faq blocks
Lage Prioriteit
Section titled “Lage Prioriteit”- Lucide icon set — Volledige icon-set beschikbaar maken voor features block
- Columns grid layout — AI kolomverhoudingen laten bepalen
- SEO structured data — FAQ → FAQPage schema.org, breadcrumbs
- Conversatie modus — Chat-interface voor stapsgewijze pagina-opbouw