BBC's guide to development
  • General

    • About
    • Tools
    • Git(hub)
    • Showpad
    • Hosting
    • Maintenance
    • Security
    • Go live checklist
  • Front-end development

    • Bundlers
    • CSS/SCSS
    • Javascript
    • Vue
    • PHP
    • Mails
    • Dev Faq
  • Functions
  • Mixins
  • General

    • OOP Structure
  • Component Classes

    • Accordion
    • App
    • Component
    • HighwayApp
    • Popup
    • PNG Sequencer
    • Tab
  • Manager Classes

    • BountListenerMgr
    • Cache
    • Configuration
    • InViewStateMgr
    • Instance Manager
    • Event dispatcher
  • Factories

    • SwiperFactory
  • PDF

    • AssetLoader
    • BasePdfDoc
    • TemplatePdfDoc
    • CustomPdfDoc
  • Utility functions

    • canvas
    • Connection Status
    • css
    • dev
    • placeholder
    • dom
    • fetch
    • json
    • object
    • scroll
    • scrollbar
    • spreadsheets
    • string
    • url
  • General

    • ComponentMgr
    • ThreeJsViewer
  • Components

    • ComponentMgr
    • GltfModel
    • Snappable
    • Socket
    • ThreeJsViewer
    • ThreeJsViewerCamera
  • Loaders

    • ConfigurationSerializer
    • GltfBlockParser
  • Utils

    • CanvasInputAdapter
    • CollisionManager
    • SocketGridExpander
    • blender
    • headless
  • General

    • Troubleshooting
    • Legacy
  • Components

    • AssetBar
    • ConfigGenerator
    • ShowpadApp
  • Managers

    • Assets
    • AppsDb
    • Config
  • Utils

    • Connection Status
    • general
    • showpad-interactive
    • showpad-upload
  • Components

    • Accordion
    • BackButton
    • Breadcrumb
    • ByltButton
    • Hamburger
    • Icon
    • Logo
    • Loader
    • Modal
    • Popup
    • Prompt
    • ProgressBar
    • TextLoader
  • Composables

    • useDebugMode
    • useConnectionStatus
  • Utils

    • dom
    • props
  • General

    • General
    • Tracking
  • Components

    • Accordion
    • ActionButton
    • AssetItem
    • AssetList
    • BackButton
    • ConfigGenButton
    • Logo
    • Media
    • Modal
    • Popup
    • Prompt
    • SPButton
    • SPRouterView
    • SPTrackedRouterLink
    • TextLoader
    • View
  • Composables

    • useConnectionStatus
  • Stores

    • useAppsDbStore
    • useBreadcrumbStore
    • useShowpadAPIStore
    • useShowpadSDKStore
    • useSpConfigStore
    • useSpStore
    • useSpTrackingStore
  • The New Kit

    • General
    • Installation & Usage
    • ACF Blocks
    • PHPCS
    • Functions
    • Vite
    • WP Config
    • Staging Deployment
  • Best Practices

    • Page Structure
    • Fonts/Typography
  • Todo
GitHub
  • General

    • About
    • Tools
    • Git(hub)
    • Showpad
    • Hosting
    • Maintenance
    • Security
    • Go live checklist
  • Front-end development

    • Bundlers
    • CSS/SCSS
    • Javascript
    • Vue
    • PHP
    • Mails
    • Dev Faq
  • Functions
  • Mixins
  • General

    • OOP Structure
  • Component Classes

    • Accordion
    • App
    • Component
    • HighwayApp
    • Popup
    • PNG Sequencer
    • Tab
  • Manager Classes

    • BountListenerMgr
    • Cache
    • Configuration
    • InViewStateMgr
    • Instance Manager
    • Event dispatcher
  • Factories

    • SwiperFactory
  • PDF

    • AssetLoader
    • BasePdfDoc
    • TemplatePdfDoc
    • CustomPdfDoc
  • Utility functions

    • canvas
    • Connection Status
    • css
    • dev
    • placeholder
    • dom
    • fetch
    • json
    • object
    • scroll
    • scrollbar
    • spreadsheets
    • string
    • url
  • General

    • ComponentMgr
    • ThreeJsViewer
  • Components

    • ComponentMgr
    • GltfModel
    • Snappable
    • Socket
    • ThreeJsViewer
    • ThreeJsViewerCamera
  • Loaders

    • ConfigurationSerializer
    • GltfBlockParser
  • Utils

    • CanvasInputAdapter
    • CollisionManager
    • SocketGridExpander
    • blender
    • headless
  • General

    • Troubleshooting
    • Legacy
  • Components

    • AssetBar
    • ConfigGenerator
    • ShowpadApp
  • Managers

    • Assets
    • AppsDb
    • Config
  • Utils

    • Connection Status
    • general
    • showpad-interactive
    • showpad-upload
  • Components

    • Accordion
    • BackButton
    • Breadcrumb
    • ByltButton
    • Hamburger
    • Icon
    • Logo
    • Loader
    • Modal
    • Popup
    • Prompt
    • ProgressBar
    • TextLoader
  • Composables

    • useDebugMode
    • useConnectionStatus
  • Utils

    • dom
    • props
  • General

    • General
    • Tracking
  • Components

    • Accordion
    • ActionButton
    • AssetItem
    • AssetList
    • BackButton
    • ConfigGenButton
    • Logo
    • Media
    • Modal
    • Popup
    • Prompt
    • SPButton
    • SPRouterView
    • SPTrackedRouterLink
    • TextLoader
    • View
  • Composables

    • useConnectionStatus
  • Stores

    • useAppsDbStore
    • useBreadcrumbStore
    • useShowpadAPIStore
    • useShowpadSDKStore
    • useSpConfigStore
    • useSpStore
    • useSpTrackingStore
  • The New Kit

    • General
    • Installation & Usage
    • ACF Blocks
    • PHPCS
    • Functions
    • Vite
    • WP Config
    • Staging Deployment
  • Best Practices

    • Page Structure
    • Fonts/Typography
  • Todo
GitHub
  • Functions

Functions

Theme Support

The theme_setup() function configures essential WordPress theme features and capabilities. This function runs on the after_setup_theme hook to ensure proper timing during WordPress initialization.

function theme_setup() {
    // Enables WordPress to manage the document title tag automatically
    // WordPress will handle <title> generation based on page context
    add_theme_support( 'title-tag' );
    
    // Enables featured image/post thumbnail functionality
    // Allows posts and pages to have featured images
    add_theme_support( 'post-thumbnails' );

    // Enables HTML5 markup for core WordPress features
    // Provides cleaner, semantic HTML output for various elements
    add_theme_support(
        'html5',
        array(
            'search-form',     // Search widget form
            'gallery',         // Image gallery shortcode
            'caption',         // Image captions
            'script',          // JavaScript tags
            'style',           // CSS style tags
        )
    );

    // Makes embeds (YouTube, Twitter, etc.) responsive automatically
    // Wraps oEmbeds in responsive containers
    add_theme_support( 'responsive-embeds' );
    
    // Enables custom editor stylesheets for the block editor
    // Allows styling the Gutenberg editor to match front-end appearance
    add_theme_support( 'editor-styles' );
    
    // Wide and full-width block alignments (currently disabled)
    // Would allow blocks to extend beyond content width
    // add_theme_support( 'align-wide' );
    
    // Default block styles from WordPress (currently disabled)
    // Would include WordPress's default block styling
    // add_theme_support( 'wp-block-styles' );
    
    // Enables custom spacing controls in the block editor
    // Allows users to adjust margins and padding on blocks
    add_theme_support( 'custom-spacing' );
    
    // Enables appearance tools in the site editor
    // Provides additional design options like colors, typography, etc.
    add_theme_support( 'appearance-tools' );

    // Internationalization support (currently disabled)
    // Would enable multi-language support with translation files
    // load_theme_textdomain( 'pocpoc', get_template_directory() . '/assets/languages' );
}
add_action( 'after_setup_theme', 'theme_setup' );

Vite Asset Enqueuing

Vite Asset Enqueuing Chart

/*------------------------------------*\
    Vite Asset Enqueuing (WP + Vite)
\*------------------------------------*/

// Reads the Vite `manifest.json` generated during `vite build`
// This manifest maps your source files to their processed, hashed filenames.
function get_vite_manifest() {
    $manifest_path = get_template_directory() . '/assets/dist/.vite/manifest.json';
    if ( ! file_exists( $manifest_path ) ) {
        return array(); // No manifest found → return empty array
    }
    $manifest_content = file_get_contents( $manifest_path );
    if ( false === $manifest_content ) {
        return array(); // Failed to read file → return empty
    }
    $manifest = json_decode( $manifest_content, true );
    if ( JSON_ERROR_NONE !== json_last_error() ) {
        return array(); // JSON parse error → return empty
    }
    return $manifest ?: array();
}

function theme_scripts() {
    // Avoid enqueuing scripts in the WordPress admin or login page
    if ( is_admin() || ( isset( $GLOBALS['pagenow'] ) && $GLOBALS['pagenow'] === 'wp-login.php' ) ) {
        return;
    }

    // Detect current environment (your helper should return 'local' for dev)
    $environment = get_environment();

    if ( $environment === 'local' ) {
        /**
         * DEVELOPMENT MODE
         * - Use Vite's dev server for instant HMR (Hot Module Replacement)
         * - Load only JS — Vite will inject styles automatically via JS imports
         */
        $dev_version = time(); // Bust cache on each refresh
        wp_enqueue_script( 'vite-client', 'http://localhost:3000/@vite/client', array(), $dev_version, true );
        wp_enqueue_script( 'main-js', 'http://localhost:3000/src/js/main.js', array(), $dev_version, true );

        // Note: main.js should import your main.scss file
        // Example: `import '../scss/main.scss'`

    } else {
        /**
         * PRODUCTION MODE
         * - Use built & optimized files from /assets/dist/
         * - Use the manifest to get correct hashed filenames
         */
        $manifest  = get_vite_manifest();
        $dist_dir  = get_template_directory() . '/assets/dist/';
        $dist_uri  = get_template_directory_uri() . '/assets/dist/';

        // Get the main JS entry (includes references to emitted CSS)
        $entry = isset( $manifest['src/js/main.js'] ) ? $manifest['src/js/main.js'] : null;

        if ( $entry ) {
            // Enqueue JS
            if ( ! empty( $entry['file'] ) ) {
                $js_file = $entry['file'];
                wp_enqueue_script(
                    'main-js',
                    $dist_uri . $js_file,
                    array(),
                    file_exists( $dist_dir . $js_file ) ? filemtime( $dist_dir . $js_file ) : null,
                    true
                );
            }

            // Enqueue CSS linked in the manifest from main.js imports
            if ( ! empty( $entry['css'] ) && is_array( $entry['css'] ) ) {
                foreach ( $entry['css'] as $i => $css_file ) {
                    wp_enqueue_style(
                        'main-css-' . $i,
                        $dist_uri . $css_file,
                        array(),
                        file_exists( $dist_dir . $css_file ) ? filemtime( $dist_dir . $css_file ) : null
                    );
                }
            }
        } else {
            /**
             * FALLBACK:
             * If you ever build SCSS directly as its own entry (not imported via JS)
             */
            if ( ! empty( $manifest['src/scss/main.scss']['css'] ) ) {
                foreach ( $manifest['src/scss/main.scss']['css'] as $i => $css_file ) {
                    wp_enqueue_style(
                        'main-css-fallback-' . $i,
                        $dist_uri . $css_file,
                        array(),
                        file_exists( $dist_dir . $css_file ) ? filemtime( $dist_dir . $css_file ) : null
                    );
                }
            }
        }
    }

    // Pass PHP data to JavaScript (AJAX URL, nonce, theme URL, etc.)
    $theme_data = array(
        'ajaxUrl'  => admin_url( 'admin-ajax.php' ),
        'nonce'    => wp_create_nonce( 'ajax-nonce' ),
        'themeUrl' => get_template_directory_uri(),
    );
    wp_add_inline_script( 'main-js', 'window.themeData = ' . wp_json_encode( $theme_data ), 'before' );
}
add_action( 'wp_enqueue_scripts', 'theme_scripts' );

// Filter to make certain scripts load as ES Modules (required by Vite)
function make_main_script_module( $tag, $handle, $src ) {
    if ( in_array( $handle, array( 'main-js', 'vite-client' ), true ) ) {
        return '<script type="module" src="' . esc_url( $src ) . '"></script>';
    }
    return $tag;
}
add_filter( 'script_loader_tag', 'make_main_script_module', 10, 3 );

Move Yoast to bottom

// Move Yoast meta box to bottom
function move_yoast_to_bottom() {
    return 'low';
}
add_filter( 'wpseo_metabox_prio', 'move_yoast_to_bottom' );

Include additional function files

This function will include all php files inside assets/functions/, this is done to make the functions.php more maintainable and clear.

/*------------------------------------*\
    Include Additional Function Files
\*------------------------------------*/

foreach ( glob( get_template_directory() . '/assets/functions/*.php' ) as $file ) {
    require $file;
}

acf-local-json.php

This script connects WordPress Gutenberg blocks with ACF Local JSON so that each block can keep its own field group JSON in its own folder — instead of everything going into one big acf-json directory.

Main Functionality

Registers blocks automatically

  • Looks in assets/blocks/*/block.json
  • Calls register_block_type_from_metadata() for each

Loads ACF JSON from multiple places

  • Central acf-json folder
  • Each block's folder (so their fields load automatically)

Saves ACF JSON intelligently

  • If a field group targets a specific block → save JSON into that block's folder
  • If not block-specific → save to central acf-json

Custom JSON filenames

  • If tied to a block → filename = block slug
  • Otherwise → filename = field group title

Optional debug logging

  • Controlled by ACF_JSON_DEBUG constant
  • Logs loaded paths, save decisions, and registrations

Key Benefits

  • Organized Structure: Each block contains its own field definitions
  • Auto-sync: Changes to block fields automatically update the right JSON file
  • Team Friendly: Easier collaboration with separate JSON files per block
  • Debug Ready: Built-in logging for troubleshooting ACF field issues

add-editor-stylesheet.php

This script loads the editor.css from the dist folder when the block editor is opened in the backend.

allow-svg.php

Allows svg's

disable-comments.php

Disables all comment functionality and REST API's

⚡ enqueue-vite-entries.php

The enqueue-vite-entries.php file is a sophisticated WordPress utility that bridges Vite.js build system with WordPress's asset enqueueing system. Here's how it works in this project:

Main Purpose

It automatically handles JavaScript and CSS asset loading for WordPress blocks, making sure assets are only being enqueued whenever they're being used inside the page. It supports both development (with Vite's HMR) and production (with built assets) modes.

Key Functions

1. enqueue_vite_entry_auto()

Usage: enqueue_vite_entry_auto(__FILE__); in block PHP files

  • Auto-discovers block assets based on file directory structure
  • Convention: Expects blocks/<slug>/<slug>.js structure
  • Example: If called from blocks/hero-section/hero-section.php, it automatically enqueues blocks/hero-section/hero-section.js

2. enqueue_vite_entry()

Core functionality that handles both development and production modes:

Development Mode (vite_is_dev() returns true):
  • Serves assets directly from Vite dev server (localhost:3000)
  • Enables Hot Module Replacement (HMR)
  • Adds type="module" for ES modules
Production Mode:
  • Reads Vite's manifest file (dist/.vite/manifest.json)
  • Enqueues built JS/CSS files with proper versioning
  • Handles CSS dependencies and imports automatically

Integration with Project

1. Vite Configuration (vite.config.js:8-23)

function getBlockEntries() {
    const files = fg.sync('assets/blocks/*/*.js', { dot: false });
    // Creates entries for each block's JS file
}

Workflow Example

  1. Block PHP file calls enqueue_vite_entry_auto(__FILE__);
  2. Function extracts directory name (e.g., "image-and-content")
  3. Development: Loads http://localhost:3000/assets/blocks/image-and-content/image-and-content.js
  4. Production: Reads manifest, finds built assets in /dist/, enqueues with proper versioning

Benefits

  • Automatic Discovery: No manual asset registration needed
  • Environment Awareness: Seamlessly switches between dev/prod
  • Hot Module Replacement: Live reloading during development
  • Optimal Production: Proper versioning, chunking, CSS handling
  • Module Support: ES6 modules with type="module"
  • Dependency Resolution: Handles CSS imports and chunks automatically

This system allows developers to focus on building blocks while the asset pipeline is handled automatically, supporting modern JavaScript development workflows within WordPress.

security-hardening.php

The security-hardening.php file implements defensive security measures to prevent user enumeration attacks and unauthorized data access. It blocks non-authenticated users from accessing WordPress REST API endpoints that could reveal user information (/wp/v2/users), redirects author archive pages to prevent username discovery through URLs, and optionally restricts media listings via the REST API for guests.

Edit this page
Last Updated: 4/27/26, 12:56 PM
Contributors: Nicolas Jaenen