ワンクリックで
wp-sage
Conventions for WordPress Bedrock + Sage + Acorn + Blade + Tailwind CSS 4 + Vite projects. Always-active rules.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Conventions for WordPress Bedrock + Sage + Acorn + Blade + Tailwind CSS 4 + Vite projects. Always-active rules.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Audit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to "improve accessibility", "a11y audit", "WCAG compliance", "screen reader support", "keyboard navigation", or "make accessible".
Optimize web performance for faster loading and better user experience. Use when asked to "speed up my site", "optimize performance", "reduce load time", "fix slow loading", "improve page speed", or "performance audit".
Optimize for search engine visibility and ranking. Use when asked to "improve SEO", "optimize for search", "fix meta tags", "add structured data", "sitemap optimization", or "search engine optimization".
WordPress operations with WP-CLI — search-replace, plugin/theme management, cron, cache, DB export/import, automation. Always-active rules when using wp commands.
WCAG 2.2 checklist for accessible markup. Always-active rules for every generated interface.
How to use Figma MCP and Variables2CSS to read designs from Figma and generate code consistent with tokens. Use when the designer shares a Figma link.
| name | wp-sage |
| description | Conventions for WordPress Bedrock + Sage + Acorn + Blade + Tailwind CSS 4 + Vite projects. Always-active rules. |
| user-invocable | false |
@theme, prefix {{PREFIX}})laravel-vite-plugin + @roots/vite-pluginlog1x/acf-composer)roots/acorn-post-types) + Extended CPTs (johnbillion/extended-cpts){{THEME_DIR}}/
├── app/
│ ├── Blocks/ # ACF Composer blocks
│ ├── Fields/ # ACF Composer field groups
│ ├── Options/ # ACF Composer option pages
│ ├── Providers/ThemeServiceProvider.php
│ ├── View/Composers/ # View composers (App, Header, Footer, etc.)
│ ├── setup.php # Theme setup (features, nav menus)
│ ├── filters.php
│ └── import.php # Helper functions
├── config/
│ ├── acf.php # ACF Composer configuration
│ └── post-types.php # Post types and taxonomies registration
├── resources/
│ ├── views/
│ │ ├── layouts/app.blade.php
│ │ ├── components/ # Composite Blade components
│ │ ├── blocks/ # ACF/Gutenberg block templates
│ │ ├── sections/ # Header, footer
│ │ ├── partials/ # Content partials, cards
│ │ ├── common/ # Reusable utilities
│ │ └── forms/ # Form templates
│ ├── css/
│ │ ├── app.css # CSS entry point
│ │ ├── common/ # theme.css, custom-properties.css, semantic-color.css, base.css
│ │ └── components/ # Atomic design: atoms/ molecules/ organisms/ design-system/
│ ├── js/
│ ├── images/ and fonts/
└── vite.config.js
{{PREFIX}} — MANDATORY| Type | Format | Example |
|---|---|---|
| Tailwind utilities | {{PREFIX}}:{utility} | {{PREFIX}}:flex, {{PREFIX}}:p-4, {{PREFIX}}:md:grid |
| CSS components | {{PREFIX}}-{name} | .{{PREFIX}}-button, .{{PREFIX}}-card, .{{PREFIX}}-container |
| Semantic utilities | {{PREFIX}}-{cat}-{var} | {{PREFIX}}-content-01, {{PREFIX}}-background-primary |
CSS import: @import 'tailwindcss' prefix({{PREFIX}});
@theme variables — never hardcode colors, spacing, or fonts{{PREFIX}}:md:, {{PREFIX}}:lg:, {{PREFIX}}:xl:| Type | Convention |
|---|---|
| Blade files | kebab-case (page-header-simple.blade.php) |
| PHP classes | PascalCase |
| Helper functions | snake_case |
| Blade variables | camelCase |
pint.json).bladeformatterrc)prettier-plugin-tailwindcss for class sorting@include(), not <x-component>Roots\Acorn\View\Composerwith() returns an array of data for the viewPost types and taxonomies are defined in config/post-types.php and automatically registered via roots/acorn-post-types using John Billion's Extended CPTs library.
config/post-types.php structure:
return [
'post_types' => [
'cpt_name' => [
'names' => [
'singular' => 'Singular',
'plural' => 'Plural',
'slug' => 'cpt-name',
],
'labels' => [...], // Full WP labels
'menu_icon' => 'dashicons-admin-post',
'supports' => ['title', 'editor', 'thumbnail', 'custom-fields'],
'hierarchical' => false,
'has_archive' => true,
'show_in_rest' => true,
'public' => true,
'publicly_queryable' => true,
'exclude_from_search' => false,
],
],
'taxonomies' => [
'tax_name' => [
'post_types' => ['post', 'page', 'cpt_name'],
'names' => [
'singular' => 'Singular',
'plural' => 'Plural',
],
'labels' => [...],
'hierarchical' => true,
'show_in_rest' => true,
],
],
];
Loading config in ThemeServiceProvider:
public function register()
{
parent::register();
$configPath = get_theme_file_path('config/post-types.php');
if (is_file($configPath)) {
$this->app->make('config')->set('post-types', require $configPath);
}
}
Best practices:
hierarchical: true for page-like post typesshow_in_rest: true for Gutenbergexclude_from_search: true only for internal CPTsACF field groups are defined as PHP classes in app/Fields/ extending Log1x\AcfComposer\Field.
Example app/Fields/ExampleFields.php:
<?php
namespace App\Fields;
use Log1x\AcfComposer\Builder;
use Log1x\AcfComposer\Field;
class ExampleFields extends Field
{
public function fields(): array
{
$fields = Builder::make('example_fields');
$fields
->setLocation()
->where('post_type', 'post');
$fields
->addTab(__('General', '{{TEXT_DOMAIN}}'), [
'placement' => 'top',
])
->addText('title', [
'label' => __('Title', '{{TEXT_DOMAIN}}'),
'instructions' => __('Enter a title', '{{TEXT_DOMAIN}}'),
'required' => 1,
])
->addTextarea('description', [
'label' => __('Description', '{{TEXT_DOMAIN}}'),
'maxlength' => 300,
])
->addImage('image', [
'label' => __('Image', '{{TEXT_DOMAIN}}'),
'return_format' => 'array',
'preview_size' => 'medium',
])
->addRepeater('items', [
'label' => __('Items', '{{TEXT_DOMAIN}}'),
'layout' => 'table',
'button_label' => __('Add Item', '{{TEXT_DOMAIN}}'),
])
->addText('name')
->addTextarea('description')
->endRepeater();
return $fields->build();
}
}
Location rules:
$fields
->setLocation()
->where('post_type', 'post')
->or('post_type', 'page')
->or('page_template', 'template-custom.blade.php');
Useful commands:
wp acorn acf:make field FieldName # Generate field group
wp acorn acf:cache # Cache fields (prod)
wp acorn acorn ide:helpers # PHPDoc autocomplete
Blocks are defined in app/Blocks/ extending Log1x\AcfComposer\Block.
Example app/Blocks/ExampleBlock.php:
<?php
namespace App\Blocks;
use Log1x\AcfComposer\Block;
use Log1x\AcfComposer\Builder;
class ExampleBlock extends Block
{
public $name = 'Example Block';
public $description = 'Block description';
public $category = 'theme'; // or 'common', 'formatting', etc.
public $icon = 'admin-post'; // dashicon
public $keywords = ['example', 'test'];
public $post_types = ['page', 'post']; // Restrict to specific CPTs
// Supported alignments
public $supports = [
'align' => ['wide', 'full'],
'mode' => false, // Disable edit/preview toggle
'jsx' => true,
];
public function fields(): array
{
$fields = Builder::make('example_block');
$fields
->addText('title', [
'label' => __('Title', '{{TEXT_DOMAIN}}'),
])
->addWysiwyg('content', [
'label' => __('Content', '{{TEXT_DOMAIN}}'),
]);
return $fields->build();
}
// Data passed to the Blade template
public function with(): array
{
return [
'title' => $this->title,
'content' => $this->content,
'classes' => $this->classes, // Automatic CSS classes
];
}
}
Template resources/views/blocks/example-block/example-block.blade.php:
<div {!! $attributes !!}>
<h2>{{ $title }}</h2>
<div>{!! $content !!}</div>
</div>
$attributes helper: automatically generates class, id, data- attributes.
Commands:
wp acorn acf:make block BlockName # Generate block + template
Option pages for theme/site settings in app/Options/.
Example app/Options/ThemeSettings.php:
<?php
namespace App\Options;
use Log1x\AcfComposer\Builder;
use Log1x\AcfComposer\Options as Field;
class ThemeSettings extends Field
{
public $name = 'Theme Settings';
public $title = 'Settings | Theme';
public $menu_slug = 'theme-settings';
public $parent = 'options-general.php'; // Under "Settings"
public $capability = 'manage_options';
public $position = 30;
public $redirect = false;
public function fields(): array
{
$fields = Builder::make('theme_settings');
$fields
->addTab(__('General', '{{TEXT_DOMAIN}}'))
->addText('site_phone', [
'label' => __('Phone', '{{TEXT_DOMAIN}}'),
])
->addText('site_email', [
'label' => __('Email', '{{TEXT_DOMAIN}}'),
]);
return $fields->build();
}
}
Retrieving options:
$phone = get_field('site_phone', 'option');
Commands:
wp acorn acf:make options OptionsName # Generate option page
@scripts → js, @styles → css, @fonts → fonts, @images → images
{{PREFIX}}: prefix on Tailwind utilities — it's mandatory@apply in CSS — prefer utilities in markup@theme variables{{COMPONENTS_CATALOG}}bud.config.js — the bundler is Vitevite.config.js without confirmation<a role="button"> or clickable <div> — use native elementsoutline on :focus without a visible alternativearia-hidden="true" focusable="false" on decorative SVGsalt=""register_post_type() — use config/post-types.phpapp/Fields/)register_block_type() — use ACF Composer (app/Blocks/)get_field() directly in templates — pass data via with() in the Block/ComposerIf the project migrates from a Nunjucks design system:
.njk → .blade.php in resources/views/components/{atoms|molecules|organisms}/.js → resources/js/{atoms|molecules|organisms}/.css → stay in resources/css/components/ (unchanged).config.yml → View Composer or @include() parameters