/**
* Proto-Blocks Fields Knowledge Module
* Complete documentation for all field types
*/
export function getFieldsKnowledge() {
return `# Proto-Blocks Field Types: Complete Reference
Fields are the editable regions in your block templates. They allow users to modify content directly in the WordPress block editor.
---
## Field Types Overview
| Type | Purpose | Returns | Editor Component |
|------|---------|---------|------------------|
| \`text\` | Single-line plain text | \`string\` | Inline text editing |
| \`wysiwyg\` | Rich text with formatting | \`string\` (HTML) | RichText editor |
| \`image\` | Media library picker | \`object\` | Image placeholder/preview |
| \`link\` | URL with text/options | \`object\` | Link popover |
| \`repeater\` | Repeatable group | \`array\` | Sortable list |
| \`innerblocks\` | Nested blocks | \`string\` (HTML) | Block inserter |
---
## Text Field
Single-line plain text editing. Perfect for headings, labels, and short text content.
### Configuration
\`\`\`json
{
"fields": {
"title": {
"type": "text",
"tagName": "h2"
},
"subtitle": {
"type": "text",
"tagName": "p"
}
}
}
\`\`\`
### Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`type\` | string | — | Must be \`"text"\` |
| \`tagName\` | string | \`"span"\` | HTML tag for the editable element |
### Template Usage
\`\`\`php
<?php
$title = $attributes['title'] ?? 'Default Title';
?>
<h2 data-proto-field="title"><?php echo esc_html($title); ?></h2>
\`\`\`
### Important Notes
- The \`tagName\` in block.json should match your template's HTML tag
- Always escape output with \`esc_html()\`
- Keep the element visible even when empty (for clicking in editor)
---
## WYSIWYG Field
Rich text editing with formatting options (bold, italic, links, lists).
### Configuration
\`\`\`json
{
"fields": {
"content": {
"type": "wysiwyg"
},
"bio": {
"type": "wysiwyg"
}
}
}
\`\`\`
### Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`type\` | string | — | Must be \`"wysiwyg"\` |
### Template Usage
\`\`\`php
<?php
$content = $attributes['content'] ?? '';
?>
<div class="content" data-proto-field="content">
<?php echo wp_kses_post($content); ?>
</div>
\`\`\`
### Important Notes
- Always use \`wp_kses_post()\` to safely output HTML
- The WYSIWYG editor provides: bold, italic, links, strikethrough
- Content is stored as HTML strings
---
## Image Field
Image selection from the WordPress Media Library.
### Configuration
\`\`\`json
{
"fields": {
"featuredImage": {
"type": "image",
"sizes": ["medium", "large", "full"]
},
"avatar": {
"type": "image",
"sizes": ["thumbnail", "medium"]
}
}
}
\`\`\`
### Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`type\` | string | — | Must be \`"image"\` |
| \`sizes\` | array | \`["full"]\` | Available WordPress image sizes |
### Return Value
\`\`\`javascript
{
"id": 123, // Attachment ID
"url": "https://...", // Image URL
"alt": "Alt text", // Alt text
"size": "large" // Selected size
}
\`\`\`
### Template Usage
\`\`\`php
<?php
$image = $attributes['featuredImage'] ?? null;
?>
<?php if ($image && !empty($image['url'])) : ?>
<figure data-proto-field="featuredImage">
<img
src="<?php echo esc_url($image['url']); ?>"
alt="<?php echo esc_attr($image['alt'] ?? ''); ?>"
class="featured-image"
/>
</figure>
<?php else : ?>
<figure data-proto-field="featuredImage" class="image-placeholder">
<span>Click to select image</span>
</figure>
<?php endif; ?>
\`\`\`
### Important Notes
- Always check if the image exists before rendering
- Provide a placeholder for empty state (clickable in editor)
- Use \`esc_url()\` for URLs and \`esc_attr()\` for alt text
- The \`sizes\` option controls which sizes appear in the dropdown
---
## Link Field
Link selection with URL, text, target, and rel attributes.
### Configuration
\`\`\`json
{
"fields": {
"ctaLink": {
"type": "link",
"tagName": "a"
},
"readMore": {
"type": "link"
}
}
}
\`\`\`
### Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`type\` | string | — | Must be \`"link"\` |
| \`tagName\` | string | \`"a"\` | HTML tag (usually \`"a"\`) |
### Return Value
\`\`\`javascript
{
"url": "https://example.com",
"text": "Click here",
"target": "_blank", // or ""
"rel": "noopener noreferrer" // or ""
}
\`\`\`
### Template Usage
\`\`\`php
<?php
$link = $attributes['ctaLink'] ?? null;
$has_link = $link && !empty($link['url']);
?>
<?php if ($has_link) : ?>
<a
href="<?php echo esc_url($link['url']); ?>"
<?php echo $link['target'] ? 'target="' . esc_attr($link['target']) . '"' : ''; ?>
<?php echo $link['rel'] ? 'rel="' . esc_attr($link['rel']) . '"' : ''; ?>
class="cta-button"
data-proto-field="ctaLink"
>
<?php echo esc_html($link['text'] ?? 'Learn More'); ?>
</a>
<?php else : ?>
<a href="#" class="cta-button cta-button--placeholder" data-proto-field="ctaLink">
Add link
</a>
<?php endif; ?>
\`\`\`
### Important Notes
- Always escape URL with \`esc_url()\`
- Provide a placeholder anchor for empty state
- The link popover allows setting target and rel attributes
---
## Repeater Field
Repeatable groups of fields with drag-and-drop reordering.
### Configuration
\`\`\`json
{
"fields": {
"items": {
"type": "repeater",
"min": 1,
"max": 10,
"itemLabel": "title",
"collapsible": true,
"fields": {
"title": {
"type": "text",
"tagName": "h3"
},
"description": {
"type": "wysiwyg"
},
"icon": {
"type": "image",
"sizes": ["thumbnail"]
}
}
}
}
}
\`\`\`
### Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`type\` | string | — | Must be \`"repeater"\` |
| \`fields\` | object | — | Nested field definitions |
| \`min\` | number | \`0\` | Minimum items required |
| \`max\` | number | \`∞\` | Maximum items allowed |
| \`itemLabel\` | string | — | Field to use as item label in editor |
| \`collapsible\` | boolean | \`false\` | Allow collapsing items |
| \`allowAdd\` | boolean | \`true\` | Allow adding new items |
| \`allowRemove\` | boolean | \`true\` | Allow removing items |
| \`allowReorder\` | boolean | \`true\` | Allow drag-drop reordering |
| \`allowDuplicate\` | boolean | \`true\` | Allow duplicating items |
### Return Value
\`\`\`javascript
[
{
"title": "First Item",
"description": "<p>Item description</p>",
"icon": { "id": 1, "url": "...", "alt": "..." }
},
{
"title": "Second Item",
"description": "<p>Another description</p>",
"icon": null
}
]
\`\`\`
### Template Usage
\`\`\`php
<?php
$items = $attributes['items'] ?? [];
// Provide default items for empty state in editor
if (empty($items)) {
$items = [
['title' => 'Item 1', 'description' => '', 'icon' => null],
['title' => 'Item 2', 'description' => '', 'icon' => null],
];
}
?>
<ul class="feature-list" data-proto-repeater="items">
<?php foreach ($items as $item) : ?>
<li class="feature-item" data-proto-repeater-item>
<?php if (!empty($item['icon']['url'])) : ?>
<img
src="<?php echo esc_url($item['icon']['url']); ?>"
alt="<?php echo esc_attr($item['icon']['alt'] ?? ''); ?>"
data-proto-field="icon"
/>
<?php else : ?>
<div data-proto-field="icon" class="icon-placeholder"></div>
<?php endif; ?>
<h3 data-proto-field="title">
<?php echo esc_html($item['title'] ?? ''); ?>
</h3>
<div data-proto-field="description">
<?php echo wp_kses_post($item['description'] ?? ''); ?>
</div>
</li>
<?php endforeach; ?>
</ul>
\`\`\`
### Important Notes
1. **Container**: Use \`data-proto-repeater="fieldName"\` on the parent element
2. **Items**: Use \`data-proto-repeater-item\` on each repeatable item
3. **Nested Fields**: Use \`data-proto-field="fieldName"\` inside items
4. **Default Items**: Provide placeholder items for empty state in editor
5. **Item Labels**: Use \`itemLabel\` to show meaningful labels in the editor sidebar
### Nested Repeaters
Repeaters can contain other repeaters:
\`\`\`json
{
"fields": {
"sections": {
"type": "repeater",
"fields": {
"title": { "type": "text" },
"items": {
"type": "repeater",
"fields": {
"name": { "type": "text" }
}
}
}
}
}
}
\`\`\`
---
## InnerBlocks Field
Allows nesting other WordPress blocks inside your block.
### Configuration
\`\`\`json
{
"fields": {
"content": {
"type": "innerblocks",
"allowedBlocks": [
"core/paragraph",
"core/heading",
"core/image",
"core/button",
"core/list"
],
"template": [
["core/heading", { "level": 2, "placeholder": "Enter title..." }],
["core/paragraph", { "placeholder": "Enter content..." }]
],
"templateLock": false,
"orientation": "vertical"
}
}
}
\`\`\`
### Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`type\` | string | — | Must be \`"innerblocks"\` |
| \`allowedBlocks\` | array | all | Blocks allowed inside |
| \`template\` | array | \`[]\` | Default block template |
| \`templateLock\` | string/boolean | \`false\` | Lock mode: \`"all"\`, \`"insert"\`, \`"contentOnly"\`, \`false\` |
| \`orientation\` | string | \`"vertical"\` | Layout: \`"vertical"\` or \`"horizontal"\` |
### Template Locks
| Value | Description |
|-------|-------------|
| \`false\` | No restrictions |
| \`"all"\` | Cannot add, remove, or move blocks |
| \`"insert"\` | Cannot add or remove (can move) |
| \`"contentOnly"\` | Can only edit content |
### Template Usage
\`\`\`php
<?php
$wrapper_attributes = get_block_wrapper_attributes([
'class' => 'hero-section',
]);
?>
<section <?php echo $wrapper_attributes; ?>>
<div class="hero-content" data-proto-inner-blocks>
<?php echo $content; ?>
</div>
</section>
\`\`\`
### Important Notes
1. **Container**: Use \`data-proto-inner-blocks\` on the container element
2. **Content Variable**: Use \`$content\` variable to output inner blocks
3. **One Per Block**: You can only have one InnerBlocks field per block
4. **Template**: Define default blocks that appear when block is inserted
5. **Allowed Blocks**: Restrict which blocks can be added inside
### Example: Hero with Inner Blocks
\`\`\`json
{
"name": "proto-blocks/hero",
"protoBlocks": {
"fields": {
"backgroundImage": {
"type": "image"
},
"innerContent": {
"type": "innerblocks",
"allowedBlocks": [
"core/heading",
"core/paragraph",
"core/buttons"
],
"template": [
["core/heading", { "level": 1, "placeholder": "Hero Title" }],
["core/paragraph", { "placeholder": "Hero description..." }],
["core/buttons", {}, [
["core/button", { "text": "Get Started" }]
]]
]
}
}
}
}
\`\`\`
\`\`\`php
<?php
$bg = $attributes['backgroundImage'] ?? null;
$bg_style = $bg ? "background-image: url('" . esc_url($bg['url']) . "');" : '';
$wrapper_attributes = get_block_wrapper_attributes([
'class' => 'hero',
'style' => $bg_style,
]);
?>
<section <?php echo $wrapper_attributes; ?>>
<div class="hero__overlay"></div>
<div class="hero__content" data-proto-inner-blocks>
<?php echo $content; ?>
</div>
</section>
\`\`\`
---
## Combining Fields
A complete block often uses multiple field types together:
\`\`\`json
{
"protoBlocks": {
"fields": {
"image": {
"type": "image",
"sizes": ["medium", "large"]
},
"title": {
"type": "text",
"tagName": "h3"
},
"description": {
"type": "wysiwyg"
},
"link": {
"type": "link"
},
"features": {
"type": "repeater",
"max": 5,
"fields": {
"text": { "type": "text" }
}
}
}
}
}
\`\`\`
---
## Best Practices
1. **Provide Defaults**: Always use null coalescing for default values
2. **Escape Output**: Match escaping to content type (\`esc_html\`, \`wp_kses_post\`, \`esc_url\`)
3. **Empty State**: Keep elements visible even when empty for editor clicking
4. **Semantic HTML**: Use appropriate HTML tags that match your content
5. **Accessibility**: Include alt text for images, proper heading hierarchy
6. **Placeholder Content**: Provide meaningful placeholder text in the editor
`;
}
export function getFieldDetails(fieldType) {
const details = {
text: `# Text Field - Detailed Documentation
The text field provides single-line plain text editing, perfect for headings, labels, and short text content.
## Configuration
\`\`\`json
{
"fields": {
"title": {
"type": "text",
"tagName": "h2"
}
}
}
\`\`\`
## Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`type\` | string | — | Must be \`"text"\` |
| \`tagName\` | string | \`"span"\` | HTML tag for the editable element |
## Common Tag Names
- \`h1\`, \`h2\`, \`h3\`, \`h4\`, \`h5\`, \`h6\` - Headings
- \`p\` - Paragraphs
- \`span\` - Inline text
- \`strong\`, \`em\` - Emphasized text
- \`label\` - Form labels
- \`figcaption\` - Figure captions
## Template Pattern
\`\`\`php
<?php
$title = $attributes['title'] ?? 'Default Title';
?>
<h2 class="block__title" data-proto-field="title">
<?php echo esc_html($title); ?>
</h2>
\`\`\`
## Multiple Text Fields
\`\`\`json
{
"fields": {
"eyebrow": { "type": "text", "tagName": "span" },
"title": { "type": "text", "tagName": "h2" },
"subtitle": { "type": "text", "tagName": "p" }
}
}
\`\`\`
\`\`\`php
<header class="block__header">
<span class="eyebrow" data-proto-field="eyebrow">
<?php echo esc_html($attributes['eyebrow'] ?? ''); ?>
</span>
<h2 class="title" data-proto-field="title">
<?php echo esc_html($attributes['title'] ?? 'Title'); ?>
</h2>
<p class="subtitle" data-proto-field="subtitle">
<?php echo esc_html($attributes['subtitle'] ?? ''); ?>
</p>
</header>
\`\`\`
## Best Practices
1. **Match tagName to HTML**: The tagName should match your template's tag
2. **Always escape**: Use \`esc_html()\` for text content
3. **Provide defaults**: Empty text is valid, provide fallback if needed
4. **Keep visible**: Even empty, the element should be clickable
5. **Semantic HTML**: Use appropriate heading levels (h1 → h6)
`,
wysiwyg: `# WYSIWYG Field - Detailed Documentation
The WYSIWYG (What You See Is What You Get) field provides rich text editing with formatting options.
## Configuration
\`\`\`json
{
"fields": {
"content": {
"type": "wysiwyg"
}
}
}
\`\`\`
## Available Formatting
The WYSIWYG editor provides:
- **Bold** (Ctrl+B)
- *Italic* (Ctrl+I)
- ~~Strikethrough~~
- Links (Ctrl+K)
- Inline code
## Template Pattern
\`\`\`php
<?php
$content = $attributes['content'] ?? '';
?>
<div class="block__content" data-proto-field="content">
<?php echo wp_kses_post($content); ?>
</div>
\`\`\`
## Output Format
The content is stored and returned as HTML:
\`\`\`html
<p>This is <strong>bold</strong> and <em>italic</em> text.</p>
<p>Here is a <a href="https://example.com">link</a>.</p>
\`\`\`
## Escaping with wp_kses_post
\`wp_kses_post()\` allows safe HTML tags:
- \`<a>\`, \`<p>\`, \`<br>\`
- \`<strong>\`, \`<em>\`, \`<b>\`, \`<i>\`
- \`<ul>\`, \`<ol>\`, \`<li>\`
- \`<blockquote>\`, \`<code>\`
- And more standard post content tags
## Multiple WYSIWYG Fields
\`\`\`json
{
"fields": {
"introduction": { "type": "wysiwyg" },
"mainContent": { "type": "wysiwyg" },
"conclusion": { "type": "wysiwyg" }
}
}
\`\`\`
## Styling WYSIWYG Content
Target nested elements in CSS:
\`\`\`css
.block__content p {
margin-bottom: 1em;
}
.block__content a {
color: var(--primary-color);
text-decoration: underline;
}
.block__content strong {
font-weight: 600;
}
\`\`\`
## Best Practices
1. **Use wp_kses_post**: Never use raw output or \`esc_html\` for WYSIWYG
2. **Style nested elements**: The content will contain various HTML tags
3. **Provide container**: Wrap in a div/section for styling purposes
4. **Empty state**: Empty WYSIWYG fields should still be clickable
`,
image: `# Image Field - Detailed Documentation
The image field provides a media library picker for selecting images.
## Configuration
\`\`\`json
{
"fields": {
"featuredImage": {
"type": "image",
"sizes": ["thumbnail", "medium", "large", "full"]
}
}
}
\`\`\`
## Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`type\` | string | — | Must be \`"image"\` |
| \`sizes\` | array | \`["full"]\` | Available image sizes in dropdown |
## WordPress Image Sizes
| Size | Dimensions | Use Case |
|------|------------|----------|
| \`thumbnail\` | 150×150 | Avatars, icons |
| \`medium\` | 300×300 max | Cards, thumbnails |
| \`medium_large\` | 768×0 | Responsive images |
| \`large\` | 1024×1024 max | Featured images |
| \`full\` | Original | Hero backgrounds |
## Return Value
\`\`\`javascript
{
"id": 123, // WordPress attachment ID
"url": "https://site.com/wp-content/uploads/2024/01/image.jpg",
"alt": "Image description", // Alt text from media library
"size": "large" // Selected size
}
\`\`\`
## Template Pattern
\`\`\`php
<?php
$image = $attributes['featuredImage'] ?? null;
$has_image = $image && !empty($image['url']);
?>
<?php if ($has_image) : ?>
<figure class="block__image" data-proto-field="featuredImage">
<img
src="<?php echo esc_url($image['url']); ?>"
alt="<?php echo esc_attr($image['alt'] ?? ''); ?>"
loading="lazy"
/>
</figure>
<?php else : ?>
<figure class="block__image block__image--empty" data-proto-field="featuredImage">
<span class="placeholder">Click to add image</span>
</figure>
<?php endif; ?>
\`\`\`
## Background Image Pattern
\`\`\`php
<?php
$bg = $attributes['backgroundImage'] ?? null;
$bg_style = $bg ? "background-image: url('" . esc_url($bg['url']) . "');" : '';
?>
<div
class="hero"
style="<?php echo esc_attr($bg_style); ?>"
data-proto-field="backgroundImage"
>
<!-- Content -->
</div>
\`\`\`
## Responsive Images with srcset
\`\`\`php
<?php
$image = $attributes['image'] ?? null;
if ($image && $image['id']) {
$srcset = wp_get_attachment_image_srcset($image['id'], $image['size'] ?? 'large');
$sizes = wp_get_attachment_image_sizes($image['id'], $image['size'] ?? 'large');
}
?>
<?php if ($image && !empty($image['url'])) : ?>
<img
src="<?php echo esc_url($image['url']); ?>"
<?php if (!empty($srcset)) : ?>
srcset="<?php echo esc_attr($srcset); ?>"
sizes="<?php echo esc_attr($sizes); ?>"
<?php endif; ?>
alt="<?php echo esc_attr($image['alt'] ?? ''); ?>"
loading="lazy"
data-proto-field="image"
/>
<?php endif; ?>
\`\`\`
## Styling Placeholder State
\`\`\`css
.block__image--empty {
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
background: #f0f0f0;
border: 2px dashed #ccc;
cursor: pointer;
}
.block__image--empty .placeholder {
color: #666;
font-size: 14px;
}
\`\`\`
## Best Practices
1. **Always check existence**: Images can be null or empty
2. **Provide placeholder**: Show clickable area when no image
3. **Use lazy loading**: Add \`loading="lazy"\` for performance
4. **Escape properly**: \`esc_url()\` for src, \`esc_attr()\` for alt
5. **Consider srcset**: Use WordPress functions for responsive images
6. **Size appropriately**: Choose sizes based on display context
`,
link: `# Link Field - Detailed Documentation
The link field provides a link selector with URL, text, target, and rel options.
## Configuration
\`\`\`json
{
"fields": {
"ctaLink": {
"type": "link",
"tagName": "a"
}
}
}
\`\`\`
## Return Value
\`\`\`javascript
{
"url": "https://example.com/page",
"text": "Learn More",
"target": "_blank", // "_blank" or ""
"rel": "noopener noreferrer" // Security attributes or ""
}
\`\`\`
## Template Pattern
\`\`\`php
<?php
$link = $attributes['ctaLink'] ?? null;
$has_link = $link && !empty($link['url']);
?>
<?php if ($has_link) : ?>
<a
href="<?php echo esc_url($link['url']); ?>"
class="button"
data-proto-field="ctaLink"
<?php if (!empty($link['target'])) : ?>
target="<?php echo esc_attr($link['target']); ?>"
<?php endif; ?>
<?php if (!empty($link['rel'])) : ?>
rel="<?php echo esc_attr($link['rel']); ?>"
<?php endif; ?>
>
<?php echo esc_html($link['text'] ?? 'Click Here'); ?>
</a>
<?php else : ?>
<a href="#" class="button button--placeholder" data-proto-field="ctaLink">
Add Link
</a>
<?php endif; ?>
\`\`\`
## Button Link Pattern
\`\`\`php
<?php
$link = $attributes['buttonLink'] ?? null;
?>
<a
href="<?php echo esc_url($link['url'] ?? '#'); ?>"
class="btn btn-primary"
data-proto-field="buttonLink"
<?php echo !empty($link['target']) ? 'target="' . esc_attr($link['target']) . '"' : ''; ?>
<?php echo !empty($link['rel']) ? 'rel="' . esc_attr($link['rel']) . '"' : ''; ?>
>
<?php echo esc_html($link['text'] ?? 'Get Started'); ?>
</a>
\`\`\`
## Multiple Links
\`\`\`json
{
"fields": {
"primaryLink": { "type": "link" },
"secondaryLink": { "type": "link" }
}
}
\`\`\`
\`\`\`php
<div class="button-group">
<a href="<?php echo esc_url($attributes['primaryLink']['url'] ?? '#'); ?>"
class="btn btn-primary"
data-proto-field="primaryLink">
<?php echo esc_html($attributes['primaryLink']['text'] ?? 'Primary'); ?>
</a>
<a href="<?php echo esc_url($attributes['secondaryLink']['url'] ?? '#'); ?>"
class="btn btn-secondary"
data-proto-field="secondaryLink">
<?php echo esc_html($attributes['secondaryLink']['text'] ?? 'Secondary'); ?>
</a>
</div>
\`\`\`
## Link Security
The link popover automatically handles:
- \`target="_blank"\` when "Open in new tab" is checked
- \`rel="noopener noreferrer"\` for security with external links
- Internal vs external link detection
## Best Practices
1. **Always escape URL**: Use \`esc_url()\` for href
2. **Provide placeholder**: Show clickable element when empty
3. **Handle empty state**: Check for both null and empty URL
4. **Security attributes**: Always output rel when target is provided
5. **Accessible text**: Ensure link text is descriptive
`,
repeater: `# Repeater Field - Detailed Documentation
The repeater field allows creating repeatable groups of fields with drag-and-drop reordering.
## Configuration
\`\`\`json
{
"fields": {
"features": {
"type": "repeater",
"min": 1,
"max": 6,
"itemLabel": "title",
"collapsible": true,
"fields": {
"icon": {
"type": "image",
"sizes": ["thumbnail"]
},
"title": {
"type": "text",
"tagName": "h3"
},
"description": {
"type": "wysiwyg"
}
}
}
}
}
\`\`\`
## Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`type\` | string | — | Must be \`"repeater"\` |
| \`fields\` | object | — | Nested field definitions |
| \`min\` | number | \`0\` | Minimum required items |
| \`max\` | number | \`∞\` | Maximum allowed items |
| \`itemLabel\` | string | — | Field name for item labels |
| \`collapsible\` | boolean | \`false\` | Allow collapsing items |
| \`allowAdd\` | boolean | \`true\` | Show add button |
| \`allowRemove\` | boolean | \`true\` | Show remove button |
| \`allowReorder\` | boolean | \`true\` | Enable drag-drop |
| \`allowDuplicate\` | boolean | \`true\` | Show duplicate button |
## Return Value
\`\`\`javascript
[
{
"icon": { "id": 1, "url": "...", "alt": "..." },
"title": "Feature One",
"description": "<p>Description text</p>"
},
{
"icon": { "id": 2, "url": "...", "alt": "..." },
"title": "Feature Two",
"description": "<p>Another description</p>"
}
]
\`\`\`
## Template Pattern
\`\`\`php
<?php
$features = $attributes['features'] ?? [];
// Default items for empty editor state
if (empty($features)) {
$features = [
['icon' => null, 'title' => 'Feature 1', 'description' => ''],
['icon' => null, 'title' => 'Feature 2', 'description' => ''],
['icon' => null, 'title' => 'Feature 3', 'description' => ''],
];
}
$wrapper_attributes = get_block_wrapper_attributes([
'class' => 'features-grid',
]);
?>
<section <?php echo $wrapper_attributes; ?>>
<div class="features-grid__items" data-proto-repeater="features">
<?php foreach ($features as $feature) : ?>
<article class="feature-card" data-proto-repeater-item>
<?php if (!empty($feature['icon']['url'])) : ?>
<img
src="<?php echo esc_url($feature['icon']['url']); ?>"
alt="<?php echo esc_attr($feature['icon']['alt'] ?? ''); ?>"
class="feature-card__icon"
data-proto-field="icon"
/>
<?php else : ?>
<div class="feature-card__icon-placeholder" data-proto-field="icon">
<span>+</span>
</div>
<?php endif; ?>
<h3 class="feature-card__title" data-proto-field="title">
<?php echo esc_html($feature['title'] ?? ''); ?>
</h3>
<div class="feature-card__description" data-proto-field="description">
<?php echo wp_kses_post($feature['description'] ?? ''); ?>
</div>
</article>
<?php endforeach; ?>
</div>
</section>
\`\`\`
## Required Attributes
1. **Container**: \`data-proto-repeater="fieldName"\`
- Goes on the parent element containing all items
- The value must match the field name in block.json
2. **Items**: \`data-proto-repeater-item\`
- Goes on each repeatable item element
- No value needed, just the attribute
3. **Nested Fields**: \`data-proto-field="fieldName"\`
- Goes on editable elements within items
- Value matches the nested field name
## Nested Repeaters
\`\`\`json
{
"fields": {
"faqCategories": {
"type": "repeater",
"itemLabel": "categoryName",
"fields": {
"categoryName": { "type": "text" },
"questions": {
"type": "repeater",
"itemLabel": "question",
"fields": {
"question": { "type": "text" },
"answer": { "type": "wysiwyg" }
}
}
}
}
}
}
\`\`\`
\`\`\`php
<div data-proto-repeater="faqCategories">
<?php foreach ($attributes['faqCategories'] ?? [] as $category) : ?>
<section data-proto-repeater-item>
<h2 data-proto-field="categoryName">
<?php echo esc_html($category['categoryName'] ?? ''); ?>
</h2>
<dl data-proto-repeater="questions">
<?php foreach ($category['questions'] ?? [] as $qa) : ?>
<div data-proto-repeater-item>
<dt data-proto-field="question">
<?php echo esc_html($qa['question'] ?? ''); ?>
</dt>
<dd data-proto-field="answer">
<?php echo wp_kses_post($qa['answer'] ?? ''); ?>
</dd>
</div>
<?php endforeach; ?>
</dl>
</section>
<?php endforeach; ?>
</div>
\`\`\`
## Styling Repeaters
\`\`\`css
/* Grid layout for items */
.features-grid__items {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
}
/* Individual item styling */
.feature-card {
padding: 1.5rem;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* Placeholder state */
.feature-card__icon-placeholder {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
background: #f0f0f0;
border-radius: 8px;
cursor: pointer;
}
\`\`\`
## Best Practices
1. **Default Items**: Provide placeholder items for empty state
2. **Item Labels**: Use \`itemLabel\` for meaningful sidebar labels
3. **Min/Max**: Set reasonable limits to prevent content overload
4. **Collapsible**: Enable for items with multiple fields
5. **Empty Checks**: Handle empty arrays and null values
6. **Consistent Structure**: Always maintain the same HTML structure per item
`,
innerblocks: `# InnerBlocks Field - Detailed Documentation
The innerblocks field allows nesting other WordPress blocks inside your block.
## Configuration
\`\`\`json
{
"fields": {
"content": {
"type": "innerblocks",
"allowedBlocks": [
"core/paragraph",
"core/heading",
"core/image",
"core/button",
"core/buttons",
"core/list",
"core/quote"
],
"template": [
["core/heading", { "level": 2, "placeholder": "Section Title" }],
["core/paragraph", { "placeholder": "Add your content here..." }]
],
"templateLock": false,
"orientation": "vertical"
}
}
}
\`\`\`
## Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`type\` | string | — | Must be \`"innerblocks"\` |
| \`allowedBlocks\` | array | all blocks | Whitelist of allowed block names |
| \`template\` | array | \`[]\` | Default blocks when inserted |
| \`templateLock\` | string/bool | \`false\` | Lock mode |
| \`orientation\` | string | \`"vertical"\` | Layout direction |
## Template Lock Values
| Value | Effect |
|-------|--------|
| \`false\` | No restrictions (default) |
| \`"all"\` | Cannot add, remove, or move blocks |
| \`"insert"\` | Cannot add or remove, but can move |
| \`"contentOnly"\` | Can only edit existing content |
## Common Allowed Blocks
\`\`\`javascript
// Text content
"core/paragraph"
"core/heading"
"core/list"
"core/quote"
// Media
"core/image"
"core/video"
"core/gallery"
// Layout
"core/group"
"core/columns"
"core/spacer"
// Interactive
"core/button"
"core/buttons"
// Other Proto-Blocks
"proto-blocks/card"
"proto-blocks/testimonial"
\`\`\`
## Template Pattern
\`\`\`php
<?php
$wrapper_attributes = get_block_wrapper_attributes([
'class' => 'content-section',
]);
?>
<section <?php echo $wrapper_attributes; ?>>
<div class="content-section__inner" data-proto-inner-blocks>
<?php echo $content; ?>
</div>
</section>
\`\`\`
## Hero Section Example
\`\`\`json
{
"name": "proto-blocks/hero",
"title": "Hero Section",
"protoBlocks": {
"fields": {
"backgroundImage": {
"type": "image",
"sizes": ["full"]
},
"content": {
"type": "innerblocks",
"allowedBlocks": [
"core/heading",
"core/paragraph",
"core/buttons"
],
"template": [
["core/heading", {
"level": 1,
"placeholder": "Hero Headline",
"textAlign": "center"
}],
["core/paragraph", {
"placeholder": "Supporting text for your hero section",
"align": "center"
}],
["core/buttons", { "layout": { "justifyContent": "center" } }, [
["core/button", { "text": "Get Started" }],
["core/button", {
"text": "Learn More",
"className": "is-style-outline"
}]
]]
]
}
},
"controls": {
"overlayOpacity": {
"type": "range",
"label": "Overlay Opacity",
"min": 0,
"max": 100,
"default": 50
}
}
}
}
\`\`\`
\`\`\`php
<?php
$bg = $attributes['backgroundImage'] ?? null;
$opacity = ($attributes['overlayOpacity'] ?? 50) / 100;
$wrapper_attributes = get_block_wrapper_attributes([
'class' => 'hero-section',
]);
?>
<section <?php echo $wrapper_attributes; ?>>
<?php if ($bg && !empty($bg['url'])) : ?>
<div
class="hero-section__background"
style="background-image: url('<?php echo esc_url($bg['url']); ?>');"
data-proto-field="backgroundImage"
></div>
<?php else : ?>
<div class="hero-section__background hero-section__background--empty" data-proto-field="backgroundImage">
<span>Select background image</span>
</div>
<?php endif; ?>
<div
class="hero-section__overlay"
style="background-color: rgba(0, 0, 0, <?php echo esc_attr($opacity); ?>);"
></div>
<div class="hero-section__content" data-proto-inner-blocks>
<?php echo $content; ?>
</div>
</section>
\`\`\`
## Two-Column Layout Example
\`\`\`json
{
"name": "proto-blocks/two-column",
"protoBlocks": {
"fields": {
"leftColumn": {
"type": "innerblocks",
"allowedBlocks": ["core/heading", "core/paragraph", "core/list"],
"template": [["core/paragraph", {}]]
},
"rightColumn": {
"type": "innerblocks",
"allowedBlocks": ["core/image", "core/video"],
"template": [["core/image", {}]]
}
}
}
}
\`\`\`
**Note**: Only ONE innerblocks field is allowed per block. For multiple content areas, use core/columns inside a single innerblocks.
## Restrictions
1. **One Per Block**: Only one innerblocks field allowed
2. **No Nesting Check**: Be careful with recursive nesting
3. **Template Array Format**: \`[blockName, attributes, innerBlocks?]\`
## Best Practices
1. **Restrict Blocks**: Use \`allowedBlocks\` to maintain design consistency
2. **Provide Template**: Give users a starting point
3. **Semantic Container**: Wrap in appropriate HTML element
4. **Consider Template Lock**: Prevent users from breaking layouts
5. **Test Thoroughly**: Inner blocks can be complex; test all combinations
`,
};
return details[fieldType] || `Unknown field type: ${fieldType}. Available types: text, wysiwyg, image, link, repeater, innerblocks`;
}