Skillshub wp-classic
Conventions for WordPress Bedrock projects with classic PHP theme (no Sage/Acorn/Blade). Always-active rules.
install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/alessioarzenton/claude-code-wp-toolkit/wp-classic" ~/.claude/skills/comeonoliver-skillshub-wp-classic && rm -rf "$T"
manifest:
skills/alessioarzenton/claude-code-wp-toolkit/wp-classic/SKILL.mdsource content
WP-Classic Stack — Bedrock + Classic PHP Theme
Stack
- WordPress 6.x + Bedrock
- PHP 8.2+ (recommended 8.4)
- Theme: Classic WordPress theme — PHP templates, no Blade
- CSS: Tailwind CSS 4 (optional) or vanilla CSS (prefix
if configured){{PREFIX}} - Bundler: Webpack/Gulp or none (check
)package.json - Custom Fields: ACF Pro (GUI backend) — if configured
- Post Types/Taxonomies:
/register_post_type()
inregister_taxonomy()inc/post-types.php
Typical project structure
{{THEME_DIR}}/ ├── templates/ # Page templates (page-*.php, single-*.php, archive-*.php) ├── parts/ # Reusable template partials ├── inc/ │ ├── post-types.php # CPT and taxonomy registration │ ├── acf-blocks.php # ACF blocks (if ACF active) │ ├── helpers.php # Helper functions │ └── enqueue.php # Scripts and styles enqueue ├── assets/ │ ├── css/ # Stylesheets │ ├── js/ # JavaScript │ ├── images/ # Images │ └── fonts/ # Fonts ├── acf-json/ # Exported ACF field groups (if ACF active) ├── functions.php # Theme setup, hooks, filters, require inc/* ├── style.css # Main stylesheet (with theme header) ├── header.php # Global header ├── footer.php # Global footer ├── sidebar.php # Sidebar (if present) ├── index.php # Fallback template ├── front-page.php # Homepage ├── page.php # Generic pages ├── single.php # Single post ├── archive.php # Archive ├── search.php # Search results └── 404.php # Error page
Prefix {{PREFIX}}
(if configured)
{{PREFIX}}If the project uses a Tailwind CSS 4 prefix:
| Type | Format | Example |
|---|---|---|
| Tailwind utilities | | , |
| CSS components | | , |
| Semantic utilities | | |
Approach
- Use the WordPress template hierarchy (
,page-{slug}.php
, etc.)single-{cpt}.php - Template partials with
— never hardcoded pathsget_template_part('parts/name', 'variant') - Avoid complex logic in templates — move to
inc/helpers.php - Use WordPress Coding Standards (WPCS)
Naming
| Type | Convention |
|---|---|
| Template files | kebab-case (, ) |
| Functions | snake_case with theme prefix (, ) |
| PHP classes | PascalCase |
| Constants | UPPER_SNAKE_CASE |
Code Style
- PHP: WordPress Coding Standards (PHPCS + WPCS)
- JS/CSS: Prettier
- PHP indentation: tabs (WPCS standard)
Templates and Components
- Components are included with
(WP 5.5+)get_template_part('parts/name', $args) - Pass data to templates with the third
parameter:$argsget_template_part('parts/card', 'post', [ 'title' => get_the_title(), 'excerpt' => get_the_excerpt(), 'thumbnail_id' => get_post_thumbnail_id(), ]); - In the partial template, access data with
:$args$title = $args['title'] ?? ''; $excerpt = $args['excerpt'] ?? '';
Post Types and Taxonomies
Registered in
inc/post-types.php with native WordPress functions.
Example:
function mytheme_register_post_types() { register_post_type('bando', [ 'labels' => [ 'name' => __('Bandi', '{{TEXT_DOMAIN}}'), 'singular_name' => __('Bando', '{{TEXT_DOMAIN}}'), ], 'public' => true, 'has_archive' => true, 'show_in_rest' => true, 'supports' => ['title', 'editor', 'thumbnail', 'custom-fields'], 'menu_icon' => 'dashicons-media-document', 'rewrite' => ['slug' => 'bandi'], ]); } add_action('init', 'mytheme_register_post_types');
Standard ACF (if configured)
ACF Blocks
Registered with
acf_register_block_type() in inc/acf-blocks.php:
function mytheme_register_acf_blocks() { if (!function_exists('acf_register_block_type')) return; acf_register_block_type([ 'name' => 'hero', 'title' => __('Hero', '{{TEXT_DOMAIN}}'), 'description' => __('Hero block with image and text', '{{TEXT_DOMAIN}}'), 'render_template' => 'parts/blocks/hero.php', 'category' => 'theme', 'icon' => 'cover-image', 'keywords' => ['hero', 'banner'], 'supports' => ['align' => ['wide', 'full']], ]); } add_action('acf/init', 'mytheme_register_acf_blocks');
Block template
<?php // parts/blocks/hero.php $title = get_field('title'); $image = get_field('image'); $classes = 'block-hero'; if (!empty($block['className'])) $classes .= ' ' . $block['className']; if (!empty($block['align'])) $classes .= ' align' . $block['align']; ?> <section class="<?php echo esc_attr($classes); ?>"> <h2><?php echo esc_html($title); ?></h2> <?php if ($image): ?> <img src="<?php echo esc_url($image['url']); ?>" alt="<?php echo esc_attr($image['alt']); ?>"> <?php endif; ?> </section>
Field Groups
Created from the ACF Pro GUI. If configured, exported to
acf-json/ for version control.
// In functions.php or inc/acf.php function mytheme_acf_json_save_point($path) { return get_stylesheet_directory() . '/acf-json'; } add_filter('acf/settings/save_json', 'mytheme_acf_json_save_point');
Enqueue Scripts and Styles
// inc/enqueue.php function mytheme_enqueue_assets() { $theme_version = wp_get_theme()->get('Version'); wp_enqueue_style( '{{TEXT_DOMAIN}}-style', get_stylesheet_uri(), [], $theme_version ); wp_enqueue_script( '{{TEXT_DOMAIN}}-main', get_theme_file_uri('assets/js/main.js'), [], $theme_version, true ); } add_action('wp_enqueue_scripts', 'mytheme_enqueue_assets');
What NOT to do
- Don't use
or Blade syntax — this theme uses plain PHP@include() - Don't use View Composers or Acorn — they are not installed
- Don't use
orbud.config.js
without verifying firstvite.config.js - Don't hardcode colors if CSS variables exist — use
var(--color-*) - Don't use
or clickable<a role="button">
— use native elements<div> - Don't remove
onoutline
without a visible alternative:focus - Don't omit
on decorative SVGsaria-hidden="true" focusable="false" - Don't add alt text to decorative images — use
alt="" - Don't register post types inline in
— usefunctions.phpinc/post-types.php - Don't create ACF field groups via complex PHP code — use the GUI and
acf-json/ - Don't use fixed heading levels in reusable components — make them parametric
- Don't use
without escaping — alwaysecho
,esc_html()
,esc_attr()
,esc_url()wp_kses_post()