Sage 11 Deep Dive: Blade, Vite, Tailwind CSS 4, and Acorn Integration
Sage is the most popular starter theme in the Roots.io ecosystem — over 13,000 GitHub stars — and Sage 11 is the biggest rewrite since Sage moved from Grunt/Gulp to Webpack in 2017. The current stable version as of April 2026 is Sage 11.2.0, built on Blade for templating, Vite for asset bundling, Tailwind CSS v4 for styling, and Acorn as the Laravel bridge that makes most of it possible.
This post is a deep dive into what Sage 11 actually is — not a marketing overview, but a technical tour of the internals. We’ll walk through the directory structure, the Blade rendering pipeline, how Vite integrates with the WordPress block editor, how Tailwind-generated theme.json keeps frontend and editor styles in sync, and the Acorn service container that powers it all. By the end, you’ll understand Sage 11 well enough to evaluate it against alternatives, start a new project, and debug the parts that feel magical until they don’t.
If you haven’t worked with modern PHP before, you’ll want to skim our Roots stack overview first. This post assumes you’re comfortable with Composer, npm/pnpm, and at least casually familiar with Laravel’s Blade syntax.
What Sage 11 Is (and Isn’t)
Sage is a starter theme. You install it as a blank slate and build your design from there. It’s not a drag-and-drop theme, not a page builder, and not a child theme of some parent. Sage gives you:
- A Blade templating engine for writing HTML-like templates with real logic, layouts, includes, components, and slots.
- A Vite-powered asset pipeline with hot module replacement for CSS and JavaScript, including inside the block editor.
- Tailwind CSS v4 as the default utility framework, with a CSS-first configuration that replaces
tailwind.config.js. - An automatically-generated
theme.jsonbuilt from your Tailwind config, keeping the block editor’s design tokens in sync with your frontend. - First-class block editor support — separate
editor.cssandeditor.jsentry points for the iframed block editor. - Service providers and dependency injection via Acorn, for people who want to write real classes instead of global functions.
What Sage is not:
- Not an FSE (full-site-editing) block theme by default. It supports FSE partially, but its home turf is custom PHP templates.
- Not a place for drag-and-drop page building. Sage assumes a developer is building the theme.
- Not a minimalist theme — you’re opting into a full Node.js + Composer + build-step workflow.
Sage 11 Directory Structure
A fresh Sage 11 theme looks like this (inside wp-content/themes/your-theme/ or Bedrock’s web/app/themes/your-theme/):
app/ # PHP classes, Acorn app code├── Providers/ # Acorn service providers├── View/│ └── Composers/ # Blade view composers├── filters.php # theme filters└── setup.php # after_setup_theme actionsconfig/ # Sage config (theme.json generation, etc.)public/ # compiled assets output hereresources/├── views/ # Blade templates (.blade.php)│ ├── layouts/ # page layouts│ ├── partials/ # header, footer, etc.│ └── sections/ # template parts├── css/│ ├── app.css # frontend CSS entry│ └── editor.css # block editor CSS entry└── js/ ├── app.js # frontend JS entry └── editor.js # block editor JS entrycomposer.json # PHP dependencies (Acorn etc.)package.json # JS dependencies (Vite, Tailwind)vite.config.js # Vite configurationtailwind.config.js # optional with Tailwind v4 CSS-first configstyle.css # WordPress theme header
Three things to notice:
resources/views/is where Blade templates live — not at the theme root.public/is where compiled assets end up — committed or generated at build time depending on your workflow.app/is a real PHP namespace with Acorn service providers, view composers, and other classes. You can write OOP code instead of piling functions intofunctions.php.
Blade Templating in WordPress
Blade is Laravel’s templating engine, and Sage makes it the default for WordPress templates. Instead of header.php with inline <?php echo ... ?>, you write resources/views/partials/header.blade.php:
<header class="site-header"> <a href="{{ home_url('/') }}"> {{ get_bloginfo('name') }} </a> @include('partials.main-menu')</header>
The Blade advantages over raw PHP templates:
- Escaping by default.
{{ $var }}escapes output; raw PHP templates require rememberingesc_html()everywhere. - Template inheritance. Extend a layout with
@extends, define sections with@section, yield them with@yield. - Components and slots. Reusable markup — buttons, cards, navigation items — as first-class components with typed props.
- Directives.
@if,@foreach,@include,@php, plus WordPress-specific Blade directives (@wp,@hasaction) shipped by Acorn. - View composers. Register a callback that runs whenever a specific view is rendered, so you can attach data without stuffing logic into the template.
- Cached compilation. Blade templates compile once to plain PHP and cache the result. Production-fast.
Components Example
Create resources/views/components/button.blade.php:
@props(['href' => null, 'variant' => 'primary'])<a href="{{ $href }}" class="btn btn-{{ $variant }}"> {{ $slot }}</a>
Use it anywhere:
<x-button href="/contact" variant="primary">Get in touch</x-button>
Components compose. Design systems that would be painful to maintain in raw PHP templates become natural in Blade.
Vite: The Asset Pipeline
Sage 10 used Bud.js (a Roots-maintained wrapper around Webpack); Sage 11 moved to Vite, the Evan You–led asset bundler that powers most of the modern JavaScript ecosystem. The benefits for WordPress development:
- Fast dev server.
pnpm run devstarts in ~1 second and serves assets with aggressive caching. - Hot module replacement (HMR). Edit a CSS file, save, and the browser reflects the change without a full reload. For JavaScript, HMR preserves component state where possible.
- HMR inside the block editor. This is the big one. Sage’s editor assets hot-reload inside the iframed block editor, so you can iterate on block styles and editor UI without constantly refreshing.
- ESM-first. Vite serves ES modules natively in development, only bundling for production builds. This makes the dev server dramatically faster than Webpack’s constant rebundling.
- Smaller dependency surface. Vite’s plugin ecosystem is smaller and better-behaved than Webpack’s.
The Sage Vite Configuration
Sage ships with @roots/vite-plugin, which wires Vite into WordPress specifically. It handles:
- WordPress’s
wp_enqueue_styleandwp_enqueue_script— the plugin generates a manifest thatAcornreads on the PHP side to enqueue compiled assets correctly. - Block editor asset registration — separate entries for
editor.css/editor.jsregister with the block editor’s iframe. - HMR inside the iframed block editor — proxies the Vite HMR WebSocket through the editor frame.
- Production builds —
pnpm run buildproduces hashed, tree-shaken, minified assets inpublic/build/.
The plugin hit v2.1.0 in April 2026 with support for theme.json partials — block-specific styles can now live in co-located .theme.js files.
Tailwind CSS v4 and Generated theme.json
Sage 11 ships with Tailwind v4 as the default utility framework. Tailwind v4 introduces a CSS-first configuration: instead of a JavaScript config file, theme tokens live in @theme blocks inside your CSS.
A minimal app.css:
@import "tailwindcss";@theme { --color-brand-primary: oklch(57% 0.2 260); --color-brand-accent: oklch(74% 0.14 140); --font-family-display: "Inter", sans-serif; --spacing-2xs: 0.25rem;}
Theme.json Generation
Sage’s Acorn package roots/acorn-theme-json reads your Tailwind config (or CSS @theme block) and generates a matching theme.json at build time. The result: colors, fonts, and spacing you define once in Tailwind appear automatically in the block editor’s design panel, so editors pick from the same tokens developers use in Blade templates.
For a design system, this eliminates the classic WordPress problem of “the theme’s colors” and “the block editor’s colors” drifting apart. There’s one source of truth — Tailwind — and both ends read from it.
Theme.json Partials (New in 2026)
@roots/vite-plugin v2.1.0 added support for theme.json partials — each block can ship a co-located .theme.js file defining block-specific styles. At build time, all partials merge into a single root theme.json. For large design systems with many custom blocks, this scales much better than a single 2,000-line JSON file.
Acorn: The Laravel Bridge
Sage’s not-so-secret weapon is Acorn — a Composer package that loads a Laravel application inside WordPress. Sage 11 ships with Acorn v6 (as of Sage 11.2.0), giving you:
- A service container — bind classes to interfaces, auto-resolve dependencies.
- Service providers — register bindings, event listeners, view composers in structured classes (
app/Providers/). - The Blade engine — what powers the templates above.
- View composers — attach data to specific views without cluttering templates.
- Configuration — Laravel-style
config/files with dotenv support. - Artisan-like console commands via
wp acorn— scaffolding, clearing caches, running migrations.
Example: A View Composer
Create app/View/Composers/Footer.php:
namespace App\View\Composers;use Roots\Acorn\View\Composer;class Footer extends Composer { protected static $views = ['partials.footer']; public function with() { return [ 'copyright_year' => date('Y'), 'social_links' => get_field('social_links', 'option'), ]; }}
Now resources/views/partials/footer.blade.php can reference $copyright_year and $social_links directly — no lookups in the template, no repeated queries, no mixing of data fetching and presentation.
Block Editor Integration
Sage 11 treats the block editor as a first-class surface. Key integration points:
- Separate
editor.cssandeditor.jsentry points. Styles and behavior specific to the editor are scoped away from the frontend. - HMR inside the iframed editor. Save an editor CSS file and see the change without refreshing.
- Generated
theme.json. Colors and fonts in the design panel come from your Tailwind config. - Block scaffolding via Acorn.
wp acorn make:block YourBlockscaffolds a new block with server-side rendering and editor UI. - Block API v3 requirement. Sage 11 iframes the editor, which requires blocks to support the current block API. Older blocks may silently break — worth checking.
Installing Sage 11
From your Bedrock site’s themes directory (web/app/themes/):
composer create-project roots/sage your-theme-namecd your-theme-namepnpm installpnpm run build
Activate the theme in WordPress. For development:
pnpm run dev
This starts the Vite dev server with HMR. Visit your site and edits to Blade templates, CSS, and JS hot-reload.
Common Gotchas
PHP 8.3 Required
Sage 11 requires PHP 8.3 minimum. Check your server’s version before installing.
Block API v3 Incompatibilities
Older blocks that don’t support apiVersion 3 may render incorrectly in Sage 11’s iframed editor. Update the blocks or accept that they’ll have visual glitches in the editor (they usually render fine on the frontend).
.blade.php Files Served Directly
Your web server should block direct HTTP access to .blade.php files. Bedrock + Trellis handle this automatically; custom server configs may not. An unblocked Blade file leaks template source as plaintext.
WP Engine Deploy Post-Hook
WP Engine requires a post-deploy.sh that runs wp acorn view:cache after every deploy. Without it, Blade views aren’t pre-compiled and response times are slower. See WP Engine’s Sage documentation for the exact script.
add_theme_support() Is Mostly Ignored
Sage 11 uses theme.json as the source of truth for theme capabilities. add_theme_support() calls still work for backward compatibility but are often superseded by theme.json settings. When something doesn’t take effect, check theme.json first.
Frequently Asked Questions
Is Sage 11 compatible with Sage 10 themes?
Partially. Blade templates port with minor changes, but anything touching the asset pipeline (Bud configuration, custom webpack rules) needs to be rewritten for Vite. Plan a week for a medium-complexity theme migration.
Does Sage 11 support full-site-editing themes?
Partially. Acorn 4.1+ added FSE support; Blade files take precedence over block templates when both exist. For fully FSE-first projects, the community fork strarsis/sage10-fse goes further. Roots’ own direction is to support both paradigms rather than force one.
Can I use Sage without Bedrock?
Yes. Sage is a theme; it works in any WordPress install with Composer access. Bedrock makes the Sage experience cleaner (the web/app/themes/ path is the native Sage location), but it’s not required.
Can I use Bootstrap or another CSS framework instead of Tailwind?
Yes. Remove the Tailwind dependency from package.json and the @import "tailwindcss" from your CSS entry. Install your framework of choice. Community Sage variants exist for Bootstrap (foo-sage is a common WooCommerce-focused variant).
What’s the performance overhead of Blade?
Negligible. Blade compiles to plain PHP and caches the compiled output. On first request the view is compiled (one-time cost); subsequent requests execute the cached PHP directly. In production, the overhead is a single include() per view.
How is Sage different from Roots Radicle?
Sage is a theme. Radicle is a full project starter that combines Bedrock + Sage + Acorn + curated mu-plugins in one Laravel-style layout. Use Sage when you want just a theme; use Radicle when you want the whole stack pre-wired.
Sage 11 in One Sentence
Sage 11 is what you get when you take the best developer experience from Laravel — Blade, service containers, Vite, Tailwind — and apply it to building a custom WordPress theme, with Acorn making all of it cohere inside a WordPress request. It’s not the right choice for every project. For developer-built custom themes with real design systems, it’s one of the strongest options available in 2026.
At Emnes, every custom theme we ship — including the one powering this site — is built on Sage. If you want help evaluating Sage for your project or want to see how we structure Sage themes at scale, get in touch.
Related reading: The Roots.io stack explained, Bedrock complete guide, and upcoming posts on what’s new in the Roots ecosystem.