| name | html-to-generateblocks |
| version | 2.0.0 |
| description | Convert HTML/CSS layouts to GenerateBlocks V2 format with inline styles |
| author | Gaurav Tiwari |
| updated | "2026-01-22T00:00:00.000Z" |
| trigger | ["HTML to GenerateBlocks","convert to GB","convert HTML to blocks","GenerateBlocks conversion"] |
| tags | ["wordpress","generateblocks","conversion","html"] |
HTML to GenerateBlocks V2 Conversion
Convert HTML/CSS layouts to GenerateBlocks V2 format using inline styles in block attributes.
Read first
Before converting any markup, read these files in the sibling
generateblocks-layouts skill:
../generateblocks-layouts/references/_index.md — task router
../generateblocks-layouts/references/recovery-rules.md — every cause of
"Attempt Recovery" errors with the exact fix. Non-negotiable.
../generateblocks-layouts/references/block-types.md — attribute specs
If the source HTML contains a list/grid that's clearly fed by dynamic data
(post cards, product listings, related content), build it as a query loop
using ../generateblocks-layouts/references/query-block.md instead of
duplicating the static markup.
Output Requirements
ALWAYS output converted blocks to a file, never inline in the chat.
- Output filename:
{original-name}-converted.html (e.g., hero-converted.html)
- For large conversions: Split into multiple files by section
- Include a brief summary in chat describing what was converted
Why file output?
- Converted block code is often 100+ lines
- Easier to copy/paste into WordPress
- Prevents truncation and formatting issues
- Allows side-by-side comparison with original
Core Principle
Use both styles and css attributes:
styles: Basic properties (padding, margin, colors, display, flex, grid). Supports responsive keys like "@media (max-width:1024px)":{...}
css: Base styles only (alphabetically sorted). Exceptions that go in css: pseudo-elements (::before/::after), media queries, animations, parent hover targeting children
The css attribute must NOT contain hover states or transitions - the plugin generates those from the styles object.
Never use BEM or custom classes - all styling goes in block attributes.
When to Use Core Blocks
For HTML elements not available in GenerateBlocks, use WordPress Core Blocks:
| HTML Element | Convert To | Reason |
|---|
<video> | core/video | Native player controls, autoplay, loop |
<audio> | core/audio | Native audio player |
<iframe> (YouTube, Vimeo) | core/embed | oEmbed support, responsive sizing |
<table> | core/table | Semantic table structure |
<figure> with <figcaption> | core/image | Built-in caption support |
<blockquote> with cite | core/quote | Semantic quote with citation |
<pre> / <code> | core/code | Preformatted code display |
<ul> / <ol> (semantic lists) | core/list | Use with .list class |
<hr> | core/separator | Horizontal rule |
| Gallery layouts | core/gallery | Lightbox, columns, captions |
| Background image sections | core/cover | Parallax, overlay, focal point |
| Text with emojis | core/paragraph | GenerateBlocks doesn't render emojis properly |
Conversion rule: Use GenerateBlocks for layout containers and styled text. Use Core Blocks for specialized content types that have built-in functionality (players, embeds, tables, etc.).
CRITICAL: htmlAttributes Format
htmlAttributes MUST be a plain object, NOT an array of objects:
"htmlAttributes": {"href": "https://example.com/", "target": "_blank", "id": "section-id"}
"htmlAttributes": [
{"attribute": "href", "value": "/contact/"},
{"attribute": "target", "value": "_blank"},
{"attribute": "id", "value": "section-id"}
]
linkHtmlAttributes (for media blocks) uses the same array format:
"linkHtmlAttributes": [
{"attribute": "href", "value": "/product/"},
{"attribute": "target", "value": "_blank"}
]
Block Structure
Standard Element Block
Element blocks add "className":"gb-element" to attributes. HTML class order: gb-element-{id} gb-element:
<div class="gb-element-elem001 gb-element">
</div>
Text Block (for headings, paragraphs, links)
<h2 class="gb-text gb-text-text001">Heading Text</h2>
Link as Card Wrapper
Cards with inner blocks use generateblocks/element (not text) with tagName: "a":
<a class="gb-element-card001 gb-element" href="https://example.com/services/">
</a>
Plain text links (no inner blocks) use generateblocks/text with tagName: "a" — no htmlAttributes for href:
<a class="gb-text gb-text-link001">Learn more</a>
Media/Image Block
<img class="gb-media gb-media-img001" src="https://example.com/image.jpg" alt="Description" loading="lazy" width="600" height="400" />
Text <a> vs Element <a> Links
| Block Type | htmlAttributes for href | href in HTML | Use Case |
|---|
generateblocks/text with tagName: "a" | No - plugin manages link internally | No | Plain text buttons/links (no inner blocks) |
generateblocks/element with tagName: "a" | Yes - {"href":"https://example.com/"} | Yes | Containers wrapping inner blocks (cards, icon buttons) |
Rule: Text <a> blocks are leaf blocks - the link URL is managed by the editor UI. Element <a> blocks are containers - they need explicit htmlAttributes for the href.
Buttons with icons use generateblocks/element (tagName a) wrapping generateblocks/text + generateblocks/shape blocks. Plain text buttons use generateblocks/text.
Styles vs CSS Decision Matrix
| Feature | Use styles | Use css |
|---|
| Layout (display, flex, grid) | ✅ | Also in CSS (base styles) |
| Spacing (padding, margin, gap) | ✅ | Also in CSS (base styles) |
| Colors (background, text) | ✅ | Also in CSS (base styles) |
| Typography (font-size, weight) | ✅ | Also in CSS (base styles) |
| Basic borders, border-radius | ✅ | Also in CSS (base styles) |
| Responsive overrides | ✅ "@media (max-width:1024px)":{...} | Also in CSS |
| Hover states | ✅ via styles object | ❌ Never in css (plugin generates) |
| Transitions | ✅ via styles object | ❌ Never in css (plugin generates) |
| Pseudo-elements (::before/::after) | ❌ | ✅ Only CSS |
| Media queries | ✅ (simple overrides) | ✅ (complex rules) |
| Animations (@keyframes) | ❌ | ✅ Only CSS |
| Parent hover targeting children | ❌ | ✅ Only CSS (in child's css) |
Pattern: Put base properties in both styles and css (alphabetically sorted). The css attribute contains base styles plus exceptions (pseudo-elements, media queries, animations, parent-hover-child selectors). Never put hover states or transitions in css.
Common Patterns
Card with Animated Underline
Cards with inner blocks use generateblocks/element. Pseudo-elements (::after) and parent-hover-pseudo go in css. Hover states and transitions do NOT go in css.
<a class="gb-element-card001 gb-element" href="https://example.com/link/">
</a>
Grid Layout (Responsive)
<div class="gb-element-grid001 gb-element">
</div>
Icon (Shape Block with SVG)
SVG icons use generateblocks/shape. Two valid approaches:
Approach 1: styles.svg object (plugin generates .gb-shape-{id} svg{...} CSS):
<span class="gb-shape gb-shape-icon001"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/></svg></span>
Approach 2: Simple styles (for quick inline icons):
<span class="gb-shape gb-shape-check001"><svg stroke-linejoin="round" stroke-linecap="round" stroke-width="3" stroke="currentColor" fill="none" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"></polyline></svg></span>
Parent hover targeting icons is written in the shape block's css (as shown in Approach 1).
Featured Card (Dark, Span Multiple Columns)
<div class="gb-element-feat001 gb-element">
</div>
Badge (Absolute Position)
<span class="gb-text gb-text-badge001">Recommended</span>
Dynamic Content with Query Blocks
For sections with dynamic WordPress posts, use native query blocks with GenerateBlocks for styling:
<div class="wp-block-query">
<a class="gb-element-post001 gb-element">
<div class="gb-element-post002 gb-element">
</div>
</a>
</div>
Unique ID Convention
- Format:
{section}{number}{letter} (e.g., hero001a, serv023, tool014)
- Section prefix: 3-4 characters (hero, serv, tool, blog, feat)
- Number: Sequential 001-999
- Optional letter: For nested elements (a, b, c)
Conversion Workflow
- Read original HTML/CSS - Understand structure and styles
- Identify sections - Break into logical components
- Map BEM classes to blocks - Each
.block__element becomes a GenerateBlocks element
- Extract base styles - Put in
styles attribute
- Extract complex styles - Put in
css attribute (pseudo-elements, media queries, parent-hover-child). Never put hover states or transitions in css
- Create unique IDs - Follow convention
- Test responsive behavior - Ensure media queries work
- Handle dynamic content - Use WordPress query blocks
CSS Syntax Rules
In styles attribute (JavaScript object):
{
"display": "flex",
"flexDirection": "column",
"backgroundColor": "#ffffff",
"borderRadius": "1rem",
"marginBottom": "2rem"
}
In css attribute (CSS string):
.gb-element-id{background-color:#ffffff;border-radius:1rem;display:flex;flex-direction:column;margin-bottom:2rem}@media(max-width:768px){.gb-element-id{flex-direction:row}}
Rules:
- CSS must be minified (no line breaks, minimal spaces)
- Properties must be alphabetically sorted
- Contains base styles only — no hover states, no transitions
- Exceptions: pseudo-elements (::before/::after), media queries, animations, parent hover targeting children
Responsive Patterns
Mobile-First Grid
.gb-element-grid{display:grid;grid-template-columns:1fr}@media(min-width:768px){.gb-element-grid{grid-template-columns:repeat(2, minmax(0, 1fr))}}@media(min-width:1024px){.gb-element-grid{grid-template-columns:repeat(4, minmax(0, 1fr))}}
Desktop-First Grid (Match Original)
.gb-element-grid{display:grid;grid-template-columns:repeat(4, minmax(0, 1fr));gap:1rem}@media(max-width:1024px){.gb-element-grid{grid-template-columns:repeat(2, minmax(0, 1fr))!important}}@media(max-width:768px){.gb-element-grid{grid-template-columns:1fr!important}}
Sticky Sidebar
.gb-element-sidebar{position:sticky;top:calc(var(--header-height, 80px) + 1rem)}@media(max-width:1024px){.gb-element-sidebar{position:static}}
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:
CORRECT - Only block delimiters:
<div class="gb-element-card001 gb-element">
<figure class="wp-block-image"><img src="image.jpg" alt=""/></figure>
</div>
Any extra HTML comments will break the WordPress block editor and cause parsing errors. This is non-negotiable. Do NOT add descriptive comments, section labels, or any other HTML comments.
Design Inference (When CSS Not Provided)
When converting HTML without explicit CSS values, infer styles based on context:
GeneratePress Defaults:
- Primary:
#0073e6
- Text:
#222222, Muted: #757575
- Body:
17px, line-height 1.7
- H1:
42px, H2: 35px, H3: 29px
- Section padding:
60px
- Container max-width:
var(--gb-container-width)
gauravtiwari.org Design System:
- Primary:
#c0392b
- Text:
#0a0a0a, Muted: #5c5c5c
- Background:
#ffffff, Light: #f5f5f3
- Headings: font-weight
900, letter-spacing -0.03em
- Section padding:
4rem
- Card radius:
1rem, Button radius: 2rem
- Hover lift:
translateY(-6px)
- Shadow:
0 20px 60px rgba(0,0,0,0.15)
Common Gotchas
- No HTML comments except block markers - Breaks WordPress block editor
- Always escape quotes in CSS strings - Use single quotes for content, attr values
- Duplicate properties - Put in both
styles and css for consistency
- CSS alphabetically sorted - Properties in the
css string must be sorted alphabetically
- No hover/transitions in
css - The plugin generates hover states and transitions from the styles object. Never put these in the css attribute
- Cards with inner blocks = element block - Use
generateblocks/element (not text) for cards containing other blocks. Text blocks are leaf blocks (no inner blocks)
- Text
<a> = no htmlAttributes for href - Link URL managed by editor UI. Element <a> = use htmlAttributes for href
- SVG icons = shape blocks - Use
generateblocks/shape for SVGs, not generateblocks/element with raw SVG inside
- Pseudo-elements need content -
content:'' for ::before/::after (these go in css)
- Parent hover targeting children - Written in the child's
css: .gb-element-card001:hover .gb-text-title001{color:#c0392b}
- Gradients only in CSS - Can't use in
styles attribute
- CSS variables work - Use
var(--custom-property) freely. Use \u002d\u002d for -- in JSON
- Element blocks need className with uniqueId - Add
"className":"gb-element-{id} gb-element" to element block attributes (uniqueId first, then gb-element)
- Use !important sparingly - Only for overriding at breakpoints
- Lists use
core/list with .list class - Convert <ul>/<ol> to native WordPress list block with className: "list"
- Use
--gb-container-width for inner containers - Set inner container width using the CSS variable; add align: "full" to parent section
- Buttons with icons - Use
generateblocks/element (tagName a) wrapping generateblocks/text + generateblocks/shape blocks. Plain text buttons use generateblocks/text
- htmlAttributes as plain object - Use
{"href":"https://example.com/"} not [{"attribute":"href","value":"https://example.com/"}]
- Full absolute URLs for links - Always use
https://example.com/page/ not /page/ in htmlAttributes href values
- No spaces in CSS functions - Write
clamp(3rem,8vw,5rem) not clamp(3rem, 8vw, 5rem) in the css attribute
- SVG attribute order in HTML - Follow this order:
stroke-linejoin, stroke-linecap, stroke-width, stroke, fill, viewBox, height, width
- Compact nesting - Closing tags go on the same line as parent closing comment:
</div>\n<!-- /wp:generateblocks/element -->
- Element
<a> with text-only content causes recovery errors - Use generateblocks/text with tagName: "a" for simple text links. Only use generateblocks/element with tagName: "a" when wrapping inner blocks (cards, icon buttons)
Performance Notes
- Inline styles are fast (no external CSS file)
- Each block's CSS is scoped to its unique ID
- GenerateBlocks automatically deduplicates common styles
- Media queries only load when needed
- Use
content-visibility: auto for off-screen sections
Example: Complete Hero Section
See to-convert/home-hero-v2.html for a complete real-world example with:
- Complex grid layout
- Multiple nested components
- Responsive breakpoints
- Hover effects
- Icon fonts
- Images with overlays