generateblocks-layouts
GenerateBlocks V2 Layout Builder
Build professional WordPress layouts using GenerateBlocks V2's four core blocks plus the Query / Looper / Loop-Item family for dynamic content.
Read first
Before generating any markup, read these in order:
references/_index.md— task router. Tells you which other files to load.references/recovery-rules.md— every known cause of "Attempt Recovery" errors with the exact fix. This is non-negotiable. Skip it and you will produce broken markup.- The task-specific reference from
_index.md.
The most common task-specific files:
- Static layout →
block-types.md - Blog grid / dynamic content →
query-block.md - Tabs / accordion / sticky header / ACF / conditions →
gb-pro.md
The meta-rule (read this twice)
The WordPress block editor validates blocks by re-serializing the attributes and string-comparing against the markup you pasted. Any deviation — even semantically-equivalent JSON or HTML — is treated as corruption and triggers "Attempt Recovery".
This is the unifying principle. Every rule in recovery-rules.md exists to
make your output byte-identical to what the editor itself would emit.
That means matching:
- The JSON key order from
block.jsondeclarations - The four substitutions (
--<>&→\u002d\u002d\u003c\u003e\u0026) on every JSON string - CSS minification and alphabetization
- The class list including any auto-injected duplicates
- The exact spacing and quoting
If you remember nothing else: emit what the editor emits, not what you think is correct.
Output Requirements
ALWAYS output generated blocks to a file, never inline in the chat.
- Output filename:
{section-name}.html(e.g.,hero-section.html,services-grid.html) - For multiple sections: Create separate files or one combined file
- Include a brief summary in chat describing what was created
Why file output?
- Block code is often 100+ lines and breaks chat formatting
- Easier to copy/paste into WordPress
- Prevents truncation of long outputs
- Allows incremental building of complex layouts
Quick Start
GenerateBlocks V2 uses four block types:
| Block | Class Pattern | Use For |
|---|---|---|
generateblocks/element |
.gb-element-{id} |
Containers (div, section, article, header, nav, footer) |
generateblocks/text |
.gb-text-{id} |
Text content (h1-h6, p, span, a, button) |
generateblocks/media |
.gb-media-{id} |
Images (static only, no dynamic features) |
generateblocks/shape |
.gb-shape-{id} |
SVG icons and decorative shapes |
When to Use Core Blocks
For elements not available in GenerateBlocks or requiring advanced media features, use WordPress Core Blocks:
| Content Type | Use Core Block | Why |
|---|---|---|
| Images with captions | core/image |
Built-in caption support |
| Image galleries | core/gallery |
Lightbox, columns, captions |
| Videos | core/video |
Native video player, controls |
| Embedded media | core/embed |
YouTube, Vimeo, Twitter, etc. |
| Audio files | core/audio |
Native audio player |
| File downloads | core/file |
Download links with filename |
| Tables | core/table |
Structured data tables |
| Lists | core/list |
Semantic ul/ol with .list class |
| Quotes | core/quote |
Blockquote with citation |
| Code blocks | core/code |
Preformatted code display |
| Separators | core/separator |
Horizontal rules |
| Buttons (grouped) | core/buttons |
Multiple button layouts |
| Columns (simple) | core/columns |
Quick equal-width layouts |
| Cover images | core/cover |
Background images with overlays |
| Dynamic post content | core/post-* |
Post title, excerpt, featured image, etc. |
| Query loops | core/query |
Dynamic content from posts |
| Emojis | core/paragraph |
GenerateBlocks doesn't render emojis properly |
Rule of thumb: Use GenerateBlocks for layout structure and custom styling. Use Core Blocks for specialized content types and media with built-in functionality.
Block Template
<!-- wp:generateblocks/{type} {json_attributes} -->
<{tag} class="gb-{type} gb-{type}-{uniqueId}">
{content}
</{tag}>
<!-- /wp:generateblocks/{type} -->
Element blocks use "className":"gb-element" (just the base class — the plugin auto-injects the id-class). The rendered HTML class is gb-element-{id} gb-element:
<!-- wp:generateblocks/element {"uniqueId":"card001","tagName":"div","styles":{...},"css":"...","className":"gb-element"} -->
<div class="gb-element-card001 gb-element">...</div>
<!-- /wp:generateblocks/element -->
Required Attributes
Every block needs (in this canonical key order, from block.json):
uniqueId— Unique identifier ({section}{number}likehero001,card023)tagName— HTML element type (omitted forshape, which useshtmlinstead)content— (text block only, position 3) the rich text contentstyles— CSS properties as JSON object (camelCase). Supports responsive keys like"@media (max-width:1024px)":{...}css— Generated CSS string (kebab-case, minified, alphabetically sorted)globalClasses— Array of global CSS class slugs (optional)htmlAttributes— Plain object of attribute key-value pairs (for links, IDs, data attributes)- Block-specific extras:
align(element),mediaId(media),queryType, paginationType, query, inheritQuery(query),midSize(query-page-numbers),icon, iconLocation, iconOnly(text) className— always last. WordPress core attribute. Should NOT include the auto-injectedgb-{type}-{uniqueId}class — the plugin adds that itself.
Per-block exceptions:
- Text block:
contentis at position 3, BEFOREstyles. This is the only block that breaks the standard order. - Shape block: no
tagName.htmlis at position 2. - Query block:
queryType, paginationType, query, inheritQuerycome afterhtmlAttributes.
See references/recovery-rules.md §3.4 for the verified per-block declaration order.
className: do NOT include the id-class
The plugin auto-injects gb-{type}-{uniqueId} into the rendered HTML class
list whenever styles is non-empty. If you also put it in className, the
rendered HTML has it twice and you trigger recovery on save.
// RIGHT — let the plugin auto-inject
"className":"gb-element"
"className":"gb-element alignfull"
// WRONG — duplicates the id-class
"className":"gb-element-card001 gb-element"
The rendered HTML class list always shows the id-class once (auto-injected
at the front) followed by whatever is in className:
<div class="gb-element-card001 gb-element"> <!-- ✓ -->
<header class="gb-element-hero001 gb-element alignfull"> <!-- ✓ -->
CRITICAL: htmlAttributes Format
htmlAttributes MUST be a plain object, NOT an array:
// ✅ CORRECT - Plain object
"htmlAttributes": {"href": "https://example.com/page/", "target": "_blank", "id": "section-id"}
// ❌ WRONG - Array of objects (causes block editor recovery errors)
"htmlAttributes": [
{"attribute": "href", "value": "/contact/"},
{"attribute": "target", "value": "_blank"}
]
Use full absolute URLs, not relative paths. The block editor saves links as absolute URLs; relative paths get converted on save, causing a mismatch that triggers block recovery.
// ✅ CORRECT
"htmlAttributes": {"href": "https://gauravtiwari.org/services/web-development/"}
// ❌ WRONG
"htmlAttributes": {"href": "/services/web-development/"}
The link block pattern (read carefully)
There are two failure modes to avoid:
generateblocks/textwithtagName:"a"strips itshrefon save. The href does not survive the round trip. Don't use it for action links.generateblocks/elementwithtagName:"a"containing raw text triggers recovery — element blocks expect inner blocks, not text content.
The correct pattern: element <a> wrapping a text span child.
<!-- wp:generateblocks/element {"uniqueId":"link1","tagName":"a","styles":{"display":"inline-block","color":"#c0392b"},"css":".gb-element-link1{color:#c0392b;display:inline-block}","htmlAttributes":{"href":"https://example.com/page/?a=1\u0026b=2"},"className":"gb-element"} -->
<a class="gb-element-link1 gb-element" href="https://example.com/page/?a=1&b=2">
<!-- wp:generateblocks/text {"uniqueId":"link2","tagName":"span","content":"Read more →","styles":{},"css":""} -->
<span class="gb-text-link2 gb-text">Read more →</span>
<!-- /wp:generateblocks/text -->
</a>
<!-- /wp:generateblocks/element -->
This works because:
- The element
<a>has an inner block child → no "raw text" recovery htmlAttributes.hrefon the element block survives save → href preserved- The text child is a
span, not ana→ no href stripping - The arrow is a literal
→in the text — never use a CSS\2192escape &in the JSONhtmlAttributesis escaped as\u0026- The same
&in the rendered HTMLhrefis escaped as& classNameis just"gb-element"(no id-class duplicate) — the plugin auto-injectsgb-element-link1so the rendered HTML has it once- JSON key order:
uniqueId, tagName, styles, css, htmlAttributes, className(canonical block.json order, withclassNamelast)
For inline links inside a paragraph, write the <a> directly in the rich text
content of a single generateblocks/text paragraph block — don't wrap each
inline link in its own block.
See references/recovery-rules.md §4 for the full explanation.
Styling Approach
Always use both styles AND css attributes:
{
"uniqueId": "card001",
"tagName": "div",
"styles": {
"backgroundColor": "#ffffff",
"display": "flex",
"padding": "2rem"
},
"css": ".gb-element-card001{background-color:#ffffff;display:flex;padding:2rem}"
}
CSS rules:
- The
cssattribute contains only base styles - no hover states, no transitions (the plugin generates those from thestylesobject) - CSS properties must be alphabetically sorted
- Exceptions that go in
css: pseudo-elements (::before/::after), media queries, animations, parent hover targeting children
/* Base styles only (alphabetically sorted) + pseudo-elements + media queries */
.gb-element-card001{background-color:#ffffff;border-radius:1rem;display:flex;padding:2rem;position:relative}.gb-element-card001::after{content:'';position:absolute;bottom:0;left:0;width:100%;height:3px;background:#c0392b;transform:scaleX(0)}@media(max-width:768px){.gb-element-card001{padding:1rem}}
Parent hover targeting children is written in the child's css:
.gb-element-card001:hover .gb-text-title001{color:#c0392b}
Responsive Design
Desktop-first approach with standard breakpoints:
| Breakpoint | Width | Use For |
|---|---|---|
| Desktop | 1025px+ | Default styles (no media query) |
| Tablet | 768px - 1024px | @media(max-width:1024px) |
| Mobile | < 768px | @media(max-width:768px) |
Two approaches for responsive styles:
- In
stylesobject (preferred for simple overrides):
{
"styles": {
"display": "grid",
"gridTemplateColumns": "minmax(0, 1fr) minmax(0, 1fr)",
"gap": "4rem",
"@media (max-width:1024px)": {
"gridTemplateColumns": "minmax(0, 1fr)"
}
}
}
- In
cssstring (for complex responsive rules):
.gb-element-hero001{display:grid;gap:4rem;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}@media (max-width:1024px){.gb-element-hero001{grid-template-columns:minmax(0,1fr)}}
Common responsive patterns:
- Grid to single column:
grid-template-columns:minmax(0,1fr) minmax(0,1fr)→grid-template-columns:minmax(0,1fr) - Reduce padding:
padding:6rem 0→padding:4rem 0→padding:3rem 0 - Reduce font sizes: Use
clamp()for fluid typography - Stack flex items:
flex-direction:row→flex-direction:column - Adjust gaps:
gap:4rem→gap:2rem - Center text on mobile:
text-align:left→text-align:center
Full-Width Section Pattern
For full-width sections with contained inner content:
<!-- wp:generateblocks/element {"uniqueId":"hero001","tagName":"section","styles":{...},"css":"...","align":"full","className":"gb-element-hero001 gb-element alignfull"} -->
<section class="gb-element-hero001 gb-element alignfull">
<!-- wp:generateblocks/element {"uniqueId":"hero002","tagName":"div","styles":{"maxWidth":"var(\u002d\u002dgb-container-width)","marginLeft":"auto","marginRight":"auto"},"css":".gb-element-hero002{margin-left:auto;margin-right:auto;max-width:var(\u002d\u002dgb-container-width)}","className":"gb-element-hero002 gb-element"} -->
<div class="gb-element-hero002 gb-element">
<!-- Inner content -->
</div>
<!-- /wp:generateblocks/element -->
</section>
<!-- /wp:generateblocks/element -->
Key:
- Outer section:
"align":"full"+"className":"gb-element-hero001 gb-element alignfull" - Inner container:
maxWidth: "var(\u002d\u002dgb-container-width)"(unicode-escaped--gb-container-width)
Unique ID Convention
Format: {section}{number}{letter}
- Section prefix: 3-4 chars (
hero,serv,card,feat,blog) - Number: 001-999 sequential
- Letter: Optional for nested elements (
a,b,c)
Examples: hero001, serv023a, card014, feat007b
References
Start with _index.md to figure out which file you need.
- Skill router — task → file mapping. Read first.
- Recovery rules — every cause of "Attempt Recovery" with the exact fix. Read on every task.
- Block types — attribute specs for element/text/media/shape
- Query block — V2 dynamic content: query, looper, loop-item, no-results, page-numbers
- GenerateBlocks Pro — Pro-only blocks, dynamic tags, conditions, transforms, effects
- CSS patterns — hover effects, gradients, pseudo-elements
- SVG icons — shape block usage and inline SVG
- Responsive — media queries and breakpoint patterns
- Troubleshooting — debug recipes for known failures
Examples
See /examples/ folder for copy-paste ready blocks:
- basic/ - Single blocks (text, buttons, images)
- compound/ - Combined blocks (cards, features, stats)
- layouts/ - Full sections (hero, services, grid)
- svg/ - Icons and decorative shapes
CRITICAL: No Extra HTML Comments
⛔ NEVER add HTML comments other than WordPress block markers.
The ONLY allowed comments are WordPress block delimiters:
<!-- wp:generateblocks/element {...} -->and<!-- /wp:generateblocks/element --><!-- wp:generateblocks/text {...} -->and<!-- /wp:generateblocks/text --><!-- wp:generateblocks/media {...} -->and<!-- /wp:generateblocks/media --><!-- wp:generateblocks/shape {...} -->and<!-- /wp:generateblocks/shape --><!-- wp:image {...} -->and<!-- /wp:image --><!-- wp:video {...} -->and<!-- /wp:video --><!-- wp:embed {...} -->and<!-- /wp:embed -->- Any other
<!-- wp:{namespace}/{block} -->format
WRONG - These will break the block editor:
<!-- Hero Section -->
<!-- Card container -->
<!-- Button wrapper -->
<!-- This is a heading -->
<!-- Content goes here -->
CORRECT - Only block delimiters:
<!-- wp:generateblocks/element {"uniqueId":"hero001",...,"className":"gb-element"} -->
<section class="gb-element-hero001 gb-element">
<!-- wp:generateblocks/text {"uniqueId":"hero002",...} -->
<h1 class="gb-text gb-text-hero002">Heading</h1>
<!-- /wp:generateblocks/text -->
</section>
<!-- /wp:generateblocks/element -->
Any extra HTML comments will break the WordPress block editor and cause parsing errors. This is non-negotiable.
Key Rules
- No custom CSS classes — all styling in block attributes
- Minify CSS — no line breaks in
cssattribute, no spaces inside function args (repeat(3,1fr)notrepeat(3, 1fr)) - CSS = base styles only — no
transitiondeclarations and no:hoverrules in thecssstring. The plugin generates both from thestylesobject. Exceptions: pseudo-elements, media queries, animations, parent-hover targeting children - Alphabetically sort CSS properties inside each rule block in the
cssstring - Duplicate styles — most properties go in both the
stylesobject AND thecssstring - Escape
--as\u002d\u002danywhere it appears inside JSON strings (bothstylesvalues andcssstrings) — literal--in JSON triggers recovery on save. Inlinestyle=""on rendered HTML can use literalvar(--foo)because it's not JSON - No descendant selectors in
css— selectors like.gb-text-x small{}or.gb-text-x .u{}get stripped and trigger recovery. Put inlinestyle=""on the inner<small>,<code>,<strong>,<span>directly. Allowed exceptions: pseudo-elements on the block's own selector, and parent-hover targeting another GB block by its own generated class - No CSS
\xxxxescape sequences — write literal characters:content:'→', notcontent:'\2192'. Better: put arrows in the rendered text content, not in pseudo-elements - Action links: element
<a>wrapping atextspan child — text<a>strips its href on save, element<a>with raw text triggers recovery. Theelement-a wraps text-spanpattern avoids both. See SKILL.md "The link block pattern" section htmlAttributesis always a plain object —{"href":"..."}never[{"attribute":"href","value":"..."}]- Full absolute URLs in
htmlAttributes.href— relative paths get canonicalized on save → recovery &encoding — keep literal&in JSONhtmlAttributes, encode as&in the rendered HTML<a href="...">bodyclassNamemust include the uniqueId class —"gb-element-{uniqueId} gb-element", never just"gb-element"- Compact nesting — closing comment adjacent to closing tag, no blank lines inside empty blocks
- No HTML comments other than WP block delimiters — no section labels, no TODOs
- Test responsive — add media queries for tablet (1024px) and mobile (768px). Avoid responsive rules that would need descendant selectors in
css - Static images with captions →
core/image, NOTgenerateblocks/media.generateblocks/mediais for dynamic loop images. Plain static images without captions can use either, butcore/imageis more reliable - Lists →
core/listwithclassName:"list" - Emojis →
core/paragraph— GB renders emoji glyphs incorrectly - Dynamic content →
query-block.md— usegenerateblocks/query+looper+loop-itemfor any post list, archive, or related-posts section - Shape block — use
styles.svgfor SVG-specific props OR simplestyleswith inline SVG attributes; both work - Use
var(\u002d\u002dgb-container-width)for inner containers withalign:"full"on the parent section for full-width layouts - SVG attribute order — the editor reorders attributes:
stroke-linejoin,stroke-linecap,stroke-width,stroke,fill,viewBox,height,width. Write them in that order.
For the full list of recovery causes and the exact fix for each, see
references/recovery-rules.md.
Design Inference (When CSS Not Provided)
When no CSS values are specified, infer styles based on context:
GeneratePress Defaults
- Primary:
#0073e6 - Text:
#222222 - Body font:
17px, line-height1.7 - H1:
42px, H2:35px, H3:29px - Section padding:
60px - Container max-width:
var(--gb-container-width) - Button padding:
15px 30px
gauravtiwari.org Design System
- Primary:
#c0392b - Text:
#0a0a0a, Muted:#5c5c5c - Background:
#ffffff, Light:#f5f5f3 - Headings: font-weight
900, tight letter-spacing - Section padding:
4rem - Card radius:
1rem, Button radius:2rem - Hover lift:
translateY(-6px) - Shadow:
0 20px 60px rgba(0,0,0,0.15)
Complex Layout Strategy
For large sections (50+ blocks), break into chunks:
- Plan structure first - Map components before coding
- Build bottom-up - Start with innermost elements
- Test incrementally - Verify each component works
- Use consistent IDs - Same prefix for related elements
See Troubleshooting for detailed guidance on complex layouts.