Skip to content
Sage
v11.0.1
  • Sage page

Advanced WordPress starter theme with Tailwind CSS and Laravel Blade

Hybrid WordPress theme development — Blade templates, Tailwind CSS, and Vite HMR for editor styles

Modern WordPress theme development with Laravel's Blade templating, Tailwind CSS that syncs to the block editor, and a professional development workflow.

~/wp-content/themes

$ composer create-project roots/sage my-theme

Creating a "roots/sage" project at "./my-theme"
Installing roots/sage (11.0.4)
  - Downloading roots/sage (11.0.4)
  - Installing roots/sage (11.0.4): Extracting archive
Created project in ./my-theme

$ cd my-theme && npm install && npm run build

added 127 packages in 8s

$ npm run build

> build
> vite build

vite v6.3.6 building for production...
✓ 4 modules transformed.
Generated an empty chunk: "app".
public/build/assets/editor.deps-DxpY22xl.json   0.02 kB │ gzip: 0.04 kB
public/build/manifest.json                      0.67 kB │ gzip: 0.23 kB
public/build/assets/theme.json                 34.56 kB │ gzip: 5.01 kB
public/build/assets/editor-ehnqdtwn.css         5.51 kB │ gzip: 1.83 kB
public/build/assets/app-Hfb9ghGI.css           19.27 kB │ gzip: 5.64 kB
public/build/assets/app-l0sNRNKZ.js             0.00 kB │ gzip: 0.02 kB
public/build/assets/editor-CfU4GbSP.js          0.03 kB │ gzip: 0.05 kB
✓ built in 123ms

Laravel Blade templates with WordPress

Use Laravel's powerful Blade templating engine for modern PHP templating features like template inheritance, components, and directives. Build reusable components with props and clean template organization.

Base layout template

resources/views/layouts/app.blade.php

<!doctype html>
<html @php(language_attributes())>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    @php(do_action('get_header'))
    @php(wp_head())

    @vite(['resources/css/app.css', 'resources/js/app.js'])
  </head>

  <body @php(body_class())>
    @php(wp_body_open())

    <div id="app">
      <a class="sr-only focus:not-sr-only" href="#main">
        {{ __('Skip to content', 'sage') }}
      </a>

      @include('sections.header')

      <main id="main" class="main">
        @yield('content')
      </main>

      @hasSection('sidebar')
        <aside class="sidebar">
          @yield('sidebar')
        </aside>
      @endif

      @include('sections.footer')
    </div>

    @php(do_action('get_footer'))
    @php(wp_footer())
  </body>
</html>

Reusable alert component

resources/views/components/alert.blade.php

@props([
  'type' => null,
  'message' => null,
])

@php($class = match ($type) {
  'success' => 'text-green-50 bg-green-400',
  'caution' => 'text-yellow-50 bg-yellow-400',
  'warning' => 'text-red-50 bg-red-400',
  default => 'text-indigo-50 bg-indigo-400',
})

<div {{ $attributes->merge(['class' => "px-2 py-1 {$class}"]) }}>
  {!! $message ?? $slot !!}
</div>

Tailwind CSS synced to WordPress block editor

Sage automatically generates theme.json from your Tailwind configuration. Your color palette, font families, and sizes are immediately available in the WordPress block editor with zero configuration.

Tailwind CSS configuration

resources/css/app.css

@import "tailwindcss" theme(static);
@source "../views/";
@source "../../app/";

Generated theme.json

theme.json

{
  "__processed__": "This file was generated using Vite",
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 3,
  "settings": {
    "layout": {
      "contentSize": "48rem"
    },
    "background": {
      "backgroundImage": true
    },
    "color": {
      "custom": false,
      "customDuotone": false,
      "customGradient": false,
      "defaultDuotone": false,
      "defaultGradients": false,
      "defaultPalette": false,
      "duotone": [],
      "palette": [
        {
          "name": "Gray (50)",
          "slug": "gray-50",
          "color": "oklch(98.5% .002 247.839)"
        },
        {
          "name": "Gray (100)",
          "slug": "gray-100",
          "color": "oklch(96.7% .003 264.542)"
        },
        {
          "name": "Gray (200)",
          "slug": "gray-200",
          "color": "oklch(92.8% .006 264.531)"
        },
        {
          "name": "Gray (300)",
          "slug": "gray-300",
          "color": "oklch(87.2% .01 258.338)"
        },
        {
          "name": "Gray (400)",
          "slug": "gray-400",
          "color": "oklch(70.7% .022 261.325)"
        },
        {
          "name": "Gray (500)",
          "slug": "gray-500",
          "color": "oklch(55.1% .027 264.364)"
        },
        {
          "name": "Gray (600)",
          "slug": "gray-600",
          "color": "oklch(44.6% .03 256.802)"
        },
        {
          "name": "Gray (700)",
          "slug": "gray-700",
          "color": "oklch(37.3% .034 259.733)"
        },
        {
          "name": "Gray (800)",
          "slug": "gray-800",
          "color": "oklch(27.8% .033 256.848)"
        },
        {
          "name": "Gray (900)",
          "slug": "gray-900",
          "color": "oklch(21% .034 264.665)"
        },
        {
          "name": "Gray (950)",
          "slug": "gray-950",
          "color": "oklch(13% .028 261.692)"
        },
        {
          "name": "Black",
          "slug": "black",
          "color": "#000"
        },
        {
          "name": "White",
          "slug": "white",
          "color": "#fff"
        }
      ]
    },
    "custom": {
      "spacing": {},
      "typography": {
        "font-size": {},
        "line-height": {}
      }
    },
    "spacing": {
      "padding": true,
      "units": [
        "px",
        "%",
        "em",
        "rem",
        "vw",
        "vh"
      ]
    },
    "typography": {
      "defaultFontSizes": false,
      "customFontSize": false,
      "fontFamilies": [
        {
          "name": "sans",
          "slug": "sans",
          "fontFamily": "ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji"
        },
        {
          "name": "serif",
          "slug": "serif",
          "fontFamily": "ui-serif,Georgia,Cambria,Times New Roman,Times,serif"
        },
        {
          "name": "mono",
          "slug": "mono",
          "fontFamily": "ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace"
        }
      ],
      "fontSizes": [
        {
          "name": "xs",
          "slug": "xs",
          "size": ".75rem"
        },
        {
          "name": "sm",
          "slug": "sm",
          "size": ".875rem"
        },
        {
          "name": "base",
          "slug": "base",
          "size": "1rem"
        },
        {
          "name": "lg",
          "slug": "lg",
          "size": "1.125rem"
        },
        {
          "name": "xl",
          "slug": "xl",
          "size": "1.25rem"
        },
        {
          "name": "2xl",
          "slug": "2xl",
          "size": "1.5rem"
        },
        {
          "name": "3xl",
          "slug": "3xl",
          "size": "1.875rem"
        },
        {
          "name": "4xl",
          "slug": "4xl",
          "size": "2.25rem"
        },
        {
          "name": "5xl",
          "slug": "5xl",
          "size": "3rem"
        },
        {
          "name": "6xl",
          "slug": "6xl",
          "size": "3.75rem"
        },
        {
          "name": "7xl",
          "slug": "7xl",
          "size": "4.5rem"
        },
        {
          "name": "8xl",
          "slug": "8xl",
          "size": "6rem"
        },
        {
          "name": "9xl",
          "slug": "9xl",
          "size": "8rem"
        }
      ]
    }
  }
}

Professional theme structure & modern workflow

Organized directory structure with PSR-4 autoloading, Vite build system with HMR, and editor styles that update live. Built on Acorn for Laravel features in WordPress.

Theme structure

my-theme

.
├── app/
│   ├── Providers/
│   ├── View/
│   │   └── Composers/
│   ├── filters.php
│   └── setup.php
├── resources/
│   ├── css/
│   │   ├── app.css
│   │   └── editor.css
│   ├── js/
│   │   ├── app.js
│   │   └── editor.js
│   └── views/
│       ├── components/
│       ├── layouts/
│       ├── partials/
│       └── sections/
├── public/
├── composer.json
├── package.json
├── theme.json
└── vite.config.js

Vite configuration with theme.json generation

vite.config.js

import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite';
import laravel from 'laravel-vite-plugin'
import { wordpressPlugin, wordpressThemeJson } from '@roots/vite-plugin';

export default defineConfig({
  base: '/app/themes/sage/public/build/',
  plugins: [
    tailwindcss(),
    laravel({
      input: [
        'resources/css/app.css',
        'resources/js/app.js',
        'resources/css/editor.css',
        'resources/js/editor.js',
      ],
      refresh: true,
    }),

    wordpressPlugin(),

    // Generate the theme.json file in the public/build/assets directory
    // based on the Tailwind config and the theme.json file from base theme folder
    wordpressThemeJson({
      disableTailwindColors: false,
      disableTailwindFonts: false,
      disableTailwindFontSizes: false,
    }),
  ],
  resolve: {
    alias: {
      '@scripts': '/resources/js',
      '@styles': '/resources/css',
      '@fonts': '/resources/fonts',
      '@images': '/resources/images',
    },
  },
})

Block editor integration with HMR

Hot Module Replacement works in the WordPress block editor. Style changes update instantly without page refreshes, making theme development faster and more enjoyable.

PSR-4 autoloading & Laravel patterns

Organized PHP with namespace autoloading, service providers for extensibility, and view composers for clean data management. All powered by Acorn.

Sponsors

Help support our open-source development efforts

Built with Sage

Recommendations

If you have to use WordPress, use Bedrock for your web app and Sage to develop your custom theme. Any Laravel developer will feel at home in these tools.

Rory McDaniel Rory McDaniel

I think 2 years ago I tweeted to y'all to say I love Sage -- now I'm getting started with Bedrock and loving it! thank you for making me feel like WordPress can be sane, secure, and modern.

Michael Snook Michael Snook

In my opinion, the roots.io toolkit is the most sane way to do WordPress in 2023

Andrew Halliwell Andrew Halliwell

Sage has brought WordPress development into the modern age. It's absolutely excellent and we'd have probably moved away from WordPress by now without it. Can't recommend it highly enough.

James Tudsbury James Tudsbury

Subscribe for updates

Join over 8,000 subscribers on our newsletter to get the latest Roots updates and tips on building better WordPress sites

Looking for WordPress plugin recommendations, the newest modern WordPress projects, and general web development tips and articles?

One last step! Check your email for a verification link.