Developer-kit wordpress-sage-theme
Provides WordPress theme development patterns using Sage (roots/sage) framework. Use when creating, modifying, or debugging WordPress themes with Sage, including (1): creating new Sage themes from scratch, (2): setting up Blade templates and components, (3): configuring build tools (Vite, Bud), (4): working with WordPress theme templates and hierarchy, (5): implementing ACF fields integration, (6): theme customization and asset management.
git clone https://github.com/giuseppe-trisciuoglio/developer-kit
T=$(mktemp -d) && git clone --depth=1 https://github.com/giuseppe-trisciuoglio/developer-kit "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/developer-kit-php/skills/wordpress/wordpress-sage-theme" ~/.claude/skills/giuseppe-trisciuoglio-developer-kit-wordpress-sage-theme && rm -rf "$T"
plugins/developer-kit-php/skills/wordpress/wordpress-sage-theme/SKILL.mdWordPress Sage Theme Development
Overview
Sage is a WordPress theme framework by Roots that provides modern development practices including Blade templates, dependency management with Composer, and build tools with Vite/Bud.
When to Use
- Creating new Sage themes from scratch or from composer templates
- Setting up Blade templates, layouts, and reusable components
- Configuring build tools (Bud/Vite) for asset compilation
- Working with WordPress template hierarchy in Blade format
- Integrating Advanced Custom Fields (ACF) with Blade templates
- Debugging theme rendering, asset loading, or build issues
Instructions
- Set up the environment: Install PHP 8.0+, Node.js 18+, Composer, and create a new Sage theme with
composer create-project roots/sage - Configure build tools: Run
, then configurenpm install && composer install
for asset entries and Tailwindbud.config.js - Create Blade templates: Place templates in
, using layouts inresources/views/
, components inlayouts/components/ - Wire up WordPress templates: Map WordPress template hierarchy to Blade files (e.g.,
for page templates)page.blade.php - Integrate ACF fields: Use
for basic fields,get_field()
loops for repeaters and flexible contenthave_rows() - Build and verify: Run
, verifynpm run build
exists, check browser console for asset errorspublic/manifest.json - Deploy: Ensure the production build step (
) runs during deployment; raw source files cannot be served directlynpm run build
Examples
Create a new Sage theme:
composer create-project roots/sage my-theme cd my-theme npm install && composer install npm run dev
Blade page template:
@extends('layouts.app') @section('content') <main class="content"> <h1>{{ the_title() }}</h1> <div class="entry-content"> {{ the_content() }} </div> </main> @endsection
ACF flexible content in Blade:
@if (have_rows('flexible_content')) @while (have_rows('flexible_content')) @php the_row() @endphp @switch(get_row_layout()) @case('hero_section') @include('components.hero') @break @endswitch @endwhile @endif
Quick Start
Creating a New Sage Theme
Prerequisites: PHP 8.0+, Node.js 18+, Composer
# Create new Sage theme wp scaffold theme-theme my-theme --theme_name="My Theme" --author="Your Name" --activate # Or install Sage directly via Composer composer create-project roots/sage my-theme cd my-theme # Install dependencies npm install composer install # Build for development npm run dev # Build for production npm run build
Directory Structure
resources/ ├── views/ # Blade templates │ ├── layouts/ # Base layouts (app.blade.php) │ ├── components/ # Reusable components │ └── partials/ # Template partials ├── styles/ # CSS/SASS files │ └── main.scss # Main stylesheet └── scripts/ # JavaScript files └── main.js # Main JavaScript
Blade Templates
Layouts
Base Layout (
resources/views/layouts/app.blade.php):
<!DOCTYPE html> <html {{ site_html_language_attributes() }}> <head> {{ wp_head() }} </head> <body {{ body_class() }}> @yield('content') {{ wp_footer() }} </body> </html>
Template Hierarchy Mapping
| WordPress Template | Sage Blade File |
|---|---|
| front-page.php | |
| single.php | |
| page.php | |
| archive.php | |
| index.php | |
Example Page Template (
resources/views/page.blade.php):
@extends('layouts.app') @section('content') <main class="content"> <h1>{{ the_title() }}</h1> <div class="entry-content"> {{ the_content() }} </div> </main> @endsection
Components
Reusable Button Component (
resources/views/components/button.blade.php):
@props(['url' => '#', 'text' => 'Click', 'variant' => 'primary']) <a href="{{ $url }}" class="btn btn-{{ $variant }}"> {{ $text }} </a>
Usage:
<x-button url="/contact" text="Contact Us" variant="secondary" />
ACF Integration
Displaying ACF Fields
Basic Field:
@while(the_post()) <h1>{{ get_field('hero_title') ?? the_title() }}</h1> <p>{{ get_field('hero_description') }}</p> @endwhile
Flexible Content:
@if (have_rows('flexible_content')) @while (have_rows('flexible_content')) @php the_row() @endphp @switch(get_row_layout()) @case('hero_section') @include('components.hero') @break @case('features_grid') @include('components.features-grid') @break @endswitch @endwhile @endif
Repeater Field:
@if (have_rows('testimonials')) <div class="testimonials"> @while (have_rows('testimonials')) @php the_row() @endphp <blockquote> <p>{{ get_sub_field('testimonial_text') }}</p> <cite>{{ get_sub_field('author_name') }}</cite> </blockquote> @endwhile </div> @endif
Build Configuration (Bud)
Tailwind CSS Setup
Install Tailwind:
npm install -D tailwindcss npx tailwindcss init -p
Configure (
bud.config.js):
export default async (app) => { app .entry({ app: ['@/scripts/main.js', '@styles/main.css'], editor: ['@scripts/editor.js', '@styles/editor.css'], }) .assets('images', 'fonts') .tailwind() .runtime() };
Tailwind CSS (
resources/styles/main.css):
@tailwind base; @tailwind components; @tailwind utilities; @layer components { .btn { @apply px-4 py-2 rounded font-semibold transition; } .btn-primary { @apply bg-blue-600 text-white hover:bg-blue-700; } }
Advanced Patterns
Conditional Logic
@if (is_front_page()) @include('components.hero') @elseif (is_singular('post')) @include('components.post-meta') @endif
Custom Queries
@php $args = [ 'post_type' => 'service', 'posts_per_page' => 6, ]; $query = new WP_Query($args); @endphp @if ($query->have_posts()) @while ($query->have_posts()) @php $query->the_post() @endphp <article> <h2>{{ the_title() }}</h2> </article> @endwhile @php wp_reset_postdata() @endphp @endif
Theme Customization
functions.php additions:
// Custom image sizes add_image_size('hero-lg', 1920, 1080, true); // Custom post types add_action('init', function() { register_post_type('service', [ 'label' => 'Services', 'public' => true, 'has_archive' => true, 'supports' => ['title', 'editor', 'thumbnail'], ]); });
References
- Sage Documentation: See sage.md for complete framework reference
- Blade Templates: See blade.md for advanced Blade patterns
- Bud Configuration: See bud.md for build tool configuration
- ACF Integration: See acf.md for ACF field examples
Troubleshooting
Build workflow validation (always run in order):
completes without errorsnpm run build- Verify
exists and contains asset entriespublic/manifest.json - Test locally in browser before production push
- Check browser console for asset 404 errors
Build issues:
# Clear cache npm run clean # Rebuild rm -rf node_modules public npm install npm run build # Validate build output ls -la public/ cat public/manifest.json | head -20
Blade not compiling: Check
public/manifest.json exists after build
AC fields not showing: Verify field names match exactly (case-sensitive)
Best Practices
Blade Template Organization
- Use components for reusable UI elements: Create blade components in
for buttons, cards, and other repeated elementsresources/views/components/ - Keep layouts minimal: Base layouts should only contain structural HTML; delegate styling to components
- Leverage Blade directives: Use
,@include
, and@each
for better code organization@component - Avoid inline PHP: Use
blocks sparingly; move complex logic to Composers or service classes@php
ACF Field Management
- Group related fields: Use ACF field groups to organize fields logically by content type
- Use field keys for references: When referencing fields programmatically, use field keys (e.g.,
) for reliabilityfield_12345678 - Implement fallback values: Always provide default values for optional fields to prevent empty output
- Cache expensive queries: Use WordPress transients for complex ACF queries on high-traffic pages
Asset Management
- Use Bud for asset compilation: Let Bud handle versioning and optimization instead of manual asset management
- Minimize HTTP requests: Combine CSS/JS files where appropriate using Bud's entry points
- Optimize images: Use WordPress image sizes and lazy loading; consider WebP conversion
- Cache busting: Bud automatically handles cache busting via manifest.json
Code Organization
- Use Composers for data: Move data retrieval logic from templates to Sage Composers (
)app/View/Composers/ - Separate concerns: Keep business logic in service classes, presentation in Blade templates
- Follow WordPress coding standards: Maintain consistency with WordPress PHP coding standards
- Use type declarations: PHP 8.0+ allows typed properties and arguments for better code quality
Security Considerations
- Escape all output: Use
(Blade auto-escapes) or WordPress functions like{{ }}
,esc_html()esc_url() - Validate ACF input: Sanitize and validate custom field inputs on the backend
- Nonce verification: Always verify nonces for form submissions
- Capability checks: Use
before privileged operationscurrent_user_can()
Constraints and Warnings
Version Requirements
- PHP 8.0+ required: Sage 10 requires PHP 8.0 or higher; earlier versions are not supported
- Node.js 18+ required: Build tools require modern Node.js; older versions may cause compilation errors
- WordPress 6.0+ recommended: While Sage works with WordPress 5.x, version 6.0+ is recommended for full feature support
- Composer required: Dependency management requires Composer; manual installation is not supported
Build Tool Limitations
- Hot reload limitations: Bud's hot reload may not work correctly with some WordPress multisite configurations
- Production builds required for testing: Some features work differently in development vs production; always test with
before deploymentnpm run build - Manifest.json dependency: Theme relies on
; missing this file breaks asset loadingpublic/manifest.json
Common Pitfalls
- Template hierarchy confusion: Sage uses Blade files but follows WordPress template hierarchy; ensure file names match WordPress expectations
- Direct file access: Do not access Blade files directly via URL; they must be rendered through WordPress
- Plugin conflicts: Some caching and security plugins may interfere with Bud's hot reload or asset serving
- Theme updates: Updating Sage via Composer may overwrite customizations; use child themes for extensive modifications
Performance Considerations
- ACF Repeater performance: Large repeater fields can impact page load; consider pagination or alternative storage for large datasets
- Blade compilation overhead: First load after cache clear compiles all templates; use OPcache in production
- Asset bundle size: Monitor compiled asset sizes; large bundles affect page load performance
- Database queries: Use Query Monitor plugin to identify and optimize expensive queries in theme templates
Deployment Constraints
- Build step required: Production deployments must run
; raw source files cannot be servednpm run build - Environment-specific configuration: Bud configuration may need adjustment for different deployment environments
- File permissions: Ensure
directory is writable during builds; incorrect permissions cause build failurespublic/