Theme Architecture
BuddyX Pro uses a modern, component-based architecture with PSR-4 autoloading for organized, maintainable code.
Overview
Section titled “Overview”The theme follows object-oriented principles with a modular component system. Each feature is encapsulated in its own component that implements the Component_Interface.
Key Features:
- PSR-4 autoloading with fallback
- Component-based architecture
- Template tag system
- Namespace:
BuddyxPro\BuddyxPro - Minimum requirements: WordPress 6.5+, PHP 8.0+ (
BUDDYXPRO_MINIMUM_WP_VERSION/BUDDYXPRO_MINIMUM_PHP_VERSIONinfunctions.php)
Directory Structure
Section titled “Directory Structure”buddyx-pro/├── assets/ # CSS, JS, images, fonts│ ├── css/│ ├── js/│ ├── images/│ └── svg/├── external/ # Optional third-party shim files (legacy, kept empty in 5.1.0+)├── inc/ # Theme components (PSR-4 autoloaded)│ ├── compatibility/ # Plugin integrations│ │ ├── buddypress/│ │ ├── woocommerce/│ │ ├── learndash/│ │ ├── fluentcart/│ │ └── ...│ ├── Customizer/ # WP_Customize bootstrap component│ ├── Customizer_Framework/ # First-party field API (Field, Panel, Section, Output_Builder)│ ├── Customizer_Settings/ # Per-section field definitions│ ├── Tokens/ # --bx-* CSS custom-property emitter│ ├── Color_Mode_Toggle/# Dark / light toggle│ ├── Styles/ # CSS enqueue manifest + inline CSS│ ├── Helpers/ # Procedural helper files (Login_Forms, Dark_Mode, ...)│ ├── widgets/ # Custom widgets│ ├── Component_Interface.php│ ├── Theme.php # Main theme class│ ├── Template_Tags.php # Template tag system│ └── functions.php # Entry point function├── template-parts/ # Reusable template parts├── vendor/ # Composer autoloader├── functions.php # Theme initialization└── style.css # Theme stylesheetCore Architecture
Section titled “Core Architecture”Entry Point (functions.php)
Section titled “Entry Point (functions.php)”// Setup autoloader (Composer or custom fallback)if ( file_exists( get_template_directory() . '/vendor/autoload.php' ) ) { require get_template_directory() . '/vendor/autoload.php';} else { spl_autoload_register( '_buddyxpro_autoload' );}
// Load entry point functionrequire get_template_directory() . '/inc/functions.php';
// Initialize themecall_user_func( 'BuddyxPro\BuddyxPro\buddyxpro' );Component Interface
Section titled “Component Interface”All theme components must implement Component_Interface:
namespace BuddyxPro\BuddyxPro;
interface Component_Interface { /** * Gets the unique identifier for the theme component. * * @return string Component slug. */ public function get_slug(): string;
/** * Adds the action and filter hooks to integrate with WordPress. */ public function initialize();}Main Theme Class (Theme.php)
Section titled “Main Theme Class (Theme.php)”The Theme class orchestrates all components:
namespace BuddyxPro\BuddyxPro;
class Theme { protected $components = array(); protected \BuddyxPro\BuddyxPro\Template_Tags $template_tags;
public function __construct( array $components = array() ) { // Register components foreach ( $components as $component ) { $this->components[ $component->get_slug() ] = $component; } }
public function initialize() { // Initialize all components array_walk( $this->components, function( $component ) { $component->initialize(); }); }}Default Components
Section titled “Default Components”Theme::get_default_components() (inc/Theme.php) registers these components, in this order:
Localization- Text domain and translationsMigration- Version-to-version data migrationsBase_Support- Core WordPress theme featuresEditor- Block editor supportAccessibility- ARIA labels, skip links, theme-color AJAXImage_Sizes- Custom image sizesAMP- AMP supportPWA- Progressive Web App supportComments- Comment markup and callbacksNav_Menus- Menu locationsSidebars- Widget areasCustom_Background- Custom background supportCustom_Header- Custom header supportCustom_Logo- Custom logo supportPost_Thumbnails- Featured image supportCustomizer-WP_CustomizebootstrapFonts- Font registration and Google FontsStyles- Stylesheet enqueue manifestScripts- JavaScript enqueue manifestExcerpts- Excerpt handlingOptions- Theme option helpersBlocks- Gutenberg block supportBlock_Patterns- Block pattern registrationCustomizer_Settings- Per-section customizer field definitionsStarter_Content- Starter contentTokens- Emits--bx-color-*/--bx-radius-*custom properties on<html>from color / dimension theme modsColor_Mode_Toggle- Dark / light color-mode togglePage_Settings- Per-page settings meta boxWelcome- Theme welcome / onboarding screenPlugin_Installer- Recommended-plugin installer
Conditional component:
// Jetpack integration (only when Jetpack is active).if ( defined( 'JETPACK__VERSION' ) ) { $components[] = new Jetpack\Component();}
Customizer_Frameworkis not a registered component. It is a first-party static-class library (Field,Panel,Section,Output_Builder) that replaced Kirki in 5.1.0; theCustomizer_Settingscomponent consumes it. There is noDynamic_Stylecomponent orinc/Dynamic_Style/directory - dynamic CSS is produced byTokens/Component.php(custom properties),Customizer_Framework/Output_Builder.php(per-fieldoutputrules), andStyles/Component.php(inline CSS atwp_head).
Plugin integrations (BuddyPress, WooCommerce, LearnDash, Dokan, FluentCart, bbPress, BuddyNext, etc.) are not components - they are procedural files loaded conditionally from functions.php (see “Plugin Integration Pattern” below).
PSR-4 Autoloading
Section titled “PSR-4 Autoloading”File-to-Class Mapping
Section titled “File-to-Class Mapping”Class namespace maps directly to file path:
Namespace: BuddyxPro\BuddyxPro\Customizer\ComponentFile: inc/Customizer/Component.php
Namespace: BuddyxPro\BuddyxPro\Tokens\ComponentFile: inc/Tokens/Component.phpCustom Autoloader (Fallback)
Section titled “Custom Autoloader (Fallback)”If Composer is unavailable:
function _buddyxpro_autoload( $class_name ) { $namespace = 'BuddyxPro\BuddyxPro';
if ( 0 !== strpos( $class_name, $namespace . '\\' ) ) { return false; }
// Convert namespace to file path $parts = explode( '\\', substr( $class_name, strlen( $namespace . '\\' ) ) ); $path = get_template_directory() . '/inc';
foreach ( $parts as $part ) { $path .= '/' . $part; } $path .= '.php';
if ( file_exists( $path ) ) { require_once $path; return true; }
return false;}spl_autoload_register( '_buddyxpro_autoload' );Template Tag System
Section titled “Template Tag System”Accessing Template Tags
Section titled “Accessing Template Tags”Template tags are accessible via the buddyxpro() function:
// In template filesbuddyxpro()->display_header();buddyxpro()->posted_on();buddyxpro()->get_version();Template_Tags Class
Section titled “Template_Tags Class”The Template_Tags class provides magic method access:
class Template_Tags { protected $template_tags = array();
public function __call( string $method, array $args ) { if ( ! isset( $this->template_tags[ $method ] ) ) { throw new BadMethodCallException( sprintf( 'The template tag %s does not exist.', $method ) ); }
return call_user_func_array( $this->template_tags[ $method ]['callback'], $args ); }}Registering Template Tags
Section titled “Registering Template Tags”Components implement Templating_Component_Interface:
namespace BuddyxPro\BuddyxPro\MyComponent;
use BuddyxPro\BuddyxPro\Component_Interface;use BuddyxPro\BuddyxPro\Templating_Component_Interface;
class Component implements Component_Interface, Templating_Component_Interface {
public function template_tags(): array { return array( 'my_custom_tag' => array( $this, 'render_custom_tag' ), ); }
public function render_custom_tag() { // Template tag logic }}Plugin Integration Pattern
Section titled “Plugin Integration Pattern”Plugin integrations are isolated in inc/compatibility/:
Directory Structure
Section titled “Directory Structure”inc/compatibility/├── buddypress/│ ├── buddypress-functions.php│ └── README.md├── woocommerce/│ ├── woocommerce-functions.php│ └── README.md├── fluentcart/│ ├── fluentcart-functions.php│ ├── HOOKS-REFERENCE.md│ └── README.md└── learndash/ ├── learndash-functions.php └── README.mdConditional Loading
Section titled “Conditional Loading”In functions.php:
// Load WooCommerce functionsif ( class_exists( 'WooCommerce' ) ) { require get_template_directory() . '/inc/compatibility/woocommerce/woocommerce-functions.php';}
// Load FluentCart functionsif ( defined( 'FLUENTCART_PLUGIN_FILE_PATH' ) ) { require get_template_directory() . '/inc/compatibility/fluentcart/fluentcart-functions.php';}
// Load LearnDash functionsif ( class_exists( 'SFWD_LMS' ) ) { require get_template_directory() . '/inc/compatibility/learndash/learndash-functions.php';}Integration Class Pattern
Section titled “Integration Class Pattern”class BuddyXPro_FluentCart_Support { private static $instance = null;
public static function get_instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; }
private function __construct() { $this->init_hooks(); }
private function init_hooks() { // Add hooks for plugin integration add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) ); add_filter( 'body_class', array( $this, 'add_body_classes' ) ); }}
// InitializeBuddyXPro_FluentCart_Support::get_instance();Customizer Architecture
Section titled “Customizer Architecture”Customizer Framework (5.1.0+)
Section titled “Customizer Framework (5.1.0+)”BuddyX Pro 5.1.0 replaced the external Kirki dependency with a first-party Customizer Framework that lives at inc/Customizer_Framework/. The public API matches Kirki’s add_*() calls 1:1 so existing field declarations continue to work; the framework owns sanitisation, output, and the preview JS.
// No bootstrap require - the framework is autoloaded as a Component.use BuddyxPro\BuddyxPro\Customizer_Framework\Field;Adding Customizer Options
Section titled “Adding Customizer Options”// Panel (registered via WP_Customize_Manager directly - the framework// only owns sections + fields).add_action( 'customize_register', function ( $wp_customize ) { $wp_customize->add_panel( 'my_panel', array( 'title' => __( 'My Panel', 'buddyxpro' ), 'priority' => 10, ) );
$wp_customize->add_section( 'my_section', array( 'title' => __( 'My Section', 'buddyxpro' ), 'panel' => 'my_panel', ) );} );
// Field - framework picks the right control + sanitizer based on `type`.\BuddyxPro\BuddyxPro\Customizer_Framework\Field::add( 'switch', array( 'settings' => 'my_option', 'label' => __( 'Enable Feature', 'buddyxpro' ), 'section' => 'my_section', 'default' => 'off', 'choices' => array( 'on' => __( 'Yes', 'buddyxpro' ), 'off' => __( 'No', 'buddyxpro' ), ), ));Supported field types
Section titled “Supported field types”color, switch, radio_image, radio_buttonset, select, slider, dimension, typography, background, image, cropped_image, text, textarea, repeater, sortable, custom. See inc/Customizer_Framework/Field.php::$field_types for the canonical map.
Auto-resolved transport
Section titled “Auto-resolved transport”When a field includes an output array (one or more { element, property, units } rules), the framework auto-resolves its transport to postMessage and wires customizer-preview.js to apply the new CSS rule live in the iframe. Set 'transport' => 'refresh' explicitly only when a setting genuinely needs a full reload.
Tokens system
Section titled “Tokens system”Colour and dimension settings flow through inc/Tokens/Component.php, which writes one set of --bx-color-* / --bx-radius-* CSS custom properties on <html> plus a thin compat layer of legacy aliases (--color-theme-body, --global-border-radius, etc.). New CSS should consume the --bx-* token; aliases exist only for back-compat with pre-5.1.0 selectors. normalize_dimension() defaults bare numbers to px so customizer sliders that don’t emit a unit still produce valid CSS.
Output_Builder
Section titled “Output_Builder”inc/Customizer_Framework/Output_Builder.php walks every field with an output rule and emits one combined inline CSS block at wp_head. Typography arrays expand into multi-declaration blocks; backgrounds expand into the 6-key background shorthand; scalar values use the field’s _type to pick a default CSS property.
Dynamic Style Generation
Section titled “Dynamic Style Generation”Dynamic CSS in 5.1.x is produced by three cooperating pieces - there is no single “Dynamic_Style” component:
inc/Tokens/Component.phpmaps color / dimension theme mods onto canonical--bx-color-*and--bx-radius-*custom properties (plus legacy aliases) and prints them on<html>atwp_head. Light and dark modes emit separate token blocks.inc/Customizer_Framework/Output_Builder.phpwalks every customizer field that declares anoutputrule and emits one combined inline CSS block.inc/Styles/Component.phpenqueues the CSS manifest and adds any remaining inline CSS (viaload_custom_styles()onwp_head).
Child themes should consume the --bx-* tokens rather than regenerating CSS. See the Customization Guide.
Widget Registration
Section titled “Widget Registration”Custom Widgets
Section titled “Custom Widgets”Widgets are in inc/widgets/:
// Load widgetsrequire get_template_directory() . '/inc/widgets/login-widget.php';require get_template_directory() . '/inc/widgets/bp-profile-completion-widget.php';Bundled widget classes
Section titled “Bundled widget classes”| Class | File |
|---|---|
BP_BUDDYX_BP_Login_Widget |
inc/widgets/login-widget.php |
BP_BuddyxPro_Profile_Completion_Widget |
inc/widgets/bp-profile-completion-widget.php |
LD_Course_Features_Widget |
inc/widgets/ld-featured-course-widget.php |
BuddyxPro_WCV_Widget_Vendor_Profile |
inc/widgets/class-vendor-profile-widget.php (extends WC_Widget) |
class BP_BUDDYX_BP_Login_Widget extends WP_Widget {
public function __construct() { parent::__construct( 'buddyx_login_widget', __( 'BuddyX Login', 'buddyxpro' ), array( 'description' => __( 'Login form widget', 'buddyxpro' ) ) ); }
public function widget( $args, $instance ) { // Widget output }}
// The login widget registers itself on widgets_init.function buddyx_register_bp_login_widget() { register_widget( 'BP_BUDDYX_BP_Login_Widget' );}add_action( 'widgets_init', 'buddyx_register_bp_login_widget' );Helper Functions
Section titled “Helper Functions”Modular Helpers (inc/Helpers/)
Section titled “Modular Helpers (inc/Helpers/)”// Login and registration popupsrequire_once get_template_directory() . '/inc/Helpers/Login_Forms.php';
// BuddyPress activity enhancementsrequire_once get_template_directory() . '/inc/Helpers/Activity_Functions.php';
// Side panel functionalityrequire_once get_template_directory() . '/inc/Helpers/Side_Panel.php';
// Dark mode togglerequire_once get_template_directory() . '/inc/Helpers/Dark_Mode.php';Utility Functions (inc/extra.php)
Section titled “Utility Functions (inc/extra.php)”Core template functions:
// Content wrapper hooksadd_action( 'buddyx_before_content', 'buddyx_content_top' );add_action( 'buddyx_after_content', 'buddyx_content_bottom' );
// Sub headeradd_action( 'buddyx_sub_header', 'buddyx_sub_header' );
// Header menu-icon actions area (search / cart). This in turn fires the// buddyx_header_actions hook - there is no buddyx_header hook.buddyx_site_menu_icon();Version Management
Section titled “Version Management”Theme Version
Section titled “Theme Version”// Get theme version$version = wp_get_theme( get_template() )->get( 'Version' );
// Via template tagsbuddyxpro()->get_version();Asset Versioning
Section titled “Asset Versioning”// Development: file modification time// Production: theme versionbuddyxpro()->get_asset_version( $filepath );Best Practices
Section titled “Best Practices”- Use PSR-4 autoloading for new classes
- Implement
Component_Interfacefor new components - Use template tag system for reusable functions
- Add plugin integrations to
inc/compatibility/ - Use
BuddyxPro\BuddyxPro\Customizer_Framework\Field::add()for customizer options (no third-party dependency in 5.1.0+) - Prefix all functions with
buddyx_orbuddyxpro_ - Use namespace
BuddyxPro\BuddyxProfor classes
- Modify core theme files directly
- Add business logic to templates
- Use global variables without prefixing
- Skip component initialization
- Bypass the autoloader
- Hardcode plugin paths

