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
truefor 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' );
More from oiler/claude-skills
plotly-dash-expert
Expert guidance for building Plotly Dash web applications. Use when user asks to create dashboards, interactive tables, data browsers, Dash apps, or mentions "Dash", "plotly", "DataTable", "dash callbacks", "dcc", or "dash_table". Covers app structure, callbacks, layouts, DataTable with server-side paging/sorting/filtering, database integration (SQLite/Postgres), multi-page apps, and self-hosted deployment with gunicorn/nginx. Focused on open-source Dash (not Dash Enterprise).
15python-expert
Expert guidance for Python programming. Use when user asks to write Python code, create scripts, build web apps with Django/Flask/FastAPI, run one-off tools with uv, debug Python errors, optimize performance, or asks about Python best practices, packaging, testing, or async patterns. Covers modern Python 3.10+ idioms, uv-based scripting, Django, Flask, FastAPI, data science, testing, and production deployment.
8css-specialist
Expert CSS guidance for developers with strong fundamentals who need help with modern CSS features (2020-2025) and advanced animations/visual effects. Applies clean, semantic CSS patterns with minimal utility classes and shallow inheritance.
6web-security
Application-level security for web development. Use when writing, reviewing, or auditing code for security in WordPress, Laravel, Django, FastAPI, or Plotly Dash. Covers OWASP Top 10 2021, XSS prevention, SQL injection, CSRF, security headers (CSP, HSTS, CORS), session and cookie security, JWT handling, authentication, file uploads, API security, PII protection, and secure configuration. Trigger on "security review", "secure this", "harden", "vulnerability", "XSS", "injection", "CSRF", "CORS", "CSP", "security headers", "session security", "JWT", or "OWASP".
4wordpress-blocks
WordPress custom Gutenberg block development with server-side PHP rendering. Includes block registration patterns, media upload integration, multiple item blocks, proper escaping/sanitization, and editor UI best practices. Maintains separation of concerns where editors control content while developers control design.
4