Etch Layouts Skill
Use this skill when creating layouts, sections, or components for Etch (the WordPress visual builder).
For detailed reference, read etch-skills/generating-layouts.md in the project root.
Output Structure
Create layouts in gt-design-system/layouts/{component-name}/:
layouts/{component-name}/
├── {component-name}-etch.json # For pasting into Etch (JSON format)
├── {component-name}-preview.html # Browser preview (compiled CSS inline)
└── {component-name}.js # JavaScript (if interactive)
Paste Format for Etch (v2.1)
Etch only accepts JSON format for pasting. The structure is:
{
"type": "block",
"gutenbergBlock": {
"blockName": "etch/element",
"attrs": {
"metadata": { "name": "My Section" },
"tag": "section",
"attributes": { "class": "my-section" },
"styles": ["styleId123"]
},
"innerBlocks": [],
"innerHTML": "",
"innerContent": []
},
"version": 2.1,
"styles": {
"styleId123": {
"type": "class",
"selector": ".my-section",
"collection": "default",
"css": "padding: var(--space-2xl) 0;\nbackground: var(--bg-base);\n\n@media (max-width: to-rem(768px)) {\n padding: var(--space-xl) 0;\n}",
"readonly": false
}
}
}
JSON Structure
| Field |
Description |
type |
Always "block" |
gutenbergBlock |
Block tree with blockName, attrs, innerBlocks, innerHTML, innerContent |
version |
Always 2.1 |
timestamp |
ISO 8601 timestamp |
styles |
Object mapping style IDs to style definitions |
Block Types
| Block |
Purpose |
etch/element |
HTML element with tag, attributes, styles, metadata |
etch/text |
Text content with content and metadata attributes |
etch/loop |
Iterator with target, itemId, loopParams |
etch/condition |
Conditional rendering with condition |
etch/component |
Reusable pattern reference with ref |
Every Block MUST Have
All 5 fields: blockName, attrs, innerBlocks, innerHTML, innerContent
Every Element MUST Have
metadata.name — builder tree label
class attribute — for CSS targeting
styles array — with own style ID(s) linking to styles map
innerHTML/innerContent Rules
| Case |
innerHTML |
innerContent |
| text block |
"" |
[] |
| void element (img, br, hr) |
"\n\n" |
["\n", "\n"] |
| 0 children (non-void) |
"" |
[] |
| 1 child |
"\n\n" |
["\n", null, "\n"] |
| 2 children |
"\n\n\n\n" |
["\n", null, "\n\n", null, "\n"] |
| N children |
formula |
["\n", null, "\n\n", ..., null, "\n"] |
Style Definition
{
"type": "class",
"selector": ".class-name",
"collection": "default",
"css": "/* SCSS-like syntax with to-rem() and design system variables */",
"readonly": false
}
Style types: "class" (.selector), "id" (#selector), "element" (tag selector)
Critical Rules
- No
data-etch-element — that attribute is from the legacy conversion script only
- No built-in styles — no
etch-section-style or etch-container-style
- Every element gets its own style — per-element styling, not monolithic section CSS
to-rem() for ALL pixel values — never raw px
- Design system variables — never hardcoded colors, spacing, or sizes
- BEM naming —
section__element--modifier pattern
SCSS Rules
Styles use SCSS-like syntax:
to-rem() for all pixel values: border: to-rem(1px) solid var(--border-default);
& for pseudo-classes: &:hover { color: var(--accent); }
- Nested media queries:
@media (max-width: to-rem(768px)) { ... }
- Child selectors from parent:
&:hover .card__title { color: var(--accent); }
clamp() with to-rem(): font-size: clamp(to-rem(28px), 4vw, to-rem(40px));
CSS Variables (GT Design System)
Colors
--bg-base, --bg-subtle, --bg-elevated, --bg-sunken, --bg-overlay-heavy
--text-primary, --text-secondary, --text-muted, --text-inverse
--accent, --accent-hover
--border-light, --border-default, --border-medium
Typography
--font-sans, --font-mono
--fs-xs to --fs-4xl
--fw-normal to --fw-black
--lh-tight, --lh-snug, --lh-normal, --lh-relaxed
Spacing
--space-3xs to --space-3xl
Layout
--max-width, --content-width
--header-height, --header-height-mobile
Effects
--radius-xs to --radius-full
--shadow-xs to --shadow-xl
--duration-fast, --duration-normal
--ease-out, --ease-in-out
Z-Index
--z-header, --z-modal, --z-tooltip
Dynamic Expressions
Use {context.path} syntax in attributes and text content:
{site.url} - Site URL
{site.name} - Site name
{this.title} - Current post title
{this.id} - Current post ID
{user.displayName} - Current user name
{props.myProp} - Component prop
{post.title} - Loop item field (when itemId is "post")
{post.permalink.relative} - Post permalink
{post.featuredImage.url} - Featured image URL
{post.date.dateFormat("M j, Y")} - Formatted date
{post.excerpt | stripTags | truncate:100} - Filtered excerpt
Workflow
- Read
etch-skills/generating-layouts.md for full reference
- Build JSON structure with blocks and styles (SCSS syntax in
css fields)
- Ensure every element has
class, metadata.name, and styles with own ID(s)
- Save as
-etch.json for pasting into Etch
- Create
-preview.html with compiled CSS for browser testing
- Create
.js if interactive
1---2name: etch-layouts3description: Create responsive layouts using Etch page builder patterns and components.4---56# Etch Layouts Skill78Use this skill when creating layouts, sections, or components for Etch (the WordPress visual builder).910**For detailed reference, read `etch-skills/generating-layouts.md` in the project root.**1112## Output Structure1314Create layouts in `gt-design-system/layouts/{component-name}/`:1516```17layouts/{component-name}/18├── {component-name}-etch.json # For pasting into Etch (JSON format)19├── {component-name}-preview.html # Browser preview (compiled CSS inline)20└── {component-name}.js # JavaScript (if interactive)21```2223## Paste Format for Etch (v2.1)2425**Etch only accepts JSON format for pasting.** The structure is:2627```json28{29 "type": "block",30 "gutenbergBlock": {31 "blockName": "etch/element",32 "attrs": {33 "metadata": { "name": "My Section" },34 "tag": "section",35 "attributes": { "class": "my-section" },36 "styles": ["styleId123"]37 },38 "innerBlocks": [],39 "innerHTML": "",40 "innerContent": []41 },42 "version": 2.1,43 "styles": {44 "styleId123": {45 "type": "class",46 "selector": ".my-section",47 "collection": "default",48 "css": "padding: var(--space-2xl) 0;\nbackground: var(--bg-base);\n\n@media (max-width: to-rem(768px)) {\n padding: var(--space-xl) 0;\n}",49 "readonly": false50 }51 }52}53```5455### JSON Structure5657| Field | Description |58|-------|-------------|59| `type` | Always `"block"` |60| `gutenbergBlock` | Block tree with `blockName`, `attrs`, `innerBlocks`, `innerHTML`, `innerContent` |61| `version` | Always `2.1` |62| `timestamp` | ISO 8601 timestamp |63| `styles` | Object mapping style IDs to style definitions |6465### Block Types6667| Block | Purpose |68|-------|---------|69| `etch/element` | HTML element with `tag`, `attributes`, `styles`, `metadata` |70| `etch/text` | Text content with `content` and `metadata` attributes |71| `etch/loop` | Iterator with `target`, `itemId`, `loopParams` |72| `etch/condition` | Conditional rendering with `condition` |73| `etch/component` | Reusable pattern reference with `ref` |7475### Every Block MUST Have7677All 5 fields: `blockName`, `attrs`, `innerBlocks`, `innerHTML`, `innerContent`7879### Every Element MUST Have8081- `metadata.name` — builder tree label82- `class` attribute — for CSS targeting83- `styles` array — with own style ID(s) linking to `styles` map8485### innerHTML/innerContent Rules8687| Case | innerHTML | innerContent |88|------|-----------|--------------|89| text block | `""` | `[]` |90| void element (img, br, hr) | `"\n\n"` | `["\n", "\n"]` |91| 0 children (non-void) | `""` | `[]` |92| 1 child | `"\n\n"` | `["\n", null, "\n"]` |93| 2 children | `"\n\n\n\n"` | `["\n", null, "\n\n", null, "\n"]` |94| N children | formula | `["\n", null, "\n\n", ..., null, "\n"]` |9596### Style Definition9798```json99{100 "type": "class",101 "selector": ".class-name",102 "collection": "default",103 "css": "/* SCSS-like syntax with to-rem() and design system variables */",104 "readonly": false105}106```107108Style types: `"class"` (`.selector`), `"id"` (`#selector`), `"element"` (tag selector)109110## Critical Rules1111121. **No `data-etch-element`** — that attribute is from the legacy conversion script only1132. **No built-in styles** — no `etch-section-style` or `etch-container-style`1143. **Every element gets its own style** — per-element styling, not monolithic section CSS1154. **`to-rem()` for ALL pixel values** — never raw px1165. **Design system variables** — never hardcoded colors, spacing, or sizes1176. **BEM naming** — `section__element--modifier` pattern118119## SCSS Rules120121Styles use SCSS-like syntax:1221231. `to-rem()` for all pixel values: `border: to-rem(1px) solid var(--border-default);`1242. `&` for pseudo-classes: `&:hover { color: var(--accent); }`1253. Nested media queries: `@media (max-width: to-rem(768px)) { ... }`1264. Child selectors from parent: `&:hover .card__title { color: var(--accent); }`1275. `clamp()` with `to-rem()`: `font-size: clamp(to-rem(28px), 4vw, to-rem(40px));`128129## CSS Variables (GT Design System)130131### Colors132- `--bg-base`, `--bg-subtle`, `--bg-elevated`, `--bg-sunken`, `--bg-overlay-heavy`133- `--text-primary`, `--text-secondary`, `--text-muted`, `--text-inverse`134- `--accent`, `--accent-hover`135- `--border-light`, `--border-default`, `--border-medium`136137### Typography138- `--font-sans`, `--font-mono`139- `--fs-xs` to `--fs-4xl`140- `--fw-normal` to `--fw-black`141- `--lh-tight`, `--lh-snug`, `--lh-normal`, `--lh-relaxed`142143### Spacing144- `--space-3xs` to `--space-3xl`145146### Layout147- `--max-width`, `--content-width`148- `--header-height`, `--header-height-mobile`149150### Effects151- `--radius-xs` to `--radius-full`152- `--shadow-xs` to `--shadow-xl`153- `--duration-fast`, `--duration-normal`154- `--ease-out`, `--ease-in-out`155156### Z-Index157- `--z-header`, `--z-modal`, `--z-tooltip`158159## Dynamic Expressions160161Use `{context.path}` syntax in attributes and text content:162163- `{site.url}` - Site URL164- `{site.name}` - Site name165- `{this.title}` - Current post title166- `{this.id}` - Current post ID167- `{user.displayName}` - Current user name168- `{props.myProp}` - Component prop169- `{post.title}` - Loop item field (when itemId is "post")170- `{post.permalink.relative}` - Post permalink171- `{post.featuredImage.url}` - Featured image URL172- `{post.date.dateFormat("M j, Y")}` - Formatted date173- `{post.excerpt | stripTags | truncate:100}` - Filtered excerpt174175## Workflow1761771. Read `etch-skills/generating-layouts.md` for full reference1782. Build JSON structure with blocks and styles (SCSS syntax in `css` fields)1793. Ensure every element has `class`, `metadata.name`, and `styles` with own ID(s)1804. Save as `-etch.json` for pasting into Etch1815. Create `-preview.html` with compiled CSS for browser testing1826. Create `.js` if interactive