- Root is a WordPress block theme; primary code lives alongside
style.cssandtheme.jsonfor metadata and global styles. - Block patterns live in
patterns/(PHP files registering block markup); keep slugs/descriptions aligned to filenames. - Template parts sit in
parts/(header/footer) and full templates intemplates/andindex.php. - Shared assets live under
assets/(fonts/,styles/for pattern-specific CSS) and translations inlanguages/. - Reusable PHP helpers reside in
functions.phpandinc/; avoid putting logic in template files. .distignorefile: Present in theme directory but ignored by git (seedemo/.gitignore). Required for WordPress.org distribution to exclude dev files from ZIP package. Synced from standalone Elayne repo (~/code/elayne/.distignore).
- No JS build pipeline is required; theme assets are committed. Activate by placing the folder in
wp-content/themes/elayne/and enabling it in WP Admin. - IMPORTANT: All WP-CLI commands must be run from Trellis VM, NOT from local machine
- For CLI activation inside a Bedrock/Trellis install:
wp theme activate elayne --path=web/wp. - Regenerate translations when strings change:
wp i18n make-pot . languages/elayne.pot. - Flush caches after template or pattern edits if using object caching:
wp cache flush. - Commit hygiene: Demo theme commits must be attribution-free (no Claude/AI footers).
- Uses Trellis VM (Lima-based, NOT Vagrant)
- Protocol: Uses HTTP (not HTTPS) - Access via
http://demo.imagewize.test/(NOThttps://) - Access VM:
cd ~/code/imagewize.com/trellis && trellis vm shell - File Sync: Automatic, real-time sync between host (
~/code/imagewize.com/demo/) and VM (/srv/www/demo.imagewize.com/) - No manual file copying needed - edits on host immediately available in VM
- Common issue: Changes not appearing = WordPress cache, NOT file sync. Solution:
wp cache flush --path=web/wp
# Interactive VM shell
cd ~/code/imagewize.com/trellis
trellis vm shell
cd /srv/www/demo.imagewize.com/current
wp cache flush --path=web/wp
# Single command from host
trellis vm shell --workdir /srv/www/demo.imagewize.com/current -- wp cache flush --path=web/wp
# Multisite commands (run for all sites)
wp site list --field=url --path=web/wp | xargs -n1 -I % wp --url=% cache flush --path=web/wpEnable in config/environments/development.php:
Config::define('WP_DEVELOPMENT_MODE', 'theme');Benefits: Bypasses theme.json/pattern caching for immediate changes
- PHP: follow WordPress coding standards (4-space indent, snake_case functions); keep logic minimal and filter-based. Use text domain
elaynein translatable strings. - Patterns: name files with kebab-case slugs (e.g.,
hero-two-tone.php) and match thetitle/categoriesto existing taxonomy. - CSS: favor block-level styles under
assets/styles/; keep selectors scoped to block classes to avoid global leakage. - Templates/parts: prefer semantic HTML and block markup; avoid inline styles when theme.json can express the setting.
- NEVER use hardcoded media IDs in
wp:imageblocks (e.g.,"id":59) - NEVER use external URLs (Unsplash, CDNs, etc.) - all images must be local files
- Always use direct file paths:
<?php echo esc_url( get_template_directory_uri() ); ?>/patterns/images/filename.webp - GPL compatibility: All pattern images must be GPL-compatible or public domain (CC0, Pexels License, etc.)
- Document sources in
readme.txt(Copyright section) - WordPress.org requirement - Follow attribution format from existing images (lines 269-349 in readme.txt)
- Preferred sources: WordPress Openverse (openverse.org - filter by "Use commercially" + "Modify or adapt"), Pexels (GPL-compatible license), or custom photography
- NEVER use: not GPL-compatible images
- Document sources in
- Image optimization: Use WebP format, optimize file sizes (<200KB), appropriate dimensions
- Hardcoded IDs cause performance issues: database queries for non-existent media, blinking/flashing effects, console errors
- All pattern images stored in
patterns/images/directory - Use semantic color/spacing variables:
var:preset|color|primary - Follow format:
elayne/pattern-namefor slugs
- NEVER use
wp:htmlblocks for content that can be created with native WordPress blocks - ALWAYS prefer native blocks: Use
wp:list,wp:separator,wp:group,wp:columns, etc. - Custom styling: Add CSS classes and styles to
style.cssrather than inline HTML - Why? Native blocks are editable in the block editor, support theme.json styling, and work with block patterns
- Example - WRONG:
<!-- wp:html --><div class="custom-list">...</div><!-- /wp:html --> - Example - CORRECT:
<!-- wp:list {"className":"is-style-checkmark-list"} -->with CSS instyle.css - Separator blocks: Use
wp:separatorwithis-style-dotsclass for dotted lines - Custom list styles: Register CSS class like
is-style-checkmark-listand apply towp:listblocks - Benefits: Editor compatibility, theme.json integration, pattern reusability, accessibility
- Block attributes must be correctly nested in block comment JSON to avoid validation errors
- Common mistake: Nesting root-level attributes inside
styleobject causes "Block validation failed" errors - Correct structure:
backgroundColor,layout,alignare root-level attributes, NOT nested instyle - Wrong:
"style":{...,"backgroundColor":"base","layout":{...}} - Correct:
"style":{...},"backgroundColor":"base","layout":{...} - WordPress expects specific attribute placement based on block schema
- Validation errors appear in browser console: "Content generated by
savefunction" vs "Content retrieved from post body" - Always verify block comment JSON structure matches WordPress core block attribute schema
- Keep the block comment and rendered wrapper in sync: include
metadata(categories/patternName/name) when present, and mirror padding/margin values (including left/right) in the outerdivinline styles so Gutenberg recovery does not rewrite the pattern. - Block comment balance: Every
<!-- wp:group -->must have a matching<!-- /wp:group -->. Extra closing comments or missing wrappers can force Gutenberg to wrap the tail in a Classic block.
- Use grid layout with
minimumColumnWidthfor responsive multi-column patterns (3→2→1 columns) - NEVER use
wp:columnsfor pricing/feature grids - goes 3→3(cramped)→1, bad tablet experience - NEVER use fixed
columnCount- forces exact column count at all screen sizes - Correct:
{"layout":{"type":"grid","minimumColumnWidth":"20rem"}} - Behavior: Desktop (3 cols) → Tablet (2 cols) → Mobile (1 col) based on available space
- Use for: Pricing tables, feature grids, team grids, card layouts
- Reference: See
pricing-comparison.php - Grid standards: Use category tags plus consistent widths:
elayne/card-simple=18rem,elayne/card-extended=19rem,elayne/card-profiles=20rem, text-heavy grids =28-29rem. - Centering rule: Keep the outer full-width group as
layout: default, then wrap the grid in an inneralignwidegroup so it centers inside the constrained container.
- Always add margin reset to patterns with background colors:
"margin":{"top":"0","bottom":"0"} - WordPress core adds automatic
margin-block-startbetween blocks in constrained layouts - Without margin reset, unwanted gaps appear between adjacent patterns with different backgrounds
- Use inline styles (Ollie approach) instead of global CSS overrides
- Example:
style="margin-top:0;margin-bottom:0;padding-top:var(--wp--preset--spacing--xxx-large);..." - Required for: Full-width sections, hero sections, CTAs, testimonials, feature grids with backgrounds
- NEVER use constrained layout on outer
alignfullgroup - causes horizontal gaps/overflow - Root cause:
"layout":{"type":"constrained","contentSize":"1200px"}onalignfullgroup creates max-width that conflicts with full-width alignment - Correct approach:
- Outer
alignfullgroup: ALWAYS use"layout":{"type":"default"} - Inner content groups: Use
"layout":{"type":"constrained","contentSize":"XXXpx"}to center and limit width
- Outer
- Reference: See
hero-two-tone.phpfor working example - Wrong:
<!-- wp:group {"align":"full","layout":{"type":"constrained","contentSize":"1200px"}} --> - Correct:
<!-- wp:group {"align":"full","layout":{"type":"default"}} -->with nested constrained groups inside
get_template_directory_uri()is evaluated when a pattern is inserted; the resulting URL is hardcoded intopost_content.- After moving content between environments, run
wp search-replaceto swap.testURLs to production (and vice versa) before going live.
- Problem: Page templates using
"layout":{"type":"default"}onpost-contentprevent full-width patterns from breaking out - Root cause:
defaultlayout constrains all children, whileconstrainedlayout allowsalignfullto break out - Solution: Page templates must use
{"layout":{"type":"constrained"}}onpost-contentblock - Fixed templates:
template-page-wide.php,template-page-wide-no-title.phpupdated to use constrained layout - Correct template:
template-page-full.phpalready correct (used bypage-no-title.html) - Reference: Based on Ollie theme's approach
- Manual verification is primary: activate the theme, add each pattern to a page, and confirm layout/spacing matches design.
- Validate block templates in the editor to ensure no block validation errors appear.
- Run
wp i18n make-potif translations are touched to confirm the POT file updates cleanly.
- When bumping versions (e.g., to
1.0.0-beta.2), updatestyle.cssheader,readme.txtStable tag and changelog section, andCHANGELOG.mdrelease entry together. - Match
readme.txtchangelog formatting to Moiraine:== Changelog ==header, entries like= 1.0.0-beta.3 - 12/10/25 =with bullet prefixesNEW/ADDED/CHANGED/TECHNICALand concise sentences ending in periods. - Keep
CHANGELOG.mdaligned to Keep a Changelog; include descriptive subsection titles (e.g.,### Added - ...,### Changed - ...) similar to Moiraine’s style. - Use
git status/git diffto review current changes; compare againstmainwhen needed (git diff main...HEAD) before updating release notes.
- Use short, Title-Case commit messages focused on a single change (e.g.,
Hero Pattern Update). - PRs should state purpose, affected paths (patterns, templates, assets), and manual test notes (browser/viewport and steps).
- Include screenshots or screen recordings for visual changes to patterns, templates, or global styles.
- Avoid committing secrets or environment config; only theme assets/code belong here.
- SSH as web user (recommended for WP-CLI):
ssh [email protected] - SSH as root (server management):
ssh [email protected] - Demo site paths on server (Bedrock structure):
- Current release:
/srv/www/demo.imagewize.com/current/ - Theme:
/srv/www/demo.imagewize.com/current/web/app/themes/elayne/ - Uploads:
/srv/www/demo.imagewize.com/shared/uploads/ - Logs:
/srv/www/demo.imagewize.com/logs/
- Current release:
# SSH to demo server
ssh [email protected]
cd /srv/www/demo.imagewize.com/current
# Check WordPress status (multisite)
wp site list --path=web/wp
# Flush cache (multisite - all sites)
wp site list --field=url --path=web/wp | xargs -n1 -I % wp --url=% cache flush --path=web/wp
# View error logs
tail -f /srv/www/demo.imagewize.com/logs/error.log- Managed via Trellis from repository root
- See main
CLAUDE.mdin repository root for deployment commands - Demo site uses multisite configuration
- Do not commit
.env, uploads, or build artifacts; only theme code and assets should live in the repo. - When working in Trellis/Bedrock, always pass
--path=web/wpto WP-CLI to target the correct install. - Keep licensed assets (fonts/images) documented in
assets/; ensure redistribution rights before adding new media.