Sage - Modern WordPress Téma
A Sage egy modern WordPress téma fejlesztési keretrendszer, amely Laravel Blade templating-et, modern JavaScript tooling-ot és az Acorn keretrendszert használja. A Roots csapat fejleszti.
Mi a Sage?
A Sage a WordPress témafejlesztés legjobb gyakorlatait ötvözi:
- Laravel Blade - Tiszta, kifejező template szintaxis
- Vite - Villámgyors fejlesztői szerver és build tool
- Tailwind CSS - Utility-first CSS keretrendszer (opcionális)
- Modern JavaScript - ES6+, modulok támogatása
- View Composers - Adatok és logika szétválasztása
- Acorn - Laravel komponensek WordPress-ben
Előfeltételek
- PHP >= 8.1
- Composer
- Node.js >= 18.0.0
- Yarn vagy npm
Telepítés
Új Sage téma létrehozása
# A themes mappában
cd wp-content/themes
# Sage telepítése
composer create-project roots/sage tema-neve
Függőségek telepítése
cd tema-neve
# PHP függőségek
composer install
# Node függőségek
yarn install
# vagy
npm install
Acorn telepítése
A Sage-nek szüksége van az Acorn-ra. Telepítheted plugin-ként vagy Composer-rel:
# Bedrock használata esetén (ajánlott)
composer require roots/acorn
# Vagy WordPress pluginként
# Töltsd le: https://roots.io/acorn/
Fejlesztés
Fejlesztői szerver indítása
yarn dev
# vagy
npm run dev
Ez elindítja a Vite fejlesztői szervert HMR (Hot Module Replacement) támogatással.
Build készítése
yarn build
# vagy
npm run build
Mappastruktúra
sage/
├── app/
│ ├── Providers/ # Service Providers
│ ├── View/
│ │ └── Composers/ # View Composers
│ ├── filters.php # WordPress filterek
│ ├── setup.php # Téma beállítások
│ └── helpers.php # Helper függvények
├── config/
│ ├── app.php # Alkalmazás konfiguráció
│ ├── theme.php # Téma konfiguráció
│ └── view.php # View konfiguráció
├── public/ # Build output (ne szerkeszd)
├── resources/
│ ├── views/ # Blade templates
│ │ ├── layouts/
│ │ ├── partials/
│ │ ├── sections/
│ │ └── components/
│ ├── scripts/ # JavaScript
│ │ └── app.js
│ └── styles/ # CSS/SCSS
│ └── app.css
├── storage/
│ └── framework/
│ └── views/ # Compiled Blade views
├── bud.config.js # Bud build konfiguráció
├── composer.json
├── package.json
├── functions.php
├── index.php
└── style.css
Blade Templates
Alap layout
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(wp_head())
</head>
<body @php(body_class())>
@php(wp_body_open())
<div id="app">
@include('partials.header')
<main class="main">
@yield('content')
</main>
@include('partials.footer')
</div>
@php(wp_footer())
</body>
</html>
Oldal template
resources/views/page.blade.php:
@extends('layouts.app')
@section('content')
@while(have_posts()) @php(the_post())
@includeFirst(['partials.content-page', 'partials.content'])
@endwhile
@endsection
Bejegyzés tartalom
resources/views/partials/content.blade.php:
<article @php(post_class())>
<header>
<h1 class="entry-title">
{!! $title !!}
</h1>
@include('partials.entry-meta')
</header>
<div class="entry-content">
@php(the_content())
</div>
</article>
Header
resources/views/partials/header.blade.php:
<header class="site-header">
<a class="brand" href="{{ home_url('/') }}">
{!! $siteName !!}
</a>
@if (has_nav_menu('primary_navigation'))
<nav class="nav-primary" aria-label="{{ wp_get_nav_menu_name('primary_navigation') }}">
{!! wp_nav_menu([
'theme_location' => 'primary_navigation',
'container' => false,
'echo' => false
]) !!}
</nav>
@endif
</header>
View Composers
A View Composers lehetővé teszi az adatok és a megjelenítés szétválasztását.
App Composer (globális)
app/View/Composers/App.php:
<?php
namespace App\View\Composers;
use Roots\Acorn\View\Composer;
class App extends Composer
{
protected static $views = ['*'];
public function with()
{
return [
'siteName' => $this->siteName(),
];
}
public function siteName()
{
return get_bloginfo('name', 'display');
}
}
Post Composer
app/View/Composers/Post.php:
<?php
namespace App\View\Composers;
use Roots\Acorn\View\Composer;
class Post extends Composer
{
protected static $views = [
'partials.content',
'partials.content-*',
];
public function with()
{
return [
'title' => $this->title(),
'permalink' => $this->permalink(),
'thumbnail' => $this->thumbnail(),
'excerpt' => $this->excerpt(),
'date' => $this->date(),
'author' => $this->author(),
'categories' => $this->categories(),
];
}
public function title()
{
if ($this->view->name() === 'partials.content-single') {
return get_the_title();
}
return sprintf(
'<a href="%s">%s</a>',
get_permalink(),
get_the_title()
);
}
public function permalink()
{
return get_permalink();
}
public function thumbnail()
{
return get_the_post_thumbnail(null, 'large');
}
public function excerpt()
{
return has_excerpt()
? get_the_excerpt()
: wp_trim_words(get_the_content(), 40);
}
public function date()
{
return get_the_date();
}
public function author()
{
return get_the_author();
}
public function categories()
{
return get_the_category();
}
}
Blade Komponensek
Komponens létrehozása
resources/views/components/button.blade.php:
@props([
'type' => 'primary',
'url' => '#',
'size' => 'md'
])
<a
href="{{ $url }}"
{{ $attributes->merge(['class' => "btn btn-{$type} btn-{$size}"]) }}
>
{{ $slot }}
</a>
Komponens használata
<x-button type="secondary" url="/contact">
Kapcsolat
</x-button>
<x-button type="primary" url="/shop" class="mt-4">
Vásárlás
</x-button>
Komponens slot-okkal
resources/views/components/card.blade.php:
@props(['title' => null])
<div {{ $attributes->merge(['class' => 'card']) }}>
@if ($title)
<div class="card-header">
<h3>{{ $title }}</h3>
</div>
@endif
<div class="card-body">
{{ $slot }}
</div>
@if (isset($footer))
<div class="card-footer">
{{ $footer }}
</div>
@endif
</div>
Használata:
<x-card title="Termék neve">
<p>Termék leírása...</p>
<x-slot:footer>
<x-button url="/buy">Megveszem</x-button>
</x-slot:footer>
</x-card>
Téma beállítások
app/setup.php:
<?php
namespace App;
use Roots\Acorn\Assets\Asset;
/**
* Téma támogatások regisztrálása
*/
add_action('after_setup_theme', function () {
// Post thumbnails
add_theme_support('post-thumbnails');
// Title tag
add_theme_support('title-tag');
// HTML5 támogatás
add_theme_support('html5', [
'caption',
'comment-form',
'comment-list',
'gallery',
'search-form',
'script',
'style',
]);
// Egyedi logo
add_theme_support('custom-logo', [
'height' => 100,
'width' => 400,
'flex-height' => true,
'flex-width' => true,
]);
// Editor stílusok
add_theme_support('editor-styles');
add_editor_style('public/app.css');
// Responsive embeds
add_theme_support('responsive-embeds');
// Menük regisztrálása
register_nav_menus([
'primary_navigation' => __('Primary Navigation', 'sage'),
'footer_navigation' => __('Footer Navigation', 'sage'),
]);
// Képméretek
add_image_size('hero', 1920, 1080, true);
add_image_size('card', 600, 400, true);
});
/**
* Sidebar regisztrálása
*/
add_action('widgets_init', function () {
register_sidebar([
'name' => __('Primary Sidebar', 'sage'),
'id' => 'sidebar-primary',
'before_widget' => '<section class="widget %1$s %2$s">',
'after_widget' => '</section>',
'before_title' => '<h3>',
'after_title' => '</h3>',
]);
register_sidebar([
'name' => __('Footer', 'sage'),
'id' => 'sidebar-footer',
'before_widget' => '<section class="widget %1$s %2$s">',
'after_widget' => '</section>',
'before_title' => '<h3>',
'after_title' => '</h3>',
]);
});
Vite konfiguráció
vite.config.js:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/styles/app.css',
'resources/scripts/app.js',
],
refresh: true,
}),
],
});
Tailwind CSS
A Sage alapértelmezetten Tailwind CSS-t használ.
tailwind.config.js:
export default {
content: [
'./app/**/*.php',
'./resources/**/*.{php,vue,js,blade.php}',
],
theme: {
extend: {
colors: {
primary: '#3490dc',
secondary: '#ffed4a',
danger: '#e3342f',
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
},
},
plugins: [],
};
JavaScript
resources/scripts/app.js:
// Alpine.js (opcionális)
import Alpine from 'alpinejs';
window.Alpine = Alpine;
Alpine.start();
// Egyedi kód
document.addEventListener('DOMContentLoaded', () => {
// Mobile menu toggle
const menuToggle = document.querySelector('.menu-toggle');
const navigation = document.querySelector('.nav-primary');
menuToggle?.addEventListener('click', () => {
navigation?.classList.toggle('is-open');
});
});
WooCommerce támogatás
WooCommerce template-ek
Hozz létre egy woocommerce mappát a resources/views alatt:
resources/views/woocommerce/
├── single-product.blade.php
├── archive-product.blade.php
└── partials/
└── product-card.blade.php
resources/views/woocommerce/archive-product.blade.php:
@extends('layouts.app')
@section('content')
<div class="woocommerce">
@php(woocommerce_content())
</div>
@endsection
WooCommerce támogatás regisztrálása
app/setup.php:
add_action('after_setup_theme', function () {
add_theme_support('woocommerce');
add_theme_support('wc-product-gallery-zoom');
add_theme_support('wc-product-gallery-lightbox');
add_theme_support('wc-product-gallery-slider');
});
WP-CLI parancsok
# View cache törlése
wp acorn view:clear
# View cache létrehozása
wp acorn view:cache
# Konfiguráció cache
wp acorn config:cache
wp acorn config:clear
# Komponens generálása
wp acorn make:component Button
# View Composer generálása
wp acorn make:composer Post
Sage + Bedrock + Trellis
A teljes Roots stack:
projekt/
├── trellis/ # Szerver és deployment
└── site/ # Bedrock
├── composer.json
├── web/
│ └── app/
│ └── themes/
│ └── sage/ # Sage téma
└── ...
Gyakori műveletek
Egyedi oldal template
resources/views/template-custom.blade.php:
{{--
Template Name: Egyedi Sablon
--}}
@extends('layouts.app')
@section('content')
<div class="custom-template">
@while(have_posts()) @php(the_post())
<h1>{!! get_the_title() !!}</h1>
{!! get_the_content() !!}
@endwhile
</div>
@endsection
Shortcode létrehozása
app/filters.php:
add_shortcode('custom_button', function ($atts) {
$atts = shortcode_atts([
'url' => '#',
'text' => 'Kattints ide',
], $atts);
return view('components.button', [
'url' => $atts['url'],
'slot' => $atts['text'],
])->render();
});