kodansha/wack-foundation

The base (parent) theme for WACK stack based WordPress projects.

Installs: 2

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Type:wordpress-theme

pkg:composer/kodansha/wack-foundation

v0.1.1 2025-11-13 05:20 UTC

This package is auto-updated.

Last update: 2025-11-13 05:21:29 UTC


README

Overview

WACK Foundation is a battle-tested parent theme designed specifically for headless WordPress deployments. It provides a curated set of features that disable unnecessary functionality by default while offering granular control through WordPress filters to re-enable features as needed.

By using this as a parent theme, you gain:

  • Security hardening out of the box (XML-RPC disabled, REST API access control, etc.)
  • Block editor optimization for headless workflows (reduced block types, disabled Quick Edit, etc.)
  • Filter-based extensibility for all features
  • Zero configuration required for sensible defaults

Purpose & Design Philosophy

This theme is built for headless WordPress setups where:

  • Content is managed in WordPress but rendered on a separate frontend
  • The block editor (Gutenberg) is used for structured content authoring
  • Security and performance are prioritized over convenience features
  • Child themes extend functionality through filters rather than overriding templates

Key Principles

  1. Deny by default, allow by exception: Features are disabled unless explicitly enabled
  2. Filter-driven configuration: All behavior is customizable via WordPress filters
  3. Minimal footprint: No bloat, no legacy compatibility, no frontend assets
  4. Developer-friendly: Clear APIs, comprehensive documentation, predictable behavior

Installation

Installing via Composer

This theme is available only through Composer.

Installation steps:

  1. Require the theme package:
composer require kodansha/wack-foundation
  1. The theme will be installed to web/app/themes/wack-foundation (when using Bedrock) or wp-content/themes/wack-foundation (standard WordPress).

  2. Activate the theme or use it as a parent theme for your child theme.

Using as a Parent Theme

To use WACK Foundation as a parent theme, specify it in your child theme's style.css:

/*
Theme Name: Your Child Theme Name
Template: wack-foundation
Version: 1.0.0
*/

The Template field must match the directory name of the WACK Foundation theme.

Features

Appearance

Admin Favicon

Automatically sets custom favicons for the WordPress admin dashboard and login page by detecting favicon files in your theme's root directory.

Supported files (priority order):

  1. favicon.ico - ICO format (recommended for compatibility)
  2. favicon.png - PNG format (modern browsers)
  3. favicon.svg - SVG format (scalable vector graphics)

How it works:

  • Place a favicon file (favicon.ico, favicon.png, or favicon.svg) in your theme's root directory
  • The class automatically detects and outputs the appropriate <link> tag
  • MIME types are auto-detected from file extension
  • Child themes inherit and can override parent theme favicons

No configuration needed - just add the file to your theme directory.

Comment

Comment Disabler

Completely disables comments and trackbacks functionality for headless WordPress.

What it disables:

  • Comment support for all public post types
  • Comment-related admin UI (menus, dashboard modules, profile shortcuts)
  • REST API comment endpoints (/wp/v2/comments, etc.)
  • XML-RPC comment methods (wp.newComment, wp.editComment, etc.)
  • Comment widgets and admin bar items

What it preserves:

  • Logged-in users with edit_posts capability retain full access (for Gutenberg)
  • Frontend template rendering (this is admin/API-only)

No filters available - comments are always disabled by this theme. This feature cannot be turned off.

Dashboard

Dashboard Disabler

Disables the WordPress admin dashboard (index.php) and redirects users to a more useful admin page.

Default behavior:

  • Redirects all users to the posts list (edit.php)
  • Removes the "Dashboard" menu item from the admin sidebar

Filters:

wack_dashboard_redirect_url

Customize the redirect destination URL.

<?php
// Redirect to pages list instead of posts
add_filter('wack_dashboard_redirect_url', fn() => 'edit.php?post_type=page');

// Redirect to a custom page
add_filter('wack_dashboard_redirect_url', fn() => 'admin.php?page=my-custom-page');

Parameters:

  • (none) - Returns a string URL path relative to admin_url()

Default: 'edit.php' (posts list)

wack_dashboard_allowed_capabilities

Allow specific user capabilities to access the dashboard.

<?php
// Allow administrators to see the dashboard
add_filter('wack_dashboard_allowed_capabilities', function($capabilities) {
    $capabilities[] = 'manage_options';
    return $capabilities;
});

// Allow editors and admins
add_filter('wack_dashboard_allowed_capabilities', function($capabilities) {
    $capabilities[] = 'manage_options';
    $capabilities[] = 'edit_others_posts';
    return $capabilities;
});

Parameters:

  • array $capabilities - Array of capability strings

Default: [] (no users can access dashboard)

Editor

Block Type Controller

Controls which Gutenberg blocks are available in the block editor. Uses a minimal whitelist approach.

Default allowed blocks:

  • core/heading
  • core/image
  • core/list
  • core/list-item
  • core/paragraph

All other blocks (including media, embeds, widgets, etc.) are disabled by default.

Filter:

wack_block_type_enabled_types

Extend the list of allowed block types.

<?php
// Add additional core blocks
add_filter('wack_block_type_enabled_types', fn($default_blocks) => array_merge($default_blocks, [
    'core/table',
    'core/video',
    'core/gallery',
    'core/quote',
    'core/code',
]));

// Replace entirely (not recommended)
add_filter('wack_block_type_enabled_types', fn() => [
    'core/paragraph',
    'core/heading',
    'my-custom/block',
]);

Parameters:

  • array $default_blocks - Array of default block type names

Default: BlockType::DEFAULT_ALLOWED_BLOCK_TYPES

Block type reference: Primary (PHP – suitable for CLI, diagnostics, CI):

$registry = \WP_Block_Type_Registry::get_instance();
$all_blocks = $registry->get_all_registered(); // array keyed by block name
foreach ($all_blocks as $name => $block) {
    // $name example: 'core/paragraph'
    // Access meta: $block->title, $block->category, $block->supports
}

Secondary (browser console quick lookup): wp.blocks.getBlockTypes() Reference docs: https://developer.wordpress.org/block-editor/reference-guides/core-blocks/

Block Style Manager

Controls which block styles are available for core and custom blocks. Disables non-default styles by default.

Default behavior:

  • Only "default" block styles are enabled
  • Non-default styles (like "outline" button, "fill" quote, etc.) are disabled

Filter:

wack_block_style_enabled_styles

Specify which block styles should be available.

<?php
// Enable specific non-default styles
add_filter('wack_block_style_enabled_styles', fn($styles) => [
    'core/button:outline',
    'core/quote:fancy-quote',
    'core/separator:wide',
    'core/separator:dots',
]);

Parameters:

  • array $styles - Array of block style identifiers in 'blockName:styleName' format

Default: [] (all non-default styles disabled, only default styles available)

Block style reference: Default styles (like core/button:fill, core/image:default) are always available and cannot be disabled.

Primary (PHP – enumerating styles):

$registry = \WP_Block_Type_Registry::get_instance();
$styles = [];
foreach ($registry->get_all_registered() as $block) {
    if (!empty($block->styles)) {
        $styles[$block->name] = $block->styles; // each style array has keys like 'name', 'label', 'isDefault'
    }
}
// $styles maps block name => array of style meta arrays

Secondary (browser console quick lookup): wp.blocks.getBlockTypes().filter(b => b.styles?.length).map(b => ({block: b.name, styles: b.styles}))

Format Controller

Controls which text formatting options are available in the Rich Text toolbar (bold, italic, link, etc.).

Default behavior:

  • All text formats are disabled by default (empty array)
  • Use the filter to enable specific formats

Filter:

wack_text_format_enabled_types

Specify which text formats should be enabled.

<?php
// Enable basic formatting
add_filter('wack_text_format_enabled_types', fn() => [
    'core/bold',
    'core/italic',
    'core/link',
]);

// Enable extended formatting
add_filter('wack_text_format_enabled_types', fn() => [
    'core/bold',
    'core/code',
    'core/italic',
    'core/link',
    'core/strikethrough',
    'core/underline',
]);

// Enable all available formats
add_filter('wack_text_format_enabled_types', fn() => [
    'core/bold',
    'core/code',
    'core/italic',
    'core/link',
    'core/strikethrough',
    'core/subscript',
    'core/superscript',
    'core/text-color',
    'core/underline',
]);

Parameters:

  • array $formats - Array of format type identifiers

Default: [] (all formats disabled)

Format type reference:

Embed Block Variation Manager

Controls which embed block variations (YouTube, Twitter, Vimeo, etc.) are available in the block editor.

Default behavior:

  • All embed variations are disabled by default (empty array)

Filter:

wack_embed_block_enabled_variations

Specify which embed providers should be available.

<?php
// Enable YouTube and Vimeo embeds only
add_filter('wack_embed_block_enabled_variations', fn() => [
    'youtube',
    'vimeo',
]);

// Enable social media embeds
add_filter('wack_embed_block_enabled_variations', fn() => [
    'twitter',
    'facebook',
    'instagram',
]);

Parameters:

  • array $variations - Array of embed variation slugs

Default: [] (all embeds disabled)

Embed variation reference:

  • Common providers: youtube, vimeo, twitter, facebook, instagram, spotify, etc.
  • Full list available in WordPress core source

Content Editor Disabler

Hides the content editor (title and content fields) for specific post types where structured content is managed entirely through ACF or custom fields.

Default behavior:

  • No post types have the editor disabled (empty array)

Why not remove 'editor' support? While you can disable the content editor by removing 'editor' from a post type's supports array, this prevents the Gutenberg UI from loading entirely. This class allows you to hide the content editor while maintaining the block editor interface, ensuring a consistent UI across all post types.

Filter:

wack_content_editor_disabled_post_types

Specify which post types should have their content editor hidden.

<?php
// Hide content editor for 'author' and 'product' post types
add_filter('wack_content_editor_disabled_post_types', fn() => [
    'author',
    'product',
]);

// Hide for a single post type
add_filter('wack_content_editor_disabled_post_types', fn() => ['landing-page']);

Parameters:

  • array $post_types - Array of post type slugs

Default: [] (no post types affected)

How it works:

  • Loads a CSS file that hides the editor interface
  • Only loads on the specified post types
  • Content is still stored in the database; only the UI is hidden
  • Preserves the Gutenberg block editor UI for consistency

Quick Edit Disabler

Disables the "Quick Edit" inline editing functionality in WordPress admin post lists.

Default behavior:

  • Quick Edit is disabled for all post types by default

Filter:

wack_quick_edit_enabled_post_types

Specify which post types should have Quick Edit enabled.

<?php
// Enable Quick Edit for posts and pages
add_filter('wack_quick_edit_enabled_post_types', fn() => [
    'post',
    'page',
]);

// Enable for custom post type
add_filter('wack_quick_edit_enabled_post_types', fn($types) => array_merge(
    $types,
    ['product', 'event']
));

Parameters:

  • array $post_types - Array of post type slugs where Quick Edit should be enabled

Default: [] (Quick Edit disabled for all post types)

Why disable Quick Edit?

  • Prevents inconsistent data when using structured content (ACF, custom fields)
  • Forces editors to use the full editor where validation rules apply
  • Reduces accidental edits in headless workflows

Media

Image Size Control

Controls which WordPress image sizes are automatically generated when images are uploaded. Uses a whitelist approach.

Default behavior:

  • All WordPress default image sizes are disabled (thumbnail, medium, large, etc.)
  • All auto-generated sizes are disabled
  • Big image auto-resize threshold is disabled

Filter:

wack_image_size_control_custom_sizes

Define custom image sizes to generate.

<?php
// Define custom image sizes
add_filter('wack_image_size_control_custom_sizes', fn() => [
    // Format: 'size-name' => [width, height, crop]
    // All parameters are optional except width

    // Fixed size with crop
    'card-thumbnail' => [400, 300, true],

    // Fixed size without crop (default)
    'hero-banner' => [1200, 600],

    // Width only (height auto-calculated)
    'content-width' => [800],

    // Width and height, no crop
    'gallery-large' => [1024, 768, false],

    // Width only with soft crop
    'post-thumbnail' => [600, 0, false],
]);

Parameters:

  • array $sizes - Associative array mapping size names to [width, height, crop] arrays
    • width (int, required): Image width in pixels
    • height (int, optional): Image height in pixels (0 = auto)
    • crop (bool, optional): Whether to crop to exact dimensions (default: false)

Default: [] (no sizes generated)

Size format:

  • Array values: [width], [width, height], or [width, height, crop]
  • Crop can be true (center crop) or false (proportional resize)
  • Height of 0 means auto-calculate based on aspect ratio

What's disabled:

  • WordPress default sizes: thumbnail, medium, medium_large, large
  • Theme-defined sizes via add_image_size()
  • Big image threshold (no auto-resize of large originals)

Media Filename Normalizer

Automatically generates clean, unique filenames for uploaded media files using UUIDv7.

Default behavior:

  • Original filenames are replaced with UUIDv7 identifiers
  • File extensions are preserved
  • Filenames are URL-safe and collision-resistant

Example:

  • my photo (1).jpg01933b6e-8f12-7890-abcd-ef1234567890.jpg
  • スクリーンショット 2024.png01933b6e-9a3c-7def-1234-567890abcdef.png

Filter:

media_filename_generator

Provide a custom filename generation function.

<?php
// Use timestamp with random string
add_filter('media_filename_generator', fn($default, $original, $ext) =>
    date('Y-m-d-His') . '-' . wp_generate_password(8, false), 10, 3);

// Use sanitized original filename (not recommended for security)
add_filter('media_filename_generator', fn($default, $original, $ext) =>
    sanitize_file_name($original), 10, 3);

// Custom format with prefix
add_filter('media_filename_generator', fn($default, $original, $ext) =>
    'media-' . uniqid() . '-' . time(), 10, 3);

Parameters:

  • string $default_filename - The default UUIDv7-based filename (without extension)
  • string $original_filename - The original uploaded filename (without extension)
  • string $extension - The file extension (e.g., 'jpg', 'png')

Returns: string - New filename (without extension)

Default: UUIDv7 identifier

Why UUIDv7?

  • Time-ordered for better database indexing
  • Globally unique (collision-resistant)
  • URL-safe characters only
  • No personal information leakage
  • Consistent length and format

Security

REST API Controller

Controls access to WordPress REST API endpoints using namespace whitelisting and route blacklisting.

Default behavior:

  • All REST API access is denied by default (empty whitelist)
  • Logged-in users with edit_posts capability have full access (required for Gutenberg)

Security model:

  1. Check user capability → Allow if edit_posts (admin/editor users bypass all restrictions)
  2. Check route blacklist → Deny if route is explicitly forbidden
  3. Check namespace whitelist → Allow if namespace is whitelisted
  4. Default deny → Reject all other requests

Filters:

wack_rest_api_namespace_whitelist

Add namespaces that should be publicly accessible.

<?php
// Allow WordPress core API for public posts/pages
add_filter('wack_rest_api_namespace_whitelist', function($namespaces) {
    $namespaces[] = 'wp/v2';
    return $namespaces;
});

// Allow custom plugin API
add_filter('wack_rest_api_namespace_whitelist', function($namespaces) {
    $namespaces[] = 'my-plugin/v1';
    $namespaces[] = 'wc/v3'; // WooCommerce
    return $namespaces;
});

Parameters:

  • array $namespaces - Array of namespace prefixes (e.g., 'wp/v2', 'my-plugin/v1')

Default: [] (no public access)

How namespaces work:

  • Namespaces are matched by prefix: 'wp/v2' matches /wp/v2/posts, /wp/v2/pages, etc.
  • Leading slash is optional: 'wp/v2' and '/wp/v2' are equivalent
wack_rest_api_forbidden_routes

Block specific routes even if their namespace is whitelisted.

<?php
// Block user enumeration
add_filter('wack_rest_api_forbidden_routes', function($routes) {
    $routes[] = '/wp/v2/users';
    return $routes;
});

// Block multiple sensitive endpoints
add_filter('wack_rest_api_forbidden_routes', function($routes) {
    return array_merge($routes, [
        '/wp/v2/users',
        '/wp/v2/plugins',
        '/wp/v2/themes',
        '/wp/v2/settings',
    ]);
});

Parameters:

  • array $routes - Array of route paths to block

Default: [] (no routes explicitly forbidden)

Recommended blacklist for security:

add_filter('wack_rest_api_forbidden_routes', fn($routes) => array_merge($routes, [
    '/wp/v2/users',               // User enumeration
    '/wp/v2/plugins',             // Plugin disclosure
    '/wp/v2/themes',              // Theme disclosure
    '/wp/v2/settings',            // Site settings
    '/wp/v2/comments',            // Comments (if disabled)
]));

Gutenberg compatibility:

  • Logged-in users with edit_posts capability bypass all restrictions
  • This ensures the block editor works without additional configuration
  • Public API access requires explicit whitelisting

XML-RPC Disabler

Completely disables XML-RPC functionality to prevent legacy API attacks.

What it disables:

  • XML-RPC API endpoint (xmlrpc.php)
  • All XML-RPC methods
  • X-Pingback header
  • Pingback functionality

Security benefits:

  • Prevents brute force attacks via system.multicall
  • Blocks DDoS attacks via pingback
  • Reduces attack surface for headless WordPress

No filters available - XML-RPC is completely disabled. If you need XML-RPC for legacy integrations, do not instantiate this class.

Note: XML-RPC is a legacy API and is not needed for modern WordPress usage. The REST API and Application Passwords provide better alternatives.

PostType & Taxonomy Base Classes

BasePostType

Abstract base class for registering custom post types with sensible defaults for headless WordPress.

Features:

  • Simplified post type registration with minimal boilerplate
  • Headless-friendly defaults (no frontend templates)
  • Automatic REST API exposure
  • Support for Gutenberg editor
  • Automatic label generation with locale support (English/Japanese)

Basic Usage:

<?php
namespace MyTheme\PostTypes;

use WackFoundation\PostType\BasePostType;

class AuthorPostType extends BasePostType
{
    public static function postTypeName(): string
    {
        return 'author';
    }

    public static function postTypeLabel(): string
    {
        return '著者'; // Or 'Author' for English
    }

    public function __construct()
    {
        $this->menu_position = 21;
        $this->menu_icon = 'dashicons-admin-users';
        $this->extra_args = [
            'supports' => ['title', 'editor', 'thumbnail'],
        ];
    }
}

// Register the post type
new AuthorPostType()->register();

Label Generation: Labels are automatically generated based on the site's locale:

  • Japanese locale (ja*): Uses Japanese label templates (e.g., "新規追加", "編集")
  • Other locales: Uses English label templates (e.g., "Add New", "Edit")

The post type label from postTypeLabel() is automatically inserted into the templates.

Customizing Labels: Override the createLabels() method in child classes. Use buildLabelsFromTemplates() for partial customization:

<?php
protected function createLabels(): array
{
    $labels = $this->buildLabelsFromTemplates(static::postTypeLabel());
    $labels['add_new'] = 'Create New'; // Override specific label
    $labels['edit_item'] = 'Modify';
    return $labels;
}

Required Methods:

  • postTypeName(): Return the post type slug (e.g., 'product', 'author')
  • postTypeLabel(): Return the singular label (e.g., 'Product', '商品')
  • __construct(): Initialize properties (menu_position, menu_icon, extra_args, etc.)

Customizable Properties:

  • $menu_icon: Dashicon class or custom URL (default: null)
  • $menu_position: Admin menu position (default: 20)
  • $public: Public visibility (default: true)
  • $publicly_queryable: Publicly queryable (default: true)
  • $show_ui: Show admin UI (default: true)
  • $show_in_rest: REST API enabled (default: true)
  • $has_archive: Enable archive (default: true)
  • $extra_args: Additional register_post_type() arguments (default: [])
    • Common usage: supports, taxonomies, rewrite, capability_type, etc.

BaseTaxonomy

Abstract base class for registering custom taxonomies with sensible defaults for headless WordPress.

Features:

  • Simplified taxonomy registration with minimal boilerplate
  • Headless-friendly defaults
  • REST API exposure enabled by default
  • Hierarchical (category-style) by default
  • Automatic label generation with locale support (English/Japanese)

Basic Usage:

<?php
namespace MyTheme\Taxonomies;

use WackFoundation\Taxonomy\BaseTaxonomy;

class GenreTaxonomy extends BaseTaxonomy
{
    public static function taxonomyKey(): string
    {
        return 'genre';
    }

    public static function taxonomyLabel(): string
    {
        return 'ジャンル'; // Or 'Genre' for English
    }

    public function __construct()
    {
        $this->extra_args = [
            'rewrite' => ['slug' => 'genres'],
            'show_in_nav_menus' => true,
        ];
    }
}

// Register the taxonomy for specific post types
new GenreTaxonomy()->register(['post', 'article']);

Non-hierarchical (tag-style) taxonomy:

<?php
class TagTaxonomy extends BaseTaxonomy
{
    public static function taxonomyKey(): string
    {
        return 'custom_tag';
    }

    public static function taxonomyLabel(): string
    {
        return 'カスタムタグ';
    }

    public function __construct()
    {
        // Override to make it non-hierarchical (tag-style)
        $this->hierarchical = false;

        $this->extra_args = [
            'rewrite' => ['slug' => 'tags'],
            'show_tagcloud' => true,
        ];
    }
}

Label Generation: Labels are automatically generated based on the site's locale and taxonomy type:

  • Hierarchical taxonomies: Use category-style labels
    • Japanese: "カテゴリー一覧", "カテゴリーを追加", etc.
    • English: "All Categories", "Add Category", etc.
  • Non-hierarchical taxonomies: Use tag-style labels
    • Japanese: "すべてのタグ", "タグを追加", "人気のタグ", etc.
    • English: "All Tags", "Add Tag", "Popular Tags", etc.

The taxonomy label from taxonomyLabel() is automatically inserted into the templates.

Customizing Labels: Override the createLabels() method in child classes. Use buildLabelsFromTemplates() for partial customization:

<?php
protected function createLabels(): array
{
    $labels = $this->buildLabelsFromTemplates(static::taxonomyLabel());
    $labels['add_new_item'] = 'Create New'; // Override specific label
    $labels['search_items'] = 'Find';
    return $labels;
}

Required Methods:

  • taxonomyKey(): Return the taxonomy slug (e.g., 'genre', 'product_tag')
  • taxonomyLabel(): Return the singular label (e.g., 'Genre', 'ジャンル')
  • __construct(): Initialize properties (hierarchical, extra_args, etc.)

Customizable Properties:

  • $hierarchical: Whether hierarchical (category-style) or flat (tag-style) (default: true)
  • $show_in_rest: REST API enabled (default: true)
  • $extra_args: Additional register_taxonomy() arguments (default: [])
    • Common usage: rewrite, show_in_nav_menus, show_admin_column, show_tagcloud, etc.

Validation

BaseValidation

Abstract base class for implementing custom editor validation in Gutenberg.

Purpose: Enforce content quality rules at the editor level (e.g., require featured image, enforce title length, validate custom fields).

Features:

  • JavaScript-based validation in the block editor
  • Custom error messages
  • Pre-publish checks
  • Integration with Gutenberg publish panel

Usage:

<?php
namespace MyTheme\Validations;

use WackFoundation\Validation\BaseValidation;

class PostValidation extends BaseValidation
{
    protected string $handle = 'post-validation';
    protected string $script_file = 'validation.js';

    protected array $target_post_types = [
        'post',
        'article',
    ];

    protected function getScriptData(): array
    {
        return [
            'requiredFields' => ['title', 'excerpt', 'featured_image'],
            'minTitleLength' => 10,
            'maxTitleLength' => 60,
        ];
    }
}

// Register validation
new PostValidation();

JavaScript validation example:

// validation.js
import { lockEditor } from '/app/themes/wack-foundation/src/Validation/assets/validation-lock-utility.js'; // Change path as needed

/**
 * Validate post title length
 */
const validateTitle = () => {
    const title = wp.data.select('core/editor').getEditedPostAttribute('title') || '';
    const config = validationConfig; // Passed from PHP via wp_localize_script

    lockEditor(
        title.length < config.minTitleLength,
        'title-length-lock',
        `Title must be at least ${config.minTitleLength} characters`
    );
};

/**
 * Validate featured image
 */
const validateFeaturedImage = () => {
    const featuredMedia = wp.data.select('core/editor').getEditedPostAttribute('featured_media');

    lockEditor(
        !featuredMedia,
        'featured-image-lock',
        'Featured image is required'
    );
};

/**
 * Run all validation checks
 */
const validate = () => {
    validateTitle();
    validateFeaturedImage();
};

/**
 * Initialize validation
 */
wp.domReady(() => {
    validate();
    wp.data.subscribe(validate);
});

Methods to override:

  • getTargetPostTypes(): Define which post types to validate
  • getScriptData(): Pass configuration to JavaScript
  • getScriptDependencies(): Define JavaScript dependencies

When to use:

  • Enforce required fields (featured image, excerpt, etc.)
  • Validate title/content length
  • Check custom field values
  • Prevent publishing of incomplete content

Killer Pads Users

If you previously relied on the Killer Pads project (https://github.com/kodansha/killer-pads), this parent theme fully replaces its intended optimization scope (dashboard reduction, editor hardening, security tightening). Do NOT install or activate Killer Pads when using WACK Foundation as a parent theme; running both would duplicate or conflict on the same WordPress hooks.

Not included here (you must implement separately if needed):

  • Post revision limiting or disabling
  • Autosave disabling

Those concerns are intentionally left out to avoid enforcing irreversible editorial constraints. Add them in a child theme or a small mu‑plugin if your workflow demands it.

Summary:

  • Killer Pads: not required
  • Revisions & autosave: unmanaged — handle manually if you need stricter control