Agent Native

block-content

This skill instructs agents to write editable WordPress block markup for block themes, enforcing use of core blocks over HTML, managing layout cascades, and…

by AutomatticOfficial publisherNot reviewed

Identity proven by GitHub's verified organisation badge (Automattic)

Agents
Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode
License
LGPL-2.1 (copyleft)
Pin
870948cpinned Oct 9, 2026
Context cost
about 4,400 tokenswhen the skill loads
GitHub stars
533on automattic/studio
Scripts
None found

What it does

“Write editable WordPress block markup for local Studio sites, including core/html limits, block-theme layout rules, full-width sections, validation, and skeleton-first page/CSS recipes.”
The author's description, from SKILL.md

Generated summary from the skill's own text

This skill instructs agents to write editable WordPress block markup for block themes, enforcing use of core blocks over HTML, managing layout cascades, and validating content before deployment. It covers spacing, typography, colors via theme presets, and skeleton-first recipes for long files.

When to use it

  • Building or editing WordPress page, post, template, or pattern content
  • Implementing block-theme layouts with full-width sections and constrained content
  • Styling blocks with theme palette colors and font-size presets
  • Creating multi-column rows, grids, or cards in block markup

When not to

  • Working with classic WordPress themes or classic editor
  • Building sites that do not use block themes
  • When simpler approaches like custom HTML blocks suffice for your use case

Install

These commands install from the publisher's repository and need no account.

skills CLI

npx skills add automattic/studio --skill block-content

Installs from the repository's default branch, which may have changed since the commit we checked.

GitHub CLI 2.90 or later

gh skill install automattic/studio block-content@870948cd5cbdba4d05a432c38fa5e8e1e796b3b1

Installs the commit we checked (870948c).

Copy by hand

Download the folder at 870948c and put it in .claude/skills/block-content for Claude Code and in .agents/skills/block-content for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.

Skill folder on GitHub at 870948c

Claude Code plugin

/plugin install build-with-wordpress@claude-plugins-official

The publisher's plugin in Claude's official marketplace. It may hold more than this skill, and we have not pinned it.

Copy for your agent

A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.

Prompt

Install the agent skill "block-content" by Automattic.

Source, pinned to commit 870948cd5cbdba4d05a432c38fa5e8e1e796b3b1:
https://github.com/automattic/studio/tree/870948cd5cbdba4d05a432c38fa5e8e1e796b3b1/apps/cli/ai/skills/block-content

1. Download that folder at exactly this commit. Do not use the default branch.
2. Copy every file unchanged, keeping the folder structure, into .claude/skills/block-content/ for Claude Code and .agents/skills/block-content/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist.
3. Do not edit the files, and do not run any script in the folder unless I ask you to.
4. When you are done, list the files you copied and where you put them.

Listing and checks: https://www.agentnative.dev/skills/automattic/block-content

SKILL.md at the pin

---
name: block-content
description: Write editable WordPress block markup for local Studio sites, including core/html limits, block-theme layout rules, full-width sections, validation, and skeleton-first page/CSS recipes.
user-invokable: true
---

# Block Content

Use this skill before writing or editing page content, post content, templates, template parts, patterns, or any other WordPress block markup.

## Core Policy

- Use editable WordPress blocks for content and layout. Prefer `core/group`, `core/columns`, `core/heading`, `core/paragraph`, `core/list`, `core/image`, `core/buttons`, and theme CSS.
- Only use `core/html` blocks for inline SVGs, interaction markup with no block equivalent such as marquee or custom cursor markup, or a single bottom-of-page `<script>` block.
- Never use `core/html` to wrap text content, headings, layout sections, lists, or forms.
- For forms or features core blocks do not cleanly provide, load the `plugin-recommendations` skill and use editable plugin blocks.
- No decorative HTML comments such as `<!-- Hero Section -->` or `<!-- Features -->`. Only WordPress block delimiter comments are allowed.
- WordPress renders the wrapper HTML, not the block comment, so every attribute in the comment must also be serialized on the wrapper: `"align":"full"` → the `alignfull` class, `"backgroundColor":"ink"` → `has-ink-background-color has-background`, a block `style` attribute → the matching inline `style`. An attribute that exists only in the comment renders as nothing and marks the block invalid in the editor.
- No custom class names on inner DOM elements. Put custom classes only on the outermost block wrapper via the block `className` attribute.
- A visual treatment repeated across instances of one block type (cards, outline buttons, framed images) is a registered block style variation, not a shared bare class. Register it in the theme's functions.php — `register_block_style( 'core/group', array( 'name' => 'card', 'label' => 'Card' ) )` — apply it with `"className":"is-style-card"`, and style it in `style.css` via `.is-style-card`. Registered variations appear in the editor's Styles switcher, so users can apply or remove the treatment without touching CSS. Keep bare custom classNames for one-off sections.
- Style buttons via `.wp-element-button` — the inner element WordPress applies the button's padding, background, and border to (shared by the button block and buttons from other blocks). A custom class on a button block sits on the `.wp-block-button` wrapper, so descend to `.your-class .wp-element-button`; never style the wrapper directly, or its padding stacks on top of the default and the button doubles in size.
- No inline `style` attributes or block `style` attributes for styling. Use `className` plus the theme's `style.css`.
- Prefer theme palette colors over hardcoded hex. Apply block colors with palette **slug** attributes — `{"backgroundColor":"accent-1","textColor":"base"}` — and in `style.css` reference palette colors as `var(--wp--preset--color--<slug>)`. Discover the available slugs from the active theme's `theme.json` `settings.color.palette` (for a theme you are building, the palette you defined there); when you want a color the palette lacks, prefer adding it to the palette and referencing its slug. Keeping colors on the palette keeps sections in sync with Global Styles, theme switching, and light/dark variations. A raw hex value is fine for a deliberate one-off, but it should be the exception, not the default.
- Prefer theme font-size presets over raw values, the same way as palette colors. Apply text sizes with slug attributes — `{"fontSize":"large"}` — and in `style.css` reference them as `var(--wp--preset--font-size--<slug>)`. Discover the slugs from the active theme's `theme.json` `settings.typography.fontSizes`; when a size the scale lacks recurs, add it to the scale (e.g. an `x-small` preset) and reference its slug. A raw size is fine for a deliberate one-off, but a value repeated across blocks belongs in the scale.
- Use `core/spacer` for empty spacing elements, not empty `core/group` blocks.
- No emojis anywhere in generated content.

## Layout Cascade

WordPress constrains children of `core/post-content` and any constrained-layout container to `theme.json`'s `settings.layout.contentSize`. Custom CSS such as `.hero { width: 100% }` does not override core layout selectors like `.is-layout-constrained > *:not(.alignwide):not(.alignfull)` because they are more specific.

**There is no core default for `contentSize`.** When the active theme's `theme.json` declares neither `settings.layout.contentSize` nor `wideSize`, WordPress omits the `max-width` declaration from those constrained-layout rules altogether — `layout: {"type":"constrained"}` then constrains nothing and every block runs the full width of its container. Themes created with `scaffold_theme` declare both, along with `useRootPaddingAwareAlignments` and `styles.spacing.padding` for the horizontal gutter. Keep all three when you edit `theme.json`; retune the values to suit the design, but do not drop them. In a theme that lacks them, add them there rather than patching widths and padding section by section in CSS.

Use these patterns:

- **Full-bleed section, constrained inner content**: for a full-width hero, banner, or CTA with centered content, use an outer `core/group` with `{"align":"full","layout":{"type":"constrained"}}`, then place normal inner blocks inside it.
- **Full-bleed section, wide inner content**: for multi-column rows, card grids, feature grids, and galleries, keep the same outer group and add `"align":"wide"` on the inner `core/columns`, `core/group`, or `core/gallery` so it spans `wideSize` instead of the reading column. Without it, a three-column row is squeezed into `contentSize`. Reserve the plain reading column for text-only blocks.
- **Full-bleed section, full-bleed inner content**: for image grids, edge-to-edge galleries, and similar layouts, use outer and inner `core/group` blocks with `{"align":"full","layout":{"type":"default"}}`.
- **Standard constrained content**: omit `align` and write normal blocks.

The common failures are a hero or banner that was intended to be full-width but still renders in the narrow content column, and a columns or grid block left at the reading width when it should be `"align":"wide"`. Fix that in markup by adding `align: "full"` on the outer group or correcting the inner `layout` type, not by trying to force width in CSS.

### Horizontal Padding

WordPress supplies horizontal padding in exactly one place: the root gutter (`styles.spacing.padding`) lands on full-width constrained groups via `.has-global-padding`, and core zeroes it again on constrained groups nested inside them. Every other box gets none — a full-width section with a `default`, `flex`, or `grid` layout, and any group, column, or cover that paints its own background, border, or shadow (a card, a callout, a tinted panel). Block themes do not load core's default `.has-background` padding either. Text sitting flush against its own background, border, or the viewport edge is the most common tell of an unfinished section, so:

- Whenever a block paints its own box, give its class horizontal padding in `style.css` — `.is-style-card { padding: 1.5rem; }`.
- Give a non-constrained full-width section the same gutter as the rest of the page — `padding-inline: var(--wp--style--root--padding-left);` — or place its text inside a constrained inner group, which keeps the gutter when it sits directly in a full-width `default` group.

### Shrink-Wrapped Labels (Eyebrows, Badges, Pills)

Constrained layouts keep children in the content column with `max-width` plus `margin-left/right: auto !important`. Auto margins only work on block-level boxes, so setting `display: inline-block` (or any `inline-*` value) on a block's wrapper class detaches it from the content column — it aligns to the edge of the full-width section instead. `width: fit-content` fails differently: the auto margins always center it, so it cannot sit left or right within the column.

Never change the `display` of a block wrapper inside a constrained layout. To make a label hug its text — an eyebrow, badge, tag, or pill — wrap it in a flex row group and let flex do the shrink-wrapping:

```html
<!-- wp:group {"layout":{"type":"flex"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"className":"hero-eyebrow"} -->
<p class="hero-eyebrow">New for 2026</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group -->
```

The group's `justifyContent` (`left`/`center`/`right`) controls where the label sits, and stays editable in the editor. Inside the flex row the label already shrink-wraps — flex items size to their content — so the label class never needs a `display` rule:

```css
/* Wrong — never an inline-level display on a block wrapper class */
.hero-eyebrow { display: inline-block; }
```

This holds even when it looks harmless: inside a flex row an `inline-block` or `inline-flex` declaration changes nothing (flex items blockify), but it becomes a live alignment bug the moment the user moves the block out of the wrapper in the editor. If the class already carries a `display` rule from an earlier pass, remove it when adding the wrapper.

## Root Block Gap

WordPress inserts a `margin-block-start` equal to the block gap between the top-level children of the rendered template — between the header template part, the main group, and the footer template part (`:where(.wp-site-blocks) > *`). Core supplies a default gap (24px) even when the theme's `theme.json` never declares `styles.spacing.blockGap`, so a gap appears there that no markup asked for.

- Themes created with `scaffold_theme` already zero this in `style.css` (`.wp-site-blocks > * + * { margin-block-start: 0; }`) — sections butt edge-to-edge and own their vertical rhythm via their own padding, and `main` gets its padding back through the `.wp-site-blocks main` rule next to it so templates the theme does not author (plugin templates) still clear the header and footer. Keep both rules when editing the file.
- When working in a theme without that reset, add the same rules to the theme's `style.css` instead of compensating with negative margins or guessing at the extra space.
- Do not zero the gap by setting `styles.spacing.blockGap: "0"` in `theme.json` — that value cascades as the default gap inside every flow and constrained layout and collapses content rhythm site-wide.
- When you want visible space between top-level sections, add it deliberately (padding on the sections) so the spacing is designed, not inherited.

## Page Titles

`templates/page.html` renders `core/post-title` as the page's `h1`, above `core/post-content`. Page content must not repeat that title — a designed hero with its own heading stacked under the template's title gives the page a stray "Home" above the hero and **two `h1` elements**, which is an accessibility and SEO problem, not just a visual duplication.

Pick one of the two per page:

- **Ordinary page** (Privacy Policy, Terms, a plain About): let the template's title stand and start the page content at `h2`.
- **Designed page** whose hero carries its own heading (home, landing, anything built from sections): assign the no-title template. Themes from `scaffold_theme` ship `templates/page-no-title.html`, registered in `theme.json` under `customTemplates`. Assign it to the page after creating it:

```text
wp_cli post meta update <id> _wp_page_template page-no-title
```

Do **not** solve this by deleting `core/post-title` from `page.html` — every ordinary page then renders untitled, and a missing `h1` is worse than a duplicated one. In a theme with no no-title template, add one (a copy of `page.html` without the title group) and register it in `customTemplates` rather than stripping the title.

A site's home page is the usual case for this: with a static front page and no `front-page.html`, WordPress falls through to `page.html`, so the page least likely to want a title block gets one by default.

**Do not add a `front-page.html` to solve it.** That template overrides *every* other template for the front page — the hierarchy is `front-page → home → index` — and it applies whether the front page shows a static page or the latest posts. One containing `core/post-content` therefore breaks a blog-first site, whose front page belongs to `home.html`/`index.html`. Assigning `page-no-title` to the home page achieves the same result with no hierarchy side effects. Add a `front-page.html` only when the front page needs a structure that genuinely differs from a page's, and the site's front page is definitely static.

## Anchor Links in Shared Parts

Header and footer template parts render on **every** template — blog posts, archives, search results, the 404 page — not just the front page, even on a one-page site. A bare-hash link such as `"url":"#contact"` resolves against whatever URL the visitor is on, so it works on the front page and silently does nothing everywhere else.

- In template parts and any other shared markup, write anchor links home-relative: `<!-- wp:navigation-link {"label":"Contact","url":"/#contact"} /-->`.
- Give each target section an `anchor` attribute on its outermost block — `{"anchor":"contact","align":"full"}` renders `id="contact"` — rather than hand-writing an `id` in `core/html`.
- A bare hash is fine only for a link living in the same page's own content, such as a hero button scrolling to a section further down that page.

## Skeleton-First Recipes

For long files over about 200 lines, write a small skeleton first and fill anchors across later `Edit` calls.

### Theme CSS

For `style.css`, start with custom properties and anchor comments only:

```css
:root {
	/* Map section variables onto the theme palette — reference preset slugs,
	   never hardcode hex here. The slugs come from theme.json's color palette. */
	--site-bg: var( --wp--preset--color--base );
	--site-text: var( --wp--preset--color--contrast );
}

/* === reset === */
/* === typography === */
/* === hero === */
/* === sections === */
/* === cta === */
/* === footer === */
/* === responsive === */
```

Keep the skeleton under 2KB. Fill the anchors with as few `Edit` calls as the payload limit allows: one entry in `edits[]` per anchor, its `oldText` the anchor line and its `newText` the anchor plus the new styles, several entries per call and under ~8KB of new text per call. A typical `style.css` takes two or three such calls, never one call per anchor. When filling section styles, never set `display` or width on a class used as a block `className` — alignment and shrink-wrapping belong in block markup (see Shrink-Wrapped Labels).

When `scaffold_theme` was used, do not `Write` over the scaffolded `style.css`; it already contains the required theme header. Use `Edit` to append the `:root` block and anchor comments below the existing content.

### Page Content

For long page content:

1. Create the page empty:

```text
wp_cli post create --post_content=""
```

2. Write `<site>/tmp/page-<slug>.html`, not a file inside the theme, with small section anchors:

```html
<!-- section:hero -->
<!-- section:features -->
<!-- section:cta -->
```

3. Fill the anchors with editable blocks, several per `Edit` call: one `edits[]` entry per section, under ~8KB of new text per call. Never wrap a section in `core/html`.
4. **Validate before applying (mandatory gate).** Once all anchors are filled, you MUST call `validate_blocks` with `filePath` pointing at `<site>/tmp/page-<slug>.html` and get a passing result — the core/html policy passes and editor validation reports all blocks valid. This is not optional and not a step you can defer to after `wp_cli eval`: the scratch file is block content, so it must be validated as a file while it still lives in a file. If validation reports invalid blocks, fix them in the file and call `validate_blocks` again until it passes. Never apply block content you have not validated.
5. Apply the validated content once:

```text
wp_cli eval '$content = file_get_contents(ABSPATH . "tmp/page-<slug>.html"); wp_update_post(["ID" => <id>, "post_content" => $content]); echo "ok";'
```

Do not use `--post_content-file=<path>`: it is not a WP-CLI option, so WP-CLI never reads the file and the post content is not updated.

## Validation

- Validation is a mandatory gate, not a cleanup step. You MUST call `validate_blocks` and get a passing result for any block content you generate **before** that content reaches the live site — before `wp_cli post create/update`, before `wp_cli eval`, and before importing a scratch file. Never apply, import, or save block content you have not validated. A build that skips validation is incomplete, even if the page renders.
- Run `validate_blocks` after every write or edit that creates or changes block content. Call it with `filePath` whenever the content lives in a file — including scratch files such as `<site>/tmp/page-<slug>.html` that you later import with `wp_cli eval`. The scratch file is the block content; validate the file, not just the eventual post. Theme `templates/*.html` and `parts/*.html` files count too — they are live as soon as they are written, so validate each one right after every write or edit. It first runs a static `core/html` policy check: if that reports invalid `core/html` blocks, editor validation is skipped — rewrite only those blocks as editable core or plugin blocks, then call `validate_blocks` again. Once the policy passes it validates in the live editor and applies safe serialization fixes directly to the file. If it says an auto-fix was applied, do not manually replace markup or call validation again unless you intentionally change block markup afterward. Use the diff only to inspect structural changes for CSS impact. Classes added or removed by the validator can affect layout and styling.

From automattic/studio at 870948c, shown as plain text under its license: LGPL-2.1 (copyleft). The license text is in apps/cli/LICENSE.md.

View SKILL.md on GitHub at 870948c

Files

1 file, 18 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.

Skill folder on GitHub at 870948c
All 31 skills from Automattic

More from Automattic

  • annotate

    This skill opens a browser with annotation tools that let users click page elements and leave feedback, then the agent reads those annotations and applies the…

    Claude Code, Codex, Cursor +3 · about 1K tokens · pinned 6 days ago

  • hosting-plans-helper

    This skill fetches current WordPress.com plan names, pricing, and feature details from live APIs, then answers questions about which plan tier includes…

    Claude Code, Codex, Cursor +3 · about 840 tokens · pinned 6 days ago

  • imagery

    This skill guides generating AI images for WordPress sites using the generate_images tool, or alternative image generation tools.

    Claude Code, Codex, Cursor +3 · about 2.6K tokens · pinned 6 days ago

  • liberate

    This skill helps agents migrate websites into WordPress by copying the site from a URL or local files using the Studio CLI, then comparing the imported…

    Claude Code, Codex, Cursor +4 · about 830 tokens · pinned 6 days ago

  • need-for-speed

    Measures Core Web Vitals and page composition metrics for WordPress sites using synthetic testing, then suggests targeted optimization actions based on…

    Claude Code, Codex, Cursor +3 · about 650 tokens · pinned 6 days ago

  • plugin-recommendations

    This skill guides the agent to recommend and install WordPress plugins for features that core blocks don't provide, including WooCommerce for ecommerce…

    Claude Code, Codex, Cursor +3 · about 4.6K tokens · pinned 6 days ago

  • wpcom-remote-management

    This skill enables agents to manage live WordPress.com sites through the wpcom_request tool, handling plan-based feature limitations, content and design…

    by AutomatticOfficial publisher

    Claude Code, Codex, Cursor +3 · about 1.7K tokens · pinned 6 days ago

  • wp-block-development

    This skill guides agents through WordPress block development workflows, including creating new blocks, updating block.json metadata, implementing save/render…

    by AutomatticOfficial publisher

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 1.5K tokens · pinned 6 days ago

  • wp-block-themes

    This skill enables agents to develop and debug WordPress block themes by editing theme.json for global settings and styles, managing templates and template…

    by AutomatticOfficial publisher

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 890 tokens · pinned 6 days ago

  • wp-interactivity-api

    This skill helps build and debug WordPress Interactivity API features, including data-wp-* directives, store management, and block view script modules.

    by AutomatticOfficial publisher

    Claude Code, Codex, Cursor +4 · about 1.4K tokens · pinned 6 days ago

  • wpds

    This skill helps agents build and review UIs using the WordPress Design System, including components, tokens, and patterns.

    by AutomatticOfficial publisher

    Claude Code, Codex, Cursor +4 · about 640 tokens · pinned 6 days ago

  • wordpress-block-theming

    This skill provides guidance for building WordPress block themes using Full Site Editing architecture.

    by AutomatticOfficial publisher

    Claude Code, Codex, Cursor +4 · about 4.7K tokens · pinned 6 days ago

The skills listed here are third-party software, collected from public GitHub repositories as a catalog of recent skills and provided as is. Agent Native does not write or control them and is not responsible for any harm, loss or damage caused by installing or using them. Labels and checks describe what we saw at one commit and are not a guarantee. Read a skill's files before you run it. Terms of use