Skip to main content

Course Progress

Loading...

📁 Asset Organization in Themes

Structure your theme assets for maintainability and performance

Learn best practices for organizing CSS, JavaScript, images, and other assets

Learning Objectives

  • Understand WordPress theme file structure
  • Organize CSS and JavaScript files effectively
  • Implement proper naming conventions
  • Structure assets for modularity
  • Manage vendor and third-party assets
  • Set up build tools and workflows
  • Organize media and font files
  • Create maintainable asset architecture

Why Asset Organization Matters

Proper asset organization is crucial for theme maintainability, performance, and collaboration. A well-structured asset architecture makes development faster, debugging easier, and enables efficient team collaboration.

💡
Key Concept
Good asset organization is like a well-organized toolbox - everything has its place, making it easy to find what you need and maintain your project over time.

WordPress Theme File Structure

Complete Theme Directory Structure

theme-name/
├── style.css // Theme information and base styles
├── functions.php // Theme functions and features
├── index.php // Main template file
├── screenshot.png // Theme preview (1200x900px)
│
├── assets/ // All theme assets
│   ├── css/ // Stylesheets
│   │   ├── main.css // Main compiled CSS
│   │   ├── admin.css // Admin styles
│   │   ├── editor-style.css // Editor styles
│   │   └── vendor/ // Third-party CSS
│   │       ├── bootstrap.min.css
│   │       └── font-awesome.min.css
│   │
│   ├── js/ // JavaScript files
│   │   ├── main.js // Main theme JS
│   │   ├── navigation.js // Navigation scripts
│   │   ├── customizer.js // Customizer preview
│   │   ├── admin.js // Admin scripts
│   │   └── vendor/ // Third-party JS
│   │       ├── jquery.min.js
│   │       └── slick.min.js
│   │
│   ├── images/ // Theme images
│   │   ├── logo.svg
│   │   ├── placeholder.jpg
│   │   └── icons/ // Icon files
│   │       └── sprite.svg
│   │
│   ├── fonts/ // Custom fonts
│   │   ├── custom-font.woff2
│   │   ├── custom-font.woff
│   │   └── custom-font.ttf
│   │
│   └── scss/ // Sass source files
│       ├── main.scss // Main Sass file
│       ├── abstracts/ // Variables, mixins, functions
│       │   ├── _variables.scss
│       │   ├── _mixins.scss
│       │   └── _functions.scss
│       ├── base/ // Reset, typography, base
│       │   ├── _reset.scss
│       │   ├── _typography.scss
│       │   └── _base.scss
│       ├── components/ // Component styles
│       │   ├── _buttons.scss
│       │   ├── _cards.scss
│       │   └── _forms.scss
│       ├── layout/ // Layout components
│       │   ├── _header.scss
│       │   ├── _footer.scss
│       │   ├── _sidebar.scss
│       │   └── _grid.scss
│       ├── pages/ // Page-specific styles
│       │   ├── _home.scss
│       │   ├── _about.scss
│       │   └── _contact.scss
│       └── vendor/ // Third-party Sass
│           └── _normalize.scss
│
├── inc/ // PHP includes
│   ├── enqueue.php // Asset enqueuing
│   ├── customizer.php // Customizer settings
│   ├── template-functions.php
│   └── template-tags.php
│
├── template-parts/ // Reusable template parts
│   ├── content/
│   ├── header/
│   └── footer/
│
├── templates/ // Page templates
│   ├── template-full-width.php
│   └── template-sidebar-left.php
│
├── languages/ // Translation files
│   └── theme-name.pot
│
├── src/ // Source files (development)
│   ├── js/ // ES6+ JavaScript modules
│   └── scss/ // Sass source files
│
├── dist/ // Compiled/built files
│   ├── css/
│   └── js/
│
├── node_modules/ // NPM packages (git-ignored)
├── package.json // NPM configuration
├── webpack.config.js // Webpack configuration
├── .gitignore // Git ignore file
└── README.md // Theme documentation

Core Organization Principles

1. Separation of Concerns

  • Keep different types of assets in separate directories
  • Separate source files from compiled files
  • Isolate vendor/third-party code
  • Keep development and production assets separate

2. Logical Grouping

  • Group related files together
  • Use subdirectories for better organization
  • Create component-based structures
  • Maintain consistent hierarchy

3. Scalability

  • Structure should accommodate growth
  • Easy to add new components
  • Modular architecture
  • Clear patterns for extension

CSS/Sass Organization

7-1 Pattern for Sass

A popular architecture pattern with 7 folders and 1 main file:

scss/
├── main.scss // Main file that imports all others
├── abstracts/
│   ├── _variables.scss // Sass variables
│   ├── _functions.scss // Sass functions
│   ├── _mixins.scss // Sass mixins
│   └── _placeholders.scss // Sass placeholders
├── base/
│   ├── _reset.scss // Reset/normalize
│   ├── _typography.scss // Typography rules
│   └── _base.scss // Base styles
├── components/
│   ├── _buttons.scss // Buttons
│   ├── _carousel.scss // Carousel
│   └── _dropdown.scss // Dropdown
├── layout/
│   ├── _navigation.scss // Navigation
│   ├── _grid.scss // Grid system
│   ├── _header.scss // Header
│   ├── _footer.scss // Footer
│   └── _sidebar.scss // Sidebar
├── pages/
│   ├── _home.scss // Home specific styles
│   ├── _contact.scss // Contact specific styles
│   └── _about.scss // About specific styles
├── themes/
│   ├── _theme.scss // Default theme
│   └── _admin.scss // Admin theme
└── vendors/
    ├── _bootstrap.scss // Bootstrap
    └── _jquery-ui.scss // jQuery UI

main.scss Import Structure

// main.scss
@charset "UTF-8";

// 1. Configuration and helpers
@import 'abstracts/variables';
@import 'abstracts/functions';
@import 'abstracts/mixins';
@import 'abstracts/placeholders';

// 2. Vendors
@import 'vendors/normalize';
@import 'vendors/bootstrap';

// 3. Base stuff
@import 'base/reset';
@import 'base/typography';
@import 'base/base';

// 4. Layout-related sections
@import 'layout/navigation';
@import 'layout/grid';
@import 'layout/header';
@import 'layout/footer';
@import 'layout/sidebar';
@import 'layout/forms';

// 5. Components
@import 'components/buttons';
@import 'components/carousel';
@import 'components/dropdown';
@import 'components/cards';

// 6. Page-specific styles
@import 'pages/home';
@import 'pages/about';
@import 'pages/contact';

// 7. Themes
@import 'themes/theme';
@import 'themes/admin';

JavaScript Organization

Modular JavaScript Structure

src/js/
├── main.js // Entry point
├── modules/
│   ├── navigation.js // Navigation module
│   ├── slider.js // Slider functionality
│   ├── forms.js // Form handling
│   └── ajax.js // AJAX operations
├── utils/
│   ├── helpers.js // Helper functions
│   ├── constants.js // Constants
│   └── api.js // API utilities
├── components/
│   ├── accordion.js // Accordion component
│   ├── modal.js // Modal component
│   └── tabs.js // Tabs component
└── vendor/
    └── third-party.js // Third-party scripts

ES6 Module Example

// src/js/modules/navigation.js
export class Navigation {
    constructor() {
        this.menu = document.querySelector('.main-navigation');
        this.toggleButton = document.querySelector('.menu-toggle');
        this.init();
    }

    init() {
        if (!this.menu || !this.toggleButton) return;
        
        this.toggleButton.addEventListener('click', () => {
            this.toggleMenu();
        });
        
        this.handleMobileMenu();
        this.handleStickyNav();
    }

    toggleMenu() {
        this.menu.classList.toggle('is-active');
        this.toggleButton.classList.toggle('is-active');
        
        const isExpanded = this.toggleButton.getAttribute('aria-expanded') === 'true';
        this.toggleButton.setAttribute('aria-expanded', !isExpanded);
    }

    handleMobileMenu() {
        // Mobile menu logic
    }

    handleStickyNav() {
        // Sticky navigation logic
    }
}

// src/js/main.js
import { Navigation } from './modules/navigation';
import { Slider } from './modules/slider';
import { Forms } from './modules/forms';

document.addEventListener('DOMContentLoaded', () => {
    // Initialize modules
    new Navigation();
    new Slider();
    new Forms();
});

File Naming Conventions

File Type Convention Examples
PHP Templates kebab-case single-post.php, page-about.php
CSS/Sass Files kebab-case with underscore prefix for partials main.css, _header.scss
JavaScript Files camelCase or kebab-case mainScript.js, form-handler.js
Images kebab-case, descriptive hero-background.jpg, logo-dark.svg
Vendor Files Keep original naming jquery.min.js, bootstrap.css
Build Files Include version or hash main.min.css, app.bundle.js

Proper Asset Enqueuing

inc/enqueue.php

<?php
/**
 * Enqueue scripts and styles
 */
function theme_name_scripts() {
    // Get theme version for cache busting
    $theme_version = wp_get_theme()->get('Version');
    
    // CSS
    // Vendor styles
    wp_enqueue_style(
        'bootstrap',
        get_template_directory_uri() . '/assets/css/vendor/bootstrap.min.css',
        array(),
        '5.1.3'
    );
    
    // Main theme style
    wp_enqueue_style(
        'theme-name-style',
        get_template_directory_uri() . '/assets/css/main.css',
        array('bootstrap'),
        $theme_version
    );
    
    // JavaScript
    // Deregister WordPress jQuery and use custom version if needed
    if (!is_admin()) {
        wp_deregister_script('jquery');
        wp_enqueue_script(
            'jquery',
            get_template_directory_uri() . '/assets/js/vendor/jquery-3.6.0.min.js',
            array(),
            '3.6.0',
            true
        );
    }
    
    // Vendor scripts
    wp_enqueue_script(
        'bootstrap',
        get_template_directory_uri() . '/assets/js/vendor/bootstrap.bundle.min.js',
        array('jquery'),
        '5.1.3',
        true
    );
    
    // Main theme script
    wp_enqueue_script(
        'theme-name-script',
        get_template_directory_uri() . '/assets/js/main.js',
        array('jquery', 'bootstrap'),
        $theme_version,
        true
    );
    
    // Localize script for AJAX
    wp_localize_script('theme-name-script', 'themeAjax', array(
        'ajaxurl' => admin_url('admin-ajax.php'),
        'nonce' => wp_create_nonce('theme-ajax-nonce')
    ));
    
    // Conditional scripts
    if (is_singular() && comments_open() && get_option('thread_comments')) {
        wp_enqueue_script('comment-reply');
    }
}
add_action('wp_enqueue_scripts', 'theme_name_scripts');

/**
 * Enqueue admin scripts and styles
 */
function theme_name_admin_scripts() {
    wp_enqueue_style(
        'theme-name-admin',
        get_template_directory_uri() . '/assets/css/admin.css',
        array(),
        wp_get_theme()->get('Version')
    );
    
    wp_enqueue_script(
        'theme-name-admin',
        get_template_directory_uri() . '/assets/js/admin.js',
        array('jquery'),
        wp_get_theme()->get('Version'),
        true
    );
}
add_action('admin_enqueue_scripts', 'theme_name_admin_scripts');

/**
 * Enqueue block editor assets
 */
function theme_name_block_editor_assets() {
    wp_enqueue_style(
        'theme-name-block-editor',
        get_template_directory_uri() . '/assets/css/editor-style.css',
        array(),
        wp_get_theme()->get('Version')
    );
}
add_action('enqueue_block_editor_assets', 'theme_name_block_editor_assets');

Build Tools and Development Workflow

Package.json Structure

{
  "name": "theme-name",
  "version": "1.0.0",
  "scripts": {
    "dev": "webpack --mode development --watch",
    "build": "webpack --mode production",
    "sass": "sass src/scss:assets/css --watch",
    "lint": "eslint src/js"
  },
  "devDependencies": {
    "webpack": "^5.0.0",
    "sass": "^1.0.0",
    "eslint": "^7.0.0"
  }
}

Webpack Configuration

module.exports = {
  entry: './src/js/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  }
};

Media and Font Organization

Image Organization

images/
├── icons/ // Icon files
│   ├── sprite.svg // SVG sprite
│   └── social/ // Social icons
├── backgrounds/ // Background images
│   ├── hero-home.jpg
│   └── pattern.svg
├── content/ // Content images
│   └── placeholder.jpg
└── ui/ // UI elements
    ├── logo.svg
    └── loading.gif

Font Organization

fonts/
├── custom-font/
│   ├── custom-font-regular.woff2
│   ├── custom-font-regular.woff
│   ├── custom-font-bold.woff2
│   └── custom-font-bold.woff
└── fonts.css // @font-face declarations

Best Practices

Asset Organization Best Practices

  • Consistent Structure: Maintain the same organization pattern throughout
  • Clear Naming: Use descriptive, consistent file names
  • Version Control: Track source files, ignore compiled/vendor files
  • Documentation: Document your structure in README.md
  • Separation: Keep development and production files separate
  • Modularity: Break large files into smaller, focused modules
  • Dependencies: Clearly manage and document dependencies
  • Performance: Organize for optimal loading and caching
Never edit vendor/third-party files directly. If customization is needed, override styles in your own files or create a fork of the library.

Practice Exercise

💻
Organize Your Theme Assets

Create a well-organized asset structure for your theme:

  1. Set up the complete directory structure
  2. Organize CSS using the 7-1 pattern
  3. Create modular JavaScript structure
  4. Set up asset enqueuing in functions.php
  5. Implement proper naming conventions
  6. Configure build tools (webpack/gulp)
  7. Organize images and fonts
  8. Create a .gitignore file
  9. Document structure in README.md
  10. Test asset loading and dependencies

Additional Resources