A metal guardrail running along the edge of a bridge

Guide

Claude Code rules for Elementor sites

Deny writes to Elementor's generated CSS and gate destructive WP-CLI behind an ask rule. The failure mode isn't a broken page — it's one the client can't edit.

Published August 10, 2026

Put the editability rules in CLAUDE.md, then deny writes to Elementor's generated CSS and gate destructive WP-CLI behind an ask rule. On a page-builder site the failure mode is not a broken page — it is a page that renders perfectly and can no longer be edited by the person who owns it.

The thing you are actually protecting

A client on Elementor is paying for the ability to change their own site. Every piece of layout an agent writes as raw markup in a PHP template, or buries in an HTML widget, is a piece of the site that has silently returned to being developer-only work. Nothing fails. No test catches it. The page just quietly costs money to change again.

So the rules have two jobs: teach the agent to build in the editor's vocabulary — widgets, global styles, saved templates — and make the shortcuts physically unavailable. Claude Code can do the second part properly, because permissions and hooks operate at the tool layer rather than the context layer.

The ruleset

CLAUDE.md
# Elementor site — rules for Claude

The client edits this site visually. Markup that is not a widget is markup
they cannot change. Editability is a requirement, not a preference.

## Generated output — read, never write

- `wp-content/uploads/elementor/css/**` (`post-*.css`, `global.css`) is
  generated. Elementor > Tools > Regenerate CSS & Data overwrites it.
  Read it to find a selector; never edit it.
- The `_elementor_data` postmeta is slash-escaped JSON holding the page
  layout. Never hand-edit it. Bulk changes go through WP-CLI, after an
  export, and never without asking.

## Build in the editor's vocabulary

- Colours, fonts, spacing: **Site Settings > Global Colors / Global Fonts**.
  Never a hex value hard-coded in a template.
- Repeated blocks: **saved templates or global widgets**, not copy-paste.
- Anything with values a client might change: a **registered widget with
  controls**, so they get fields instead of hard-coded text.
- Real custom CSS: the child theme, enqueued on
  `elementor/frontend/after_enqueue_styles` — never inside a page's
  Custom CSS box, where it is invisible to version control.
- **No `!important`.** It beats the editor's own controls, so the client
  changes a setting in the UI and nothing happens.

## Performance still applies

This build holds 99 Performance with 0 ms Total Blocking Time on mobile,
throttled. Do not add a slider plugin, a second icon library, or a CDN font
to solve a layout problem. The plugin list has to justify itself.

## When a request cannot be met inside these rules

Say so and stop. Do not add layout HTML to a template as a workaround.

Making the shortcut unavailable

The generated CSS is the interesting case, because it is genuinely the fastest fix. It contains the exact selector, editing it works instantly, and the change survives until someone hits Regenerate — possibly months later, at which point nobody connects the regression to the fix. A deny rule takes it off the table without the agent needing to remember why.

.claude/settings.json
{
  "permissions": {
    "deny": [
      "Edit(wp-content/uploads/**)",
      "Write(wp-content/uploads/**)"
    ],
    "ask": [
      "Bash(wp db *)",
      "Bash(wp post meta update *)",
      "Bash(wp plugin install *)",
      "Bash(wp plugin activate *)"
    ],
    "allow": [
      "Bash(wp post list *)",
      "Bash(wp post meta get *)",
      "Bash(wp plugin list *)",
      "Bash(wp elementor *)"
    ]
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/guard-elementor.sh",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Note which WP-CLI verbs sit where. The read-only ones are allowed outright so the agent can investigate without a permission prompt every thirty seconds — an agent that cannot look things up will guess instead. The destructive ones are ask rather than deny, because bulk edits through WP-CLI are legitimate work; they just deserve a human pause.

.claude/hooks/guard-elementor.sh — blocks !important and inline layout markup
#!/usr/bin/env bash
# PreToolUse receives the tool call as JSON on stdin. Exit 2 blocks the call
# and returns stderr to the model as the reason.
payload="$(cat)"
file="$(printf '%s' "$payload" | jq -r '.tool_input.file_path // empty')"
content="$(printf '%s' "$payload" | jq -r '.tool_input.new_string // .tool_input.content // empty')"

case "$file" in
  *.css)
    if printf '%s' "$content" | grep -q '!important'; then
      echo "!important overrides the Elementor controls the client uses --" >&2
      echo "raise specificity, or set the value as a Global Style instead." >&2
      exit 2
    fi
    ;;
  *wp-content/themes/*.php)
    # A grid or flex container in a PHP template is layout the editor cannot see.
    if printf '%s' "$content" | grep -qE 'class="[^"]*(grid|row|col-|flex)[^"]*"'; then
      echo "Layout markup in a PHP template is invisible in the Elementor" >&2
      echo "editor. Build it as a registered widget with controls instead." >&2
      exit 2
    fi
    ;;
esac
exit 0

That second check is deliberately blunt and will occasionally fire on something legitimate. That is the correct trade for this rule: a false positive costs one sentence of explanation, and a false negative costs a section the client cannot edit and nobody notices for a month.

Verifying the site is still editable

The rules stop the regression; nothing so far proves the site is actually editable. That check needs a browser, which means an MCP server — configure one in .mcp.json at the project root so the whole team gets it rather than one machine:

.mcp.json
{
  "mcpServers": {
    "chrome-devtools": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "chrome-devtools-mcp@latest"]
    }
  }
}

With that in place the verification step is a real one: open the page in the Elementor editor, confirm the new section appears as a widget in the panel rather than as an opaque HTML block, and change one value to confirm it takes effect. If the section cannot be selected in the editor, the work is not done — however good the front end looks.

The same setup in Cursor is here — same rules, enforced at commit time rather than at the tool call, because rules there are context rather than permissions. And the performance half of this discipline, which applies to any stack, is in the Core Web Vitals rules.

Rules stop an agent making a page-builder site slower. Making one genuinely fast in the first place is the WordPress and Elementor work itself — held to a performance budget from the first template.

Questions

Why a deny rule rather than just telling it not to?

Because the generated CSS under uploads/elementor/css is genuinely tempting: it contains the exact selector the agent needs, editing it makes the bug disappear immediately, and the change survives until someone regenerates. A deny rule removes the temptation from the tool layer entirely, which is more reliable than removing it from the model's attention.

Can I still let it run WP-CLI?

Yes, and you should — WP-CLI is how legitimate bulk work gets done. Allow the read-only verbs freely and put the destructive ones behind an ask rule: wp db query and wp post meta update can rewrite _elementor_data across a site, and that is a decision worth a human pause even when the plan is correct.

What actually breaks if _elementor_data gets corrupted?

The page opens empty in the editor. The content is still in the database, but Elementor cannot parse it, so the client sees a blank canvas and assumes the page is gone. Recovery means restoring that postmeta from a backup, which is why any operation touching it should be preceded by an export.

Written by

Karol

Senior engineer and systems architect behind Tall Karol. Everything published here is grounded in real client work — no roundups, no tools that haven't run in production.

Why Tall KarolWork with Tall Karol

Related notes

More on web development

Related service: Web Development

Want this kind of engineering on your project?

Tall Karol takes on fractional and project-based engagements for startups and agencies.

Book a working session