Skip to main content

Course Progress

Loading...

📦 Packaging Themes for Distribution

Prepare your WordPress theme for release

Learn file preparation, licensing, and creating distribution packages

Learning Objectives

  • Prepare theme files for distribution
  • Choose and implement licensing
  • Create theme screenshots and assets
  • Build distribution packages
  • Version your theme properly
  • Include necessary documentation
  • Optimize file sizes
  • Automate packaging process

Preparing for Distribution

Proper packaging ensures your theme is easy to install, professional, and meets distribution requirements for various platforms.

Distribution Preparation Steps

  • Clean Code: Remove development files and debug code
  • Optimize Assets: Compress images and minify CSS/JS
  • Complete Documentation: Include all necessary docs
  • License Properly: Clear licensing terms
  • Test Thoroughly: Ensure everything works
  • Version Correctly: Follow semantic versioning

Theme File Structure

theme-name/ # Distribution package
├── assets/
│   ├── css/
│   │   ├── style.min.css # Minified production CSS
│   │   └── admin.min.css
│   ├── js/
│   │   ├── theme.min.js # Minified production JS
│   │   └── customizer.min.js
│   ├── images/
│   │   └── *.{jpg,png,svg} # Optimized images
│   └── fonts/
│       └── *.{woff,woff2}
├── inc/
│   ├── customizer.php
│   ├── template-functions.php
│   └── template-tags.php
├── template-parts/
│   ├── content/
│   ├── header/
│   └── footer/
├── languages/
│   ├── theme-name.pot # Translation template
│   └── readme.txt
├── page-templates/
│   └── *.php
├── style.css # Theme header information
├── functions.php
├── index.php
├── screenshot.png # 1200x900px
├── README.md
├── readme.txt # WordPress.org format
├── LICENSE
├── CHANGELOG.md
└── *.php # Template files

# Files to EXCLUDE from distribution:
node_modules/
src/ # Source files
.git/
.gitignore
package.json
package-lock.json
composer.json
composer.lock
webpack.config.js
gulpfile.js
.eslintrc
.stylelintrc
phpcs.xml
*.map # Source maps
*.log
.DS_Store
Thumbs.db

Packaging Process

1 Clean Files

  • Remove debug code
  • Delete test files
  • Clean comments
  • Remove console.log

2 Optimize Assets

  • Minify CSS/JS
  • Compress images
  • Optimize fonts
  • Remove unused assets

3 Update Version

  • Update style.css
  • Update README
  • Update changelog
  • Tag in Git

4 Add Documentation

  • README files
  • License file
  • Changelog
  • Installation guide

5 Create Screenshot

  • 1200x900px size
  • PNG format
  • Show key features
  • Professional quality

6 Build Package

  • Create ZIP file
  • Verify contents
  • Test installation
  • Check file size

Theme Header Information

style.css Header

/**
 * Theme Name:        My Awesome Theme
 * Theme URI:         https://example.com/themes/my-awesome-theme
 * Author:            John Doe
 * Author URI:        https://example.com
 * Description:       A modern, responsive WordPress theme with extensive customization options, 
 *                    perfect for blogs, portfolios, and business websites. Features include 
 *                    custom headers, multiple layout options, and full Gutenberg support.
 * Version:           2.1.0
 * Requires at least: 5.9
 * Tested up to:      6.4
 * Requires PHP:      7.4
 * License:           GPL v2 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       my-awesome-theme
 * Domain Path:       /languages
 * Tags:              two-columns, three-columns, left-sidebar, right-sidebar, 
 *                    flexible-header, custom-background, custom-colors, custom-header, 
 *                    custom-menu, custom-logo, editor-style, featured-images, 
 *                    footer-widgets, full-width-template, rtl-language-support, 
 *                    sticky-post, theme-options, threaded-comments, translation-ready, 
 *                    block-styles, wide-blocks, accessibility-ready
 * 
 * This theme, like WordPress, is licensed under the GPL.
 * Use it to make something cool, have fun, and share what you've learned with others.
 */

Theme Licensing

License Use Case Requirements WordPress.org Compatible
GPL v2+ WordPress.org, free themes Must remain GPL, include license ✅ Yes
GPL v3 Stronger copyleft More restrictions on usage ✅ Yes
MIT Permissive, commercial Include copyright notice ❌ No
Split License PHP=GPL, CSS/JS=proprietary Complex licensing terms ❌ No
Commercial Premium marketplaces Custom terms, usually GPL ❌ No

LICENSE File

GNU GENERAL PUBLIC LICENSE
Version 2, June 1991

Copyright (C) 2024 Your Name

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

[Full GPL v2 text...]

Automated Build Process

NPM Build Script

{
  "scripts": {
    "build": "npm run clean && npm run compile && npm run optimize",
    "clean": "rimraf dist/",
    "compile": "npm run compile:css && npm run compile:js",
    "compile:css": "sass src/scss:dist/css --style=compressed",
    "compile:js": "webpack --mode production",
    "optimize": "npm run optimize:images && npm run optimize:fonts",
    "optimize:images": "imagemin src/images/* --out-dir=dist/images",
    "optimize:fonts": "subset-font src/fonts/*.ttf --output dist/fonts/",
    "package": "npm run build && npm run create-package",
    "create-package": "node scripts/package.js",
    "version": "npm run update-version && git add -A",
    "update-version": "node scripts/update-version.js"
  }
}

Package Creation Script (scripts/package.js)

const fs = require('fs');
const path = require('path');
const archiver = require('archiver');

// Configuration
const themeName = 'my-awesome-theme';
const version = require('../package.json').version;
const outputDir = 'packages';
const outputFile = `${themeName}-${version}.zip`;

// Files to include
const includePatterns = [
    '**/*.php',
    '**/*.css',
    '**/*.js',
    '**/*.png',
    '**/*.jpg',
    '**/*.svg',
    '**/*.woff',
    '**/*.woff2',
    '**/*.pot',
    '**/*.mo',
    '**/*.po',
    'LICENSE',
    'README.md',
    'readme.txt',
    'screenshot.png',
    'CHANGELOG.md'
];

// Files to exclude
const excludePatterns = [
    'node_modules/**',
    'src/**',
    'scripts/**',
    'tests/**',
    '.git/**',
    '.github/**',
    '*.map',
    '*.log',
    '.DS_Store',
    'Thumbs.db',
    'package.json',
    'package-lock.json',
    'composer.json',
    'composer.lock',
    'webpack.config.js',
    'gulpfile.js',
    '.gitignore',
    '.eslintrc',
    '.stylelintrc',
    'phpcs.xml',
    '.env',
    '.editorconfig'
];

// Create output directory
if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir);
}

// Create archive
const output = fs.createWriteStream(path.join(outputDir, outputFile));
const archive = archiver('zip', {
    zlib: { level: 9 } // Maximum compression
});

// Event handlers
output.on('close', () => {
    const size = (archive.pointer() / 1024 / 1024).toFixed(2);
    console.log(`✅ Package created: ${outputFile} (${size} MB)`);
    console.log(`📦 Total files: ${archive.pointer()} bytes`);
});

archive.on('warning', (err) => {
    if (err.code === 'ENOENT') {
        console.warn('⚠️ Warning:', err);
    } else {
        throw err;
    }
});

archive.on('error', (err) => {
    throw err;
});

// Pipe archive to output
archive.pipe(output);

// Add files to archive
archive.glob('**/*', {
    cwd: '.',
    ignore: excludePatterns
});

// Add theme folder structure
archive.directory('./', themeName);

// Finalize archive
archive.finalize();

Version Update Script

const fs = require('fs');
const path = require('path');

// Get new version from package.json
const packageJson = require('../package.json');
const newVersion = packageJson.version;

// Update style.css
const stylePath = path.join(__dirname, '..', 'style.css');
let styleContent = fs.readFileSync(stylePath, 'utf8');
styleContent = styleContent.replace(
    /Version:\s*[\d.]+/,
    `Version:           ${newVersion}`
);
fs.writeFileSync(stylePath, styleContent);

// Update README.md
const readmePath = path.join(__dirname, '..', 'README.md');
let readmeContent = fs.readFileSync(readmePath, 'utf8');
readmeContent = readmeContent.replace(
    /Version:\s*[\d.]+/g,
    `Version: ${newVersion}`
);
fs.writeFileSync(readmePath, readmeContent);

// Update functions.php (if version constant exists)
const functionsPath = path.join(__dirname, '..', 'functions.php');
if (fs.existsSync(functionsPath)) {
    let functionsContent = fs.readFileSync(functionsPath, 'utf8');
    functionsContent = functionsContent.replace(
        /define\(\s*'THEME_VERSION',\s*'[\d.]+'\s*\)/,
        `define( 'THEME_VERSION', '${newVersion}' )`
    );
    fs.writeFileSync(functionsPath, functionsContent);
}

console.log(`✅ Version updated to ${newVersion}`);

GitHub Release Automation

.github/workflows/release.yml

name: Create Release

on:
  push:
    tags:
      - 'v*'

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        
    - name: Install dependencies
      run: npm ci
      
    - name: Build theme
      run: npm run build
      
    - name: Create theme package
      run: |
        mkdir -p release
        rsync -av --exclude-from='.distignore' . release/theme-name/
        cd release
        zip -r theme-name.zip theme-name/
        
    - name: Create changelog
      id: changelog
      run: |
        echo "CHANGELOG<> $GITHUB_ENV
        git log --pretty=format:"- %s" $(git describe --tags --abbrev=0 HEAD^)..HEAD >> $GITHUB_ENV
        echo "EOF" >> $GITHUB_ENV
        
    - name: Create Release
      uses: softprops/action-gh-release@v1
      with:
        files: release/theme-name.zip
        body: |
          ## Changes in this Release
          ${{ env.CHANGELOG }}
          
          ## Installation
          1. Download theme-name.zip
          2. Go to WordPress Admin → Appearance → Themes
          3. Click "Add New" → "Upload Theme"
          4. Choose the downloaded file and install
          
          ## Documentation
          See [README.md](README.md) for detailed documentation.
        draft: false
        prerelease: false
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Pre-Distribution Checklist

  • All features tested and working
  • Code validated (Theme Check plugin)
  • No PHP errors or warnings
  • No JavaScript console errors
  • CSS/JS minified for production
  • Images optimized
  • Debug code removed
  • Version number updated
  • Changelog updated
  • Screenshot.png included (1200x900)
  • README files complete
  • License file included
  • Translation files (.pot) included
  • Demo content prepared
  • Documentation complete
  • Support information added
  • Development files excluded
  • Package size optimized
  • Installation tested
  • Backup created

File Size Optimization

Image Optimization

# Using ImageOptim (Mac)
imageoptim --directory ./images

# Using jpegoptim (Linux)
jpegoptim --strip-all --all-progressive *.jpg

# Using pngquant
pngquant --quality=65-80 *.png

# Using svgo for SVG
svgo -f ./images/svg --multipass

# Batch optimization with imagemin
npx imagemin images/* --out-dir=dist/images \
  --plugin=mozjpeg \
  --plugin=pngquant \
  --plugin=svgo

CSS/JS Optimization

# CSS minification with cssnano
npx postcss style.css --use cssnano --output style.min.css

# JavaScript minification with terser
npx terser script.js --compress --mangle --output script.min.js

# Combined optimization
npm run build:css && npm run build:js

Best Practices

Distribution Best Practices

  • Test installation: Always test the package on a clean WordPress install
  • Version consistently: Use semantic versioning (MAJOR.MINOR.PATCH)
  • Include documentation: Never skip documentation
  • Optimize file size: Smaller packages download faster
  • Clean code: Remove all development artifacts
  • Proper licensing: Be clear about usage rights
  • Professional presentation: Quality screenshot and description
  • Support information: Make it easy to get help
Never include sensitive information like API keys, database credentials, or personal data in your distribution package.
Create a .distignore file (similar to .gitignore) to list all files that should be excluded from the distribution package.

Practice Exercise

💻
Package Your Theme

Create a distribution-ready theme package:

  1. Clean all development files
  2. Minify CSS and JavaScript
  3. Optimize all images
  4. Update version numbers
  5. Create screenshot.png
  6. Write complete documentation
  7. Add license file
  8. Create .distignore file
  9. Build automated packaging script
  10. Create distribution ZIP file

Additional Resources