Wagtailify Skill
Convert annotated HTML wireframes into functional Wagtail themes for SUM Platform.
Prerequisites
Before invoking this skill, ensure:
- •Wireframe exists at
docs/dev/design/wireframes/{theme}/compiled/ - •Wireframe is annotated with
data-section-id,data-block-type, anddata-ownerattributes (see Phase 0 in THEME-GUIDE.md) - •NEW block schemas documented for any
NEW:*block types
Invocation
/wagtailify <theme-name>
Example: /wagtailify theme_b
Phases Overview
| Phase | Name | Human Checkpoint | Output |
|---|---|---|---|
| 1 | Audit | No | JSON inventory |
| 2 | Plan | Yes | Confirmed work plan |
| 3 | Scaffold | No | Theme directory |
| 4a | Analyze NEW Blocks | No | Schema proposals |
| 4b | Review Schemas | Yes | Approved schemas |
| 4c | Generate Blocks | No | Block classes |
| 4d | Validate & Migrate | No | Clean lint, migrations |
| 5 | Extract Tokens | No | tailwind.config.js |
| 6 | Create CSS | No | input.css |
| 7 | Chrome Templates | No | base.html, header, footer |
| 8 | Block Templates | No | Block HTML overrides |
| 9 | Page Templates | No | Page type templates |
| 10 | JavaScript | No | main.js |
| 11 | Build & Test | Yes | Working theme |
Phase 1: Audit
Goal: Extract and validate wireframe structure.
Process
- •
Run the audit script:
bashpython .claude/skills/wagtailify/scripts/audit_wireframe.py {theme} - •
The script will:
- •Locate wireframe at
docs/dev/design/wireframes/{theme}/compiled/ - •Parse all HTML files
- •Extract every element with
data-section-id - •Validate each has
data-block-typeanddata-owner - •Generate structured JSON report
- •Locate wireframe at
- •
Review the output for:
- •Validation errors (missing attributes)
- •Unknown block types
- •NEW blocks that need creation
Exit Criteria
- •All sections have required attributes
- •Section IDs are unique
- •Block types are valid (existing key, NEW:*, or template)
If validation fails, stop and ask the human to fix the wireframe.
Phase 2: Plan
Goal: Confirm block mapping and work items with human.
Process
Present the audit results in this format:
EXISTING BLOCKS (need theme template override): - hero_image (6 sections) - stats (2 sections) - manifesto (1 section) ... NEW BLOCKS (need class + template): - ValuePropositionBlock - Owner: ServiceIndexPage.intro - Source: services-04 - Fields to extract: heading, body, stats[], ctas[], image TEMPLATE-OWNED SECTIONS: - global-banner → base.html (AlertBanner snippet) - global-header → header.html - global-footer → footer.html ... Proceed? [Y/n]
Exit Criteria
Human confirms the plan before proceeding.
Phase 3: Scaffold
Goal: Create theme directory structure.
Process
- •
Run the scaffold script:
bashbash .claude/skills/wagtailify/scripts/scaffold_theme.sh {theme} - •
The script creates:
codethemes/{theme}/ ├── VERSION (set to 0.0.0) ├── theme.json ├── tailwind/ │ ├── package.json │ ├── npm-shrinkwrap.json │ ├── postcss.config.js │ └── tailwind.config.js (placeholder) ├── static/{theme}/ │ ├── css/ │ └── js/ └── templates/ ├── theme/ │ └── includes/ └── sum_core/ └── blocks/ - •
Run
npm ciin tailwind directory
Exit Criteria
- •Directory structure exists
- •npm dependencies installed
Phase 4a: Analyze NEW Block Structure
Goal: Propose field schemas for NEW blocks.
Process
For each NEW:* block:
- •
Locate the section in wireframe HTML by
data-section-id - •
Analyze HTML structure:
- •Text content →
CharBlockorTextBlock - •Rich text with formatting →
RichTextBlock - •Images →
ImageChooserBlock - •Links →
UniversalLinkBlock - •Repeated items →
ListBlock - •Nested structures → child
StructBlock
- •Text content →
- •
Generate schema proposal:
json{ "block_name": "ValuePropositionBlock", "target_file": "services.py", "group": "Sections", "fields": [ {"name": "heading", "type": "RichTextBlock", "features": ["italic", "bold"], "required": true}, {"name": "body", "type": "RichTextBlock", "features": ["bold", "italic", "link", "ul", "ol"], "required": true}, {"name": "stats", "type": "ListBlock", "child": "ValuePropositionStatBlock", "min_num": 2, "max_num": 2} ], "child_blocks": [ {"name": "ValuePropositionStatBlock", "fields": [...]} ] } - •
Determine target file based on block group:
- •Hero blocks →
hero.py - •Service blocks →
services.py - •Content blocks →
content.py - •Form blocks →
forms.py
- •Hero blocks →
Exit Criteria
Schema proposal generated for all NEW blocks.
Phase 4b: Human Review (CHECKPOINT)
Goal: Get human approval on field schemas.
Process
Present each NEW block schema:
NEW BLOCK: ValuePropositionBlock Target file: core/sum_core/blocks/services.py Group: Sections PROPOSED FIELDS: | Field | Type | Required | Notes | |-------|------|----------|-------| | heading | RichTextBlock(bold, italic) | Yes | | | body | RichTextBlock(full) | Yes | | | primary_cta | CtaLinkBlock | No | | | secondary_cta | CtaLinkBlock | No | | | stats | ListBlock(StatBlock) | Yes | min=2, max=4 | | image | ImageChooserBlock | No | | | image_quote | CharBlock(200) | No | | CHILD BLOCK: StatBlock | Field | Type | Required | |-------|------|----------| | value | CharBlock(20) | Yes | | label | CharBlock(80) | Yes | Approve schema? [Y/modify/skip]
Exit Criteria
Human approves or modifies all schemas.
Phase 4c: Generate Block Classes
Goal: Create block classes using approved schemas.
Process
For each approved schema:
- •
Generate block class using the Jinja template at
templates/block_class.py.jinja- •Render the template with the approved schema
- •The template generates imports and properly formatted block classes
- •
Add generated code to target file in
core/sum_core/blocks/ - •
Register in
PageStreamBlockincore/sum_core/blocks/base.py - •
Create minimal core template in
core/sum_core/templates/sum_core/blocks/
Exit Criteria
All NEW blocks created and registered.
Phase 4d: Validate and Migrate
Goal: Ensure code is valid and migrations are generated.
Process
- •
Run linter:
bashmake lint
- •
Check for needed migrations:
bashpython core/sum_core/test_project/manage.py makemigrations sum_core --dry-run
- •
If migrations needed, generate them:
bashpython core/sum_core/test_project/manage.py makemigrations sum_core
- •
Run tests:
bashmake test
Exit Criteria
- •
make lintpasses - •Migrations generated (if needed)
- •
make testpasses
Phase 5: Extract Tokens
Goal: Create tailwind.config.js with CSS variable bridging.
Process
- •
Read wireframe's inline Tailwind config (in
<script>tailwind.config = {...}</script>) - •
Extract:
- •Color palette
- •Font families
- •Custom breakpoints
- •Animations
- •
Convert to CSS variable pattern:
js// Wireframe: sage: { terra: '#A0563B' } // Becomes: 'sage-terra': 'hsl(var(--brand-h, 16) var(--brand-s, 46%) var(--brand-l, 43%) / <alpha-value>)' - •
Add content paths for template scanning
- •
Write to
themes/{theme}/tailwind/tailwind.config.js
Exit Criteria
Config file exists and is valid JS.
Phase 6: Create CSS
Goal: Create input.css with variable defaults and component classes.
Process
- •
Extract CSS variable defaults from wireframe colors (HSL conversion)
- •
Extract component classes from wireframe's
static/style.css:- •Button styles
- •Section layouts
- •Card styles
- •Navigation styles
- •Reveal animations
- •
Write to
themes/{theme}/static/{theme}/css/input.css
Exit Criteria
npm run build succeeds in tailwind directory.
Phase 7: Chrome Templates
Goal: Implement base template and global chrome.
Templates to Create
- •
templates/theme/base.html— Master layout - •
templates/theme/includes/header.html— Navigation - •
templates/theme/includes/footer.html— Footer - •
templates/theme/includes/sticky_cta.html— Mobile sticky CTA - •
templates/sum_core/includes/cookie_banner.html— Cookie banner override
Process
For each template:
- •Copy HTML structure from wireframe (locate by
data-section-id) - •Replace hardcoded content with template tags:
- •
{% branding_css %},{% branding_fonts %} - •
{% header_nav as nav %},{% footer_nav as footer %} - •
{% get_site_settings as site_settings %}
- •
- •Add conditional rendering (
{% if %}) - •Add accessibility attributes
Exit Criteria
Base template loads without errors.
Phase 8: Block Templates
Goal: Create theme override for every mapped block.
Process
For each block in the audit:
- •Locate section in wireframe HTML by
data-section-id - •Copy HTML structure
- •Replace hardcoded content with block field references:
- •
{{ self.heading|richtext }} - •
{{ self.image }} - •
{% for item in self.items %}
- •
- •Apply theme component classes
- •Add reveal animations
- •Save to
templates/sum_core/blocks/{block_key}.html
Exit Criteria
All blocks have theme templates.
Phase 9: Page Templates
Goal: Create templates for template-owned sections.
Templates (varies by theme)
- •
templates/theme/home_page.html - •
templates/theme/standard_page.html - •
templates/theme/service_index_page.html - •
templates/theme/blog_index_page.html - •
templates/theme/blog_post_page.html - •etc.
Process
- •Identify template-owned sections from audit
- •Create page template extending
base.html - •Implement template-owned sections directly
- •Loop through StreamField for block-owned content
Exit Criteria
All page types have templates.
Phase 10: JavaScript
Goal: Extract and adapt JS interactions.
Process
- •
Read wireframe's
static/script.js - •
Identify interactions:
- •Header scroll effect
- •Mobile menu
- •Mega menu
- •Accordion
- •Reveal animations
- •Modal/lightbox
- •
Adapt to theme structure
- •
Add error boundaries (try/catch)
- •
Add reduced motion support
- •
Save to
themes/{theme}/static/{theme}/js/main.js
Exit Criteria
No console errors, interactions work.
Phase 11: Build & Test (CHECKPOINT)
Goal: Verify theme works end-to-end.
Process
- •
Build CSS:
bashcd themes/{theme}/tailwind && npm run build - •
Update fingerprint:
bashpython themes/{theme}/build_fingerprint.py - •
Run linter:
bashmake lint
- •
Run contract tests:
bashACTIVE_THEME_SLUG={theme} pytest tests/themes/contracts/ -v - •
Start dev server:
bashmake dev-reset THEME={theme} PROFILE=sage-stone make run THEME={theme} - •
Visual inspection with human
Exit Criteria
- •CSS builds without errors
- •Lint passes
- •Contract tests pass
- •Site renders correctly
- •Human confirms visual fidelity
State Management
The skill maintains state in .wagtailify-state.json:
{
"theme": "theme_b",
"current_phase": 4,
"phases_completed": [1, 2, 3],
"audit_output": "...",
"new_blocks": [...],
"errors": []
}
Use --resume to continue from last checkpoint:
/wagtailify theme_b --resume
Use --rollback to undo to a specific phase:
/wagtailify theme_b --rollback 3
Reference Files
- •
scripts/audit_wireframe.py— Extracts and validates wireframe attributes - •
scripts/scaffold_theme.sh— Creates theme directory structure - •
templates/block_class.py.jinja— Block class template - •
reference/block-quick-ref.md— Block key → class → required fields - •
reference/wireframe-attributes.md— Data attribute specification
Troubleshooting
"Wireframe not found"
Ensure wireframe is at docs/dev/design/wireframes/{theme}/compiled/
"Missing data attributes"
Add required data-section-id, data-block-type, data-owner to wireframe sections. See Phase 0 in THEME-GUIDE.md.
"Unknown block type"
Check docs/dev/blocks-reference.md for valid block keys. Use NEW:ClassName for new blocks.
"Lint failures after block creation"
Review generated code for import errors. Ensure all referenced blocks exist.
"Contract tests fail"
Check theme template paths match expected structure. Ensure all required templates exist.