Building Hyva Templates with AI Assistance
AI generated
Claude
>_
Claude Code · Hyva Theme · Alpine.js · Tailwind CSS
Building Hyva Templates with AI Assistance
From prompt to CSP-compliant phtml template

Claude Code can sketch a new Hyva template in seconds, but only with the right project conventions as context does it produce genuinely usable code. This article shows how to scaffold a phtml template with a matching view model, generate Alpine.js components that stay CSP compliant, and verify AI-suggested Tailwind classes against the actual design system before they reach production.

16 min. read Templates · Alpine.js · CSP Claude Code · Tailwind CSS · Magento 2.4.8

1. What building Hyva templates with AI assistance actually means

Building Hyva templates with AI assistance does not mean that Claude Code turns a short description into a production-ready phtml template. It means using a language model deliberately for the parts of the work that are clearly structured: creating the base file following an existing pattern, wiring up a view model through layout XML, and drafting a first Alpine.js component. The difference between a usable and an unusable result almost always comes down to the context Claude Code receives before generation, not to the model itself.

Without context, a language model falls back on generic knowledge about Tailwind, Alpine.js, and PHP, which often only partially matches the actual conventions of a specific Hyva project. This shows up most clearly in three places: the structure of a phtml template with view model access, the way Alpine.js components are wired up in a CSP-compliant manner, and the question of which Tailwind classes actually exist in the project. The following sections cover exactly these three areas in detail, each with a practical example from a running Magento 2 project.

2. Project conventions as context for Claude Code

A Hyva theme typically inherits from a parent theme such as hyva-themes/magento2-default-theme-csp and deliberately overrides individual templates. This inheritance structure is not visible to a language model without additional context. Asking Claude Code to simply generate a new product card template usually produces something technically functional but stylistically foreign, because neither the escaper calls the project actually uses, nor the view model naming convention, nor the Tailwind classes actually in use are known to the model.

The most reliable way to provide this context is to let Claude Code read two or three existing, similar templates from the theme directly before a new file is created. A CLAUDE.md file with clear rules about block iteration, CSP registration, and mandatory escaping, as already maintained in this project, helps further. This combination of concrete example files and explicit rules produces noticeably more consistent results than a purely textual description of the desired conventions.

3. Practical example: scaffolding a new template

A new template for a product review summary that should appear above the description on the product detail page serves as an example. The task given to Claude Code covered three parts: a view model interface for the review data, a matching layout XML fragment, and the actual phtml template. It was important that Claude Code first searched comparable templates in the Magento_Catalog/templates/product/view directory before generation, to adopt the actual structure of view model access and escaper calls.

The result was a template that did not replace the block child iteration but respected it, consistently used the escaper for output data, and matched the indentation and PHPDoc structure of the neighboring files. What mattered here was less the speed of generation and more the fact that a manual review needed almost no adjustments to conventions, because those conventions were already present in the prompt context.


#!/usr/bin/env bash
# scaffold-context.sh - collect project conventions before asking
# Claude Code to scaffold a new Hyva template
set -euo pipefail

THEME_DIR="app/design/frontend/Mironsoft/default"
TARGET_DIR="$THEME_DIR/Magento_Catalog/templates/product/view"

echo "[1/3] Listing existing sibling templates for style reference..."
find "$TARGET_DIR" -maxdepth 1 -name "*.phtml" -print

echo "[2/3] Extracting ViewModel access pattern from an existing template..."
grep -n "viewModels\|escapeHtml\|getChildNames" "$TARGET_DIR/description.phtml"

echo "[3/3] Writing the new template following the same structure..."
mkdir -p "$TARGET_DIR"
cat <<'PHTML' > "$TARGET_DIR/review-summary.phtml"
<?php

/** @var \Magento\Framework\View\Block\Template $block */
/** @var \Magento\Framework\Escaper $escaper */
/** @var \Mironsoft\ReviewSummary\ViewModel\ReviewSummary $viewModel */
$viewModel = $viewModels->require(\Mironsoft\ReviewSummary\ViewModel\ReviewSummary::class);
?>
<div class="mb-6" x-data="reviewSummary()">
  <h3 class="text-lg font-semibold text-gray-900">
    <?= $escaper->escapeHtml(__('Customer Ratings')) ?>
  </h3>
  <?php foreach ($block->getChildNames() as $childName): ?>
    <?= $block->getChildHtml($childName) ?>
  <?php endforeach; ?>
</div>
PHTML

echo "Template scaffolded, run bin/phpcs on the new file next."

4. Generating Alpine.js components

For the interactivity of the review summary, a collapsible area with a star rating filter, Claude Code was asked to produce an Alpine.js component following the pattern already used in the project. Hyva themes typically register components through Alpine.data() in a separate JavaScript file rather than as an inline object directly in the x-data attribute, as soon as the logic exceeds a few lines. This pattern keeps phtml templates readable and makes the component reusable.

Claude Code can implement this separation reliably when the prompt explicitly asks for it and an existing example, such as the mobile footer collapse component from footer.phtml, is provided as a reference. Without this hint, the model tends to write the entire logic as an inline object in x-data, which is unproblematic for simple components but quickly becomes hard to follow for more complex filters and makes testing harder.


// web/js/review-summary.js
// Alpine.js component for the collapsible review summary block.
// Registered via Alpine.data() so the phtml template only references
// the component name, keeping markup and logic separated.
document.addEventListener('alpine:init', () => {
  Alpine.data('reviewSummary', () => ({
    expanded: false,
    activeStarFilter: null,

    toggleExpanded() {
      this.expanded = !this.expanded;
    },

    setStarFilter(stars) {
      this.activeStarFilter = this.activeStarFilter === stars ? null : stars;
      this.$dispatch('review-filter-changed', { stars: this.activeStarFilter });
    },

    isFilterActive(stars) {
      return this.activeStarFilter === stars;
    }
  }));
});

5. Ensuring CSP compliance

Hyva themes with Content Security Policy enabled forbid inline event handlers such as onclick and require every inline script block to be explicitly whitelisted through the Hyva CSP component's registerInlineScript(). A model trained mostly on generic Alpine.js code occasionally suggests patterns that would be unproblematic in a standard web application but get blocked by the browser policy in a CSP-strict Hyva theme, for example an onclick attribute instead of x-on:click, or an inline script without an accompanying PHP call.

The most reliable protection against such mistakes is an automated check after every AI-assisted template generation that deliberately looks for forbidden patterns instead of relying solely on prompt discipline. A simple shell script that uses ripgrep to search for on-attributes and unregistered script blocks reliably catches the most common violations before the change enters the review process.


#!/usr/bin/env bash
# csp-audit.sh - detect common CSP violations in a freshly
# AI-generated Hyva template before it enters code review
set -euo pipefail

TEMPLATE="$1"

echo "[1/3] Checking for forbidden inline event handler attributes..."
if grep -nE 'on(click|load|change|submit)=' "$TEMPLATE"; then
  echo "[FAIL] Inline event handler found, use x-on:event instead" >&2
  exit 1
fi

echo "[2/3] Checking every inline <script> block is CSP-registered..."
script_count=$(grep -c '<script type="text/plain">' "$TEMPLATE" || true)
register_count=$(grep -c 'registerInlineScript' "$TEMPLATE" || true)
if [[ "$script_count" -gt "$register_count" ]]; then
  echo "[FAIL] Inline script without matching registerInlineScript() call" >&2
  exit 1
fi

echo "[3/3] Checking for eval or new Function usage..."
grep -nE 'eval\(|new Function\(' "$TEMPLATE" && exit 1

echo "[OK] No obvious CSP violations found in $TEMPLATE"

6. Verifying Tailwind classes instead of inventing them

A recurring problem in AI-generated Hyva templates is Tailwind classes that look syntactically correct but do not actually exist in the project, because they are neither defined in tailwind.config.js nor covered by the utility classes used elsewhere in the theme. A typical example is bg-primary-500, when the project actually uses a custom color palette with names like brand-orange or a numeric scale like orange-600. Because Tailwind's version 4 CSS-first approach only generates classes that actually appear in the source and match the configuration, an invented class does not raise an error, it simply results in missing styling.

This type of mistake is especially tricky because it only becomes visible during a visual review and is not caught by PHPStan or a PHP lint run. A small Python script that extracts all classes used in the generated template and diffs them against a list of classes actually used elsewhere in the project makes this check reproducible and integrates easily into the build step, right after the Tailwind build and before the static content deploy.


#!/usr/bin/env python3
# verify_tailwind_classes.py - flag Tailwind classes suggested by an
# AI model that do not exist anywhere else in the project design system
import re
import sys
from pathlib import Path

CLASS_ATTR = re.compile(r'class="([^"]+)"')

def extract_classes(file_path: Path) -> set[str]:
    text = file_path.read_text(encoding="utf-8")
    found: set[str] = set()
    for match in CLASS_ATTR.finditer(text):
        found.update(match.group(1).split())
    return found

def load_known_classes(theme_dir: Path) -> set[str]:
    known: set[str] = set()
    for phtml_file in theme_dir.rglob("*.phtml"):
        known.update(extract_classes(phtml_file))
    return known

if __name__ == "__main__":
    new_template = Path(sys.argv[1])
    theme_root = Path("app/design/frontend/Mironsoft/default")

    known_classes = load_known_classes(theme_root)
    new_classes = extract_classes(new_template)
    unknown = sorted(new_classes - known_classes)

    if unknown:
        print(f"Unverified Tailwind classes in {new_template}:")
        for class_name in unknown:
            print(f"  - {class_name}")
        sys.exit(1)

    print(f"All classes in {new_template} match existing project usage.")

7. Common mistakes in AI-generated Hyva templates

Beyond invented Tailwind classes and CSP violations, a few other patterns show up repeatedly in AI-generated Hyva templates. The most common one: leftover jQuery selectors or Knockout bindings from generic Magento 2 training material, even though the project consistently relies on Alpine.js and loads neither jQuery nor UI components. A second pattern is replacing the getChildNames() iteration with hardcoded child block calls, which effectively defeats the Hyva block system and makes later layout extensions through XML harder.

A third, more subtle problem concerns missing escaping. Claude Code escapes output reliably once the context already shows that escapeHtml() or escapeHtmlAttr() are the project standard, but it occasionally forgets this for newly introduced variables, such as a review text loaded from an external API. A manual look at every unescaped output variable therefore remains mandatory, regardless of how reliable the model was elsewhere.

8. A safe workflow: context files and prompting

A repeatable workflow for AI-assisted Hyva templating does not start with the prompt for the new template, it starts with assembling the context: two or three structurally similar existing templates, the relevant sections of CLAUDE.md, an excerpt from tailwind.config.js, and, if available, the existing Alpine.js reference component. Only after that comes the actual task description with clearly stated requirements such as CSP compliance, using only existing design tokens, and preserving block iteration.

After generation, the same review rhythm applies as for any other AI-assisted change: read the diff in full, run the CSP audit script, check Tailwind classes against the design system, and visually inspect the template in the browser before it goes into the deploy process. This rhythm costs a few extra minutes for a single template, but it reliably prevents CSP violations or unstyled areas from only surfacing in production.


{
  "task": "scaffold-hyva-template",
  "target_directory": "app/design/frontend/Mironsoft/default/Magento_Catalog/templates/product/view",
  "new_file": "review-summary.phtml",
  "context_files": [
    "Magento_Catalog/templates/product/view/description.phtml",
    "Magento_Theme/templates/page/js/sticky-header.phtml",
    "CLAUDE.md#coding-standards",
    "web/tailwind/tailwind.config.js"
  ],
  "requirements": {
    "csp_compliant": true,
    "no_jquery": true,
    "no_knockout": true,
    "preserve_block_iteration": true,
    "escaper_required_for_all_output": true,
    "tailwind_classes": "existing project classes only, no invented utility values"
  },
  "verification": {
    "csp_audit": "bin/bash csp-audit.sh review-summary.phtml",
    "tailwind_check": "python3 verify_tailwind_classes.py review-summary.phtml",
    "static_analysis": "bin/analyse app/code/Mironsoft/ReviewSummary --level=5"
  }
}

9. Hyva templates compared: AI suggestion vs. project convention

The following overview summarizes the most common differences between an unreviewed AI suggestion and the actual project convention. It is based on patterns observed repeatedly across several Hyva scaffolding tasks and serves as a checklist for the review after every AI-assisted template generation.

Area Unreviewed AI Suggestion Project Convention Why It Matters
Interactivity jQuery selector or Knockout binding Alpine.data() component The theme deliberately loads neither jQuery nor UI components
Event handling onclick="..." attribute x-on:click with a registered function Inline handlers are blocked by the CSP
Block structure Hardcoded child block call $block->getChildNames() iteration Keeps layout extensibility through XML intact
Color classes bg-primary-500 (does not exist) bg-orange-600 (defined palette) Invented classes produce no CSS output
Output <?= $data['title'] ?> without escaper $escaper->escapeHtml($title) Prevents XSS from externally loaded data

What stands out is that almost all the deviations in the table are not caused by a poorly written prompt, they are caused by missing context about the specific project. A model that has never seen an example file with getChildNames() cannot reliably apply that convention, no matter how precisely the textual description is worded.

Mironsoft

Hyva frontend development with structured AI usage

Hyva templates that actually fit your codebase?

We use Claude Code deliberately for scaffolding new Hyva templates and back every result with a CSP audit, Tailwind class verification, and a full diff review, so your frontend stays consistent and CSP compliant.

Template Scaffolding

Create new phtml templates that follow your existing conventions

CSP Audit

Check Alpine.js components for forbidden inline patterns

Design System Check

Verify Tailwind classes against your actual theme

10. Summary

Building Hyva templates with AI assistance works reliably for scaffolding new files, deriving view model structures from existing examples, and drafting first Alpine.js components, as long as Claude Code receives enough context about the actual project conventions before generation. Without that context, templates end up technically functional but stylistically and sometimes functionally off, particularly with regard to CSP compliance and the use of Tailwind classes that actually exist.

Three checks should be a fixed part of the review after every AI-assisted template generation: a CSP audit for forbidden inline patterns, a comparison of used Tailwind classes against the design system, and a manual look at escaping and block iteration. Anyone who consistently applies these three checks can put Claude Code to productive use for Hyva frontend work without accepting stylistic or security-relevant regressions.

Building Hyva Templates with AI Assistance - The Essentials at a Glance

Context Before Prompt

Provide existing templates, CLAUDE.md, and the Tailwind config before generation, do not just describe them.

CSP Audit

Check every generated template for inline handlers and missing registerInlineScript() calls.

Tailwind Verification

Compare suggested classes against classes actually used in the project instead of trusting them blindly.

Preserve the Block System

getChildNames() iteration and mandatory escaping must not be replaced by AI suggestions.

11. FAQ: Building Hyva Templates with AI Assistance

1What does it mean to build a Hyva template with AI assistance?
Using Claude Code deliberately for clearly structured parts, such as creating the base file following an existing pattern or wiring up a view model, instead of generating everything without context.
2Why isn't a plain text description enough as context?
Without concrete example files the model knows neither view model naming conventions nor Tailwind classes actually in use. Letting it read existing templates directly produces more consistent results.
3How do you get Claude Code to produce CSP-compliant Alpine.js components?
Provide an existing CSP-compliant example as a reference and explicitly ask for Alpine.data() registration instead of inline handlers.
4What is the most common CSP mistake in AI-generated templates?
An onclick attribute instead of x-on:click, or an inline script without a registerInlineScript() call. Both get blocked in a CSP-strict Hyva theme.
5Why don't invented Tailwind classes show up in a PHP lint run?
A non-existent class is syntactically valid HTML. PHPStan checks PHP code, not CSS output, so missing styling stays undetected until a visual review.
6How do you verify AI-suggested Tailwind classes against the design system?
With a script that extracts used classes and diffs them against a list collected from existing templates, run right after the Tailwind build.
7What happens if Claude Code ignores getChildNames()?
Hardcoded child block calls defeat the Hyva block system and make later layout extensions through XML harder.
8What role does a CLAUDE.md file play in Hyva scaffolding?
It provides persistently available rules about block iteration, CSP registration, and mandatory escaping, which together with example files produce more consistent results.
9Does AI-assisted scaffolding replace the manual review?
No. Reading the diff in full, running the CSP audit, and checking Tailwind classes remain mandatory after every generation, regardless of context quality.
10How large should a single scaffolding task be?
A template together with its view model and layout fragment is a sensible unit. Bundling several unrelated templates makes review harder.