Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
.claude/skills/giuseppe-trisciuoglio-wordpress-sage-theme/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-14 | ✗→✓ | ▲ Improved | 146% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 144% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 307% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 135% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 136% | 0% |
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.
composer create-project roots/sagenpm install && composer install, then configure bud.config.js for asset entries and Tailwindresources/views/, using layouts in layouts/, components in components/page.blade.php for page templates)get_field() for basic fields, have_rows() loops for repeaters and flexible contentnpm run build, verify public/manifest.json exists, check browser console for asset errorsnpm run build) runs during deployment; raw source files cannot be served directlyCreate a new Sage theme:
bashcomposer create-project roots/sage my-theme cd my-theme npm install && composer install npm run dev
Blade page template:
blade@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:
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
Prerequisites: PHP 8.0+, Node.js 18+, Composer
bash# 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
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 JavaScriptBase Layout (resources/views/layouts/app.blade.php):
blade<!DOCTYPE html> <html {{ site_html_language_attributes() }}> <head> {{ wp_head() }} </head> <body {{ body_class() }}> @yield('content') {{ wp_footer() }} </body> </html>
| WordPress Template | Sage Blade File | |-------------------|-----------------| | front-page.php | views/front-page.blade.php | | single.php | views/single.blade.php | | page.php | views/page.blade.php | | archive.php | views/archive.blade.php | | index.php | views/index.blade.php |
Example Page Template (resources/views/page.blade.php):
blade@extends('layouts.app') @section('content') <main class="content"> <h1>{{ the_title() }}</h1> <div class="entry-content"> {{ the_content() }} </div> </main> @endsection
Reusable Button Component (resources/views/components/button.blade.php):
blade@props(['url' => '#', 'text' => 'Click', 'variant' => 'primary']) <a href="{{ $url }}" class="btn btn-{{ $variant }}"> {{ $text }} </a>
Usage:
blade<x-button url="/contact" text="Contact Us" variant="secondary" />
Basic Field:
blade@while(the_post()) <h1>{{ get_field('hero_title') ?? the_title() }}</h1> <p>{{ get_field('hero_description') }}</p> @endwhile
Flexible Content:
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 @case('features_grid') @include('components.features-grid') @break @endswitch @endwhile @endif
Repeater Field:
blade@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
Install Tailwind:
bashnpm install -D tailwindcss npx tailwindcss init -p
Configure (bud.config.js):
jsexport 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):
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; } }
blade@if (is_front_page()) @include('components.hero') @elseif (is_singular('post')) @include('components.post-meta') @endif
blade@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
functions.php additions:
php// 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'], ]); });
Build workflow validation (always run in order):
npm run build completes without errorspublic/manifest.json exists and contains asset entriesBuild issues:
bash# 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)
resources/views/components/ for buttons, cards, and other repeated elements@include, @each, and @component for better code organization@php blocks sparingly; move complex logic to Composers or service classesfield_12345678) for reliabilityapp/View/Composers/){{ }} (Blade auto-escapes) or WordPress functions like esc_html(), esc_url()current_user_can() before privileged operationsnpm run build before deploymentpublic/manifest.json; missing this file breaks asset loadingnpm run build; raw source files cannot be servedpublic/ directory is writable during builds; incorrect permissions cause build failures| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 8,629 | 7,475 | -13% | 1 | 1 | 0% | 1,645 | 4,605 | +180% | 0 | 0 | — |
case-02 | pass→pass | 10,489 | 7,504 | -28% | 1 | 1 | 0% | 1,869 | 4,569 | +144% | 0 | 0 | — |
case-03 | pass→pass | 5,255 | 4,004 | -24% | 1 | 1 | 0% | 937 | 3,813 | +307% | 0 | 0 | — |
case-04 | pass→pass | 11,202 | 7,975 | -29% | 1 | 1 | 0% | 1,907 | 4,490 | +135% | 0 | 0 | — |
case-05 | pass→pass | 14,585 | 14,189 | -3% | 1 | 1 | 0% | 2,451 | 5,785 | +136% | 0 | 0 | — |
case-06 | pass→pass | 7,807 | 3,495 | -55% | 1 | 1 | 0% | 1,460 | 3,738 | +156% | 0 | 0 | — |
case-07 | pass→pass | 2,894 | 2,534 | -12% | 1 | 1 | 0% | 421 | 3,571 | +748% | 0 | 0 | — |
case-08 | pass→pass | 3,492 | 2,908 | -17% | 1 | 1 | 0% | 577 | 3,541 | +514% | 0 | 0 | — |
case-09 | pass→pass | 14,391 | 6,669 | -54% | 1 | 1 | 0% | 2,341 | 4,124 | +76% | 0 | 0 | — |
case-10 | pass→pass | 10,765 | 7,037 | -35% | 1 | 1 | 0% | 1,823 | 4,294 | +136% | 0 | 0 | — |
case-11 | pass→pass | 14,402 | 10,400 | -28% | 1 | 1 | 0% | 2,277 | 5,112 | +125% | 0 | 0 | — |
case-12 | pass→pass | 10,810 | 10,308 | -5% | 1 | 1 | 0% | 1,827 | 5,027 | +175% | 0 | 0 | — |
case-13 | pass→pass | 8,779 | 3,149 | -64% | 1 | 1 | 0% | 1,340 | 3,585 | +168% | 0 | 0 | — |
case-14 | fail→pass | 9,612 | 3,970 | -59% | 1 | 1 | 0% | 1,583 | 3,892 | +146% | 0 | 0 | — |
case-15 | pass→pass | 3,840 | 3,222 | -16% | 1 | 1 | 0% | 632 | 3,653 | +478% | 0 | 0 | — |
case-16 | pass→pass | 5,785 | 3,724 | -36% | 1 | 1 | 0% | 1,086 | 3,851 | +255% | 0 | 0 | — |
case-17 | pass→pass | 4,135 | 5,968 | +44% | 1 | 1 | 0% | 628 | 4,197 | +568% | 0 | 0 | — |
case-18 | pass→pass | 15,907 | 9,017 | -43% | 1 | 1 | 0% | 1,996 | 4,550 | +128% | 0 | 0 | — |
case-19 | pass→pass | 7,352 | 6,495 | -12% | 1 | 1 | 0% | 1,270 | 4,301 | +239% | 0 | 0 | — |
case-20 | pass→pass | 11,895 | 9,350 | -21% | 1 | 1 | 0% | 2,325 | 5,015 | +116% | 0 | 0 | — |
case-21 | pass→pass | 4,273 | 5,151 | +21% | 1 | 1 | 0% | 773 | 4,051 | +424% | 0 | 0 | — |
case-22 | pass→pass | 4,956 | 3,086 | -38% | 1 | 1 | 0% | 806 | 3,651 | +353% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of 0 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.