wordpress-themes

WordPress custom theme development specialist focused on clean, maintainable code following VIP standards. Includes modular theme structure, dart-sass via Homebrew, proper script/style enqueueing, template parts organization, text domain management, and comprehensive security practices (escaping, sanitization, file paths).

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "wordpress-themes" with this command: npx skills add oiler/claude-skills/oiler-claude-skills-wordpress-themes

WordPress Custom Theme Development

Build clean, VIP-compliant WordPress custom themes with modular structure and modern tooling.

Core Philosophy

  • Minimal Plugin Dependency: Use public plugins for specialized functions (SEO, security), keep custom code in theme
  • VIP Standards: Follow WordPress VIP coding standards for enterprise-grade quality
  • Clean Organization: Modular structure with clear separation of concerns
  • Maintainability: Easy to understand, easy to update

Theme Directory Structure

theme-root/
├── src/
│   └── scss/
│       ├── vendor/        # Third-party CSS (reset, normalize)
│       ├── core/          # Variables, mixins, utilities
│       ├── pages/         # Page-specific CSS
│       └── styles.scss    # Main entry point
├── assets/
│   ├── css/
│   │   ├── styles.css     # Compiled main stylesheet
│   │   └── pages/         # Compiled page-specific stylesheets
│   ├── img/site/
│   └── svg/
├── inc/
│   └── functions/
│       ├── css_pagetype.php
│       ├── js_scripts.php
│       ├── theme_media.php
│       └── custom_post_types.php
├── template-parts/
│   ├── content-post.php
│   ├── content-page.php
│   ├── content-[cpt].php
│   ├── footer-markup.php
│   └── header-markup.php
├── 404.php
├── footer.php
├── functions.php          # Clean, mostly includes
├── header.php
├── index.php
├── sidebar.php
├── style.css              # Theme metadata
└── template-front.php

functions.php Pattern

Keep functions.php as a clean table of contents with descriptive comments.

<?php

/**
 * Theme text domain constant
 */
if ( ! defined( 'CUSTOM_THEME_TEXT_DOMAIN' ) ) {
    define( 'CUSTOM_THEME_TEXT_DOMAIN', 'custom-theme' );
}

add_theme_support( "title-tag" );
add_theme_support( "responsive-embeds" );
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );

// includes js file based on page type
require get_template_directory() . '/inc/functions/js_scripts.php';

// includes css file based on page type
require get_template_directory() . '/inc/functions/css_pagetype.php';

// media and image support
require get_template_directory() . '/inc/functions/theme_media.php';

// custom post types and taxonomies
require get_template_directory() . '/inc/functions/custom_post_types.php';

CRITICAL: Never include flush_rewrite_rules() in production code, even commented out.

CSS/Sass Workflow

Setup (dart-sass via Homebrew)

# Installation
brew install sass/sass/sass
brew upgrade sass

# Watch mode (development)
cd src/scss
sass styles.scss:../../assets/css/styles.css --watch

# Build mode (production)
sass styles.scss:../../assets/css/styles.css --style=compressed

Helpful Shell Aliases (zsh)

alias sassw='sass styles.scss:../../assets/css/styles.css --watch'
alias sassb='sass styles.scss:../../assets/css/styles.css --style=compressed'

# Page-specific Sass compilation
sassp() {
  if [[ -z "$1" ]]; then
    echo "Usage: sassp <filename> [build]"
    return 1
  fi
  if [[ "$2" == "build" ]]; then
    sass pages/${1}.scss:../../assets/css/pages/${1}.css --style=compressed
  else
    sass pages/${1}.scss:../../assets/css/pages/${1}.css --watch
  fi
}

CSS Enqueueing

File: /inc/functions/css_pagetype.php

<?php
// Global styles
if ( !function_exists ( "custom_theme_css_global" ) ) :
function custom_theme_css_global() {
    $theme_version = wp_get_theme()->get( "Version" );
    wp_enqueue_style( 
        "custom-theme-global", 
        get_template_directory_uri() . "/assets/css/styles.css", 
        array(), 
        $theme_version 
    );
}
add_action( "wp_enqueue_scripts", "custom_theme_css_global", 10 );
endif;

// Page-specific styles
if ( !function_exists ( "custom_theme_css_by_page_type" ) ) :
function custom_theme_css_by_page_type() {
    $theme_version = wp_get_theme()->get( "Version" );
    
    if ( is_front_page() || is_page('front-page') ) {
        wp_enqueue_style( 
            "custom-theme-front", 
            get_template_directory_uri() . "/assets/css/pages/front.css", 
            array(), 
            $theme_version 
        );
    }
}
add_action( "wp_enqueue_scripts", "custom_theme_css_by_page_type", 20 );
endif;

Key Points:

  • Use theme version for cache busting
  • Consistent handle naming (custom-theme-*)
  • Page-specific CSS loaded conditionally
  • Priority ordering (global at 10, specific at 20)

JavaScript Enqueueing

File: /inc/functions/js_scripts.php

<?php
if ( !function_exists ( "custom_theme_js_global" ) ) :
function custom_theme_js_global() {
    $theme_version = wp_get_theme()->get( 'Version' );
    
    // Main scripts: load in footer
    // wp_enqueue_script( 
    //     'theme-main', 
    //     get_template_directory_uri() . '/assets/js/app.js', 
    //     array(), 
    //     $theme_version, 
    //     true 
    // );
}
add_action('wp_enqueue_scripts', 'custom_theme_js_global');
endif;

Key Points:

  • Always pass array for dependencies (even if empty)
  • Always pass version for cache busting
  • Use true for footer loading (better performance)

Template Structure

index.php Pattern

<?php get_header(); ?>

<?php 
$page_class = is_front_page() ? 'front' : 'notfront';
?>

<main id="site-content" role="main" class="<?php echo esc_attr($page_class); ?>">
    <?php 
    if ( is_singular() ) {
        if ( have_posts() ) {
            while ( have_posts() ) {
                the_post();
                get_template_part( 'template-parts/content', get_post_type() );
            }
        }
    }
    ?>
</main>

<?php get_footer(); ?>

header.php Pattern

<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
    <meta charset="<?php bloginfo( 'charset' ); ?>">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link rel="profile" href="https://gmpg.org/xfn/11">
    
    <?php wp_head(); ?>
</head>

<body <?php body_class(); ?>>
<?php wp_body_open(); ?>

<?php get_template_part( 'template-parts/header-markup' ); ?>

footer.php Pattern

<?php get_template_part( 'template-parts/footer-markup' ); ?>

<?php wp_footer(); ?>
</body>
</html>

WordPress VIP Compliance

Always Escape Output

// Text content
echo esc_html( $text );

// HTML attributes
echo esc_attr( $class );

// URLs
echo esc_url( $url );

// Translation functions with escaping
esc_html__( 'Text', CUSTOM_THEME_TEXT_DOMAIN )
esc_attr__( 'Text', CUSTOM_THEME_TEXT_DOMAIN )
esc_html_e( 'Text', CUSTOM_THEME_TEXT_DOMAIN )

Always Sanitize Input

// Text fields
$value = sanitize_text_field( $_POST['field'] );

// URLs
$url = esc_url_raw( $_POST['url'] );

// Integers
$id = absint( $_POST['id'] );

Proper File Paths

// CORRECT: Use WordPress functions
get_template_directory()        // /path/to/theme
get_template_directory_uri()    // https://site.com/wp-content/themes/theme

// WRONG: Never hardcode paths

Text Domain Best Practices

// Define constant in functions.php
define( 'CUSTOM_THEME_TEXT_DOMAIN', 'custom-theme' );

// Use throughout theme
__( 'Read More', CUSTOM_THEME_TEXT_DOMAIN )
the_content( __( 'Continue reading', CUSTOM_THEME_TEXT_DOMAIN ) );

Media Support

File: /inc/functions/theme_media.php

<?php

// Post thumbnail support
add_theme_support( 'post-thumbnails' );

// Custom image sizes
add_image_size( 'hero-image', 1920, 1080, true );

// Add custom sizes to media library dropdown
if ( !function_exists( "custom_image_sizes" ) ) :
function custom_image_sizes( $sizes ) {
    return array_merge( $sizes, array(
        'hero-image' => __( 'Hero Image', CUSTOM_THEME_TEXT_DOMAIN ),
    ));
}
endif;
add_filter( 'image_size_names_choose', 'custom_image_sizes' );

Template Parts Pattern

content-post.php

<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
    <header class="entry-header">
        <?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
    </header>

    <div class="entry-content">
        <?php the_content( __( 'Continue reading', CUSTOM_THEME_TEXT_DOMAIN ) ); ?>
    </div>

    <footer class="entry-footer">
        <?php the_date(); ?>
        <?php the_author(); ?>
    </footer>
</article>

VIP Compliance Checklist

Before deploying:

  • All output is escaped (esc_html(), esc_attr(), esc_url())
  • All input is sanitized
  • Scripts/styles properly enqueued with versions
  • Text domain constant defined and used throughout
  • No flush_rewrite_rules() in code
  • File paths use WordPress functions
  • No hardcoded URLs or paths
  • Template parts used for modular structure
  • Theme versioning for cache busting

Quick Reference

Theme Support Features

add_theme_support( 'title-tag' );
add_theme_support( 'post-thumbnails' );
add_theme_support( 'responsive-embeds' );
add_theme_support( 'html5', array( 'search-form', 'comment-form' ) );

Clean Up WordPress Head

remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

web-security

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

python-expert

No summary provided by upstream source.

Repository SourceNeeds Review
General

plotly-dash-expert

No summary provided by upstream source.

Repository SourceNeeds Review
General

css-specialist

No summary provided by upstream source.

Repository SourceNeeds Review