php - Custom WordPress Customizer Control for Typography Presets renders blank section or fallback <select>, despi

admin2025-05-31  0

Problem I’m building a WordPress theme and want a Global → Typography → Font Presets control in the Customizer that shows a grid of clickable cards (each card previews a heading/body Google-Font pair). Instead of my custom card UI, the section is either blank or falls back to a basic <select> (or radio list) control. I’ve tried many variations of register_control_type(), direct instantiation, OPcache resets, and cleanup of duplicate classes, but no luck.

What I’ve Done Autoloader in functions.php (recursively includes /inc/ files):

// in functions.php
$rii = new RecursiveIteratorIterator(
  new RecursiveDirectoryIterator( __DIR__ . '/inc' )
);
foreach ( $rii as $file ) {
  if ( ! $file->isDir() && $file->getExtension() === 'php' ) {
    require_once $file->getPathname();
  }
}

Bootstrap singleton in inc/class-zero-customizer.php:

<?php
if ( ! defined( 'ABSPATH' ) ) exit;

class Zero_Customizer {
  private static $instance = null;

  public static function get_instance() {
    if ( null === self::$instance ) {
      self::$instance = new self();
      self::$instance->hooks();
    }
    return self::$instance;
  }

  private function hooks() {
    error_log( 'Zero: hooks() running' );
    add_action( 'customize_register',                [ $this, 'register_typography_control' ] );
    add_action( 'customize_controls_enqueue_scripts',[ $this, 'enqueue_control_assets' ] );
    add_action( 'customize_preview_init',            [ $this, 'enqueue_preview_assets' ] );
  }

  public function register_typography_control( $wp_customize ) {
    error_log( 'Zero: register_typography_control() fired' );
    require_once __DIR__ . '/customizer/controls/class-zero-control-typography.php';

    // Panel & section
    if ( ! $wp_customize->get_panel( 'zero_global_panel' ) ) {
      $wp_customize->add_panel( 'zero_global_panel', [
        'title'    => __( 'Global Settings', 'zero' ),
        'priority' => 10,
      ] );
    }
    $wp_customize->add_section( 'zero_typography_section', [
      'title'    => __( 'Typography', 'zero' ),
      'panel'    => 'zero_global_panel',
      'priority' => 10,
    ] );
    error_log( 'Zero: added section zero_typography_section' );

    // Presets & setting
    $presets = [
      'playfair-open-sans'         => esc_html__( 'Playfair Display / Open Sans', 'zero' ),
      /* …other 9 pairs… */
    ];
    $wp_customize->add_setting( 'zero_typography_preset', [
      'default'           => 'playfair-open-sans',
      'sanitize_callback' => function( $val ) use ( $presets ) {
        return isset( $presets[ $val ] ) ? $val : 'playfair-open-sans';
      },
      'transport'         => 'postMessage',
    ] );

    // Direct instantiation of custom control
    $wp_customize->add_control( new Zero_Control_Typography(
      $wp_customize,
      'zero_typography_preset',
      [
        'label'       => __( 'Font Presets', 'zero' ),
        'description' => __( 'Click a card to choose your Heading/Body pair.', 'zero' ),
        'section'     => 'zero_typography_section',
        'choices'     => $presets,
      ]
    ) );
    error_log( 'Zero: added custom-control for zero_typography_preset' );
  }

  public function enqueue_control_assets() {
    // Load panel CSS & JS
    wp_enqueue_style(
      'zero-customizer-controls',
      get_stylesheet_directory_uri() . '/assets/dist/css/main.min.css',
      [], filemtime( get_stylesheet_directory() . '/assets/dist/css/main.min.css' )
    );
    wp_enqueue_script(
      'zero-customizer-controls',
      get_stylesheet_directory_uri() . '/assets/dist/js/customizer.bundle.js',
      [ 'jquery','customize-controls' ],
      filemtime( get_stylesheet_directory() . '/assets/dist/js/customizer.bundle.js' ),
      true
    );
  }

  public function enqueue_preview_assets() {
    // Load iframe JS for live preview
    wp_enqueue_script(
      'zero-customizer-preview',
      get_stylesheet_directory_uri() . '/assets/dist/js/customizer.bundle.js',
      [ 'jquery','customize-preview' ],
      filemtime( get_stylesheet_directory() . '/assets/dist/js/customizer.bundle.js' ),
      true
    );
  }
}

add_action( 'after_setup_theme', [ 'Zero_Customizer', 'get_instance' ] );

Custom Control in inc/customizer/controls/class-zero-control-typography.php:

<?php
if ( ! class_exists( 'WP_Customize_Control' ) ) {
  return;
}
if ( ! class_exists( 'Zero_Control_Typography' ) ) {
  class Zero_Control_Typography extends WP_Customize_Control {
    public $type = 'typography';

    public function render_content() {
      error_log( 'Zero: Zero_Control_Typography::render_content()' );
      if ( empty( $this->choices ) ) {
        return;
      }

      echo '<span class="customize-control-title">' . esc_html( $this->label ) . '</span>';
      if ( $this->description ) {
        echo '<span class="description customize-control-description">'
             . esc_html( $this->description ) . '</span>';
      }

      echo '<ul>';
      foreach ( $this->choices as $slug => $name ) {
        $checked = checked( $this->value(), $slug, false );
        list( $h, $b ) = explode( '-', $slug, 2 );
        printf(
          '<li><label class="preset-card">'
          . '<input type="radio" data-customize-setting-link="%1$s" value="%2$s"%3$s />'
          . '<span class="preset-card__heading" style="font-family:\'%4$s\';">Heading</span>'
          . '<span class="preset-card__body"    style="font-family:\'%5$s\';">Body text</span>'
          . '</label></li>',
          esc_attr( $this->id ), esc_attr( $slug ), $checked,
          esc_attr( ucwords( str_replace('-', ' ', $h)) ),
          esc_attr( ucwords( str_replace('-', ' ', $b)) )
        );
      }
      echo '</ul>';
    }
  }
}

SCSS & JS

  • Imported into my normal assets/css/sass/main.scss and assets/js/customizer.js builds.
  • Enqueued in the panel via customize_controls_enqueue_scripts and preview via customize_preview_init

Errors & Symptoms

  • Blank Typography section, despite register_typography_control() firing in the logs.

  • If I try array‐style or register_control_type(), it instead renders a plain or radio list.

  • Encountered duplicate‐class “Cannot declare class Zero_Control_Typography” until I deleted old files.

  • Tried OPcache resets, restarting Docker, multiple include patterns—still no card UI.

Questions

  • Why is WP not rendering my Zero_Control_Typography::render_content() output?
  • Is there a necessary hook priority or missing argument I’m overlooking?
  • What’s the minimal, fool-proof way to ensure WP uses my custom control subclass rather than falling back?
  • Are there any Astra‐style patterns (specific enqueue hooks, control registration order) I should mimic?

Any guidance or working minimal example would be hugely appreciated—thanks!

Problem I’m building a WordPress theme and want a Global → Typography → Font Presets control in the Customizer that shows a grid of clickable cards (each card previews a heading/body Google-Font pair). Instead of my custom card UI, the section is either blank or falls back to a basic <select> (or radio list) control. I’ve tried many variations of register_control_type(), direct instantiation, OPcache resets, and cleanup of duplicate classes, but no luck.

What I’ve Done Autoloader in functions.php (recursively includes /inc/ files):

// in functions.php
$rii = new RecursiveIteratorIterator(
  new RecursiveDirectoryIterator( __DIR__ . '/inc' )
);
foreach ( $rii as $file ) {
  if ( ! $file->isDir() && $file->getExtension() === 'php' ) {
    require_once $file->getPathname();
  }
}

Bootstrap singleton in inc/class-zero-customizer.php:

<?php
if ( ! defined( 'ABSPATH' ) ) exit;

class Zero_Customizer {
  private static $instance = null;

  public static function get_instance() {
    if ( null === self::$instance ) {
      self::$instance = new self();
      self::$instance->hooks();
    }
    return self::$instance;
  }

  private function hooks() {
    error_log( 'Zero: hooks() running' );
    add_action( 'customize_register',                [ $this, 'register_typography_control' ] );
    add_action( 'customize_controls_enqueue_scripts',[ $this, 'enqueue_control_assets' ] );
    add_action( 'customize_preview_init',            [ $this, 'enqueue_preview_assets' ] );
  }

  public function register_typography_control( $wp_customize ) {
    error_log( 'Zero: register_typography_control() fired' );
    require_once __DIR__ . '/customizer/controls/class-zero-control-typography.php';

    // Panel & section
    if ( ! $wp_customize->get_panel( 'zero_global_panel' ) ) {
      $wp_customize->add_panel( 'zero_global_panel', [
        'title'    => __( 'Global Settings', 'zero' ),
        'priority' => 10,
      ] );
    }
    $wp_customize->add_section( 'zero_typography_section', [
      'title'    => __( 'Typography', 'zero' ),
      'panel'    => 'zero_global_panel',
      'priority' => 10,
    ] );
    error_log( 'Zero: added section zero_typography_section' );

    // Presets & setting
    $presets = [
      'playfair-open-sans'         => esc_html__( 'Playfair Display / Open Sans', 'zero' ),
      /* …other 9 pairs… */
    ];
    $wp_customize->add_setting( 'zero_typography_preset', [
      'default'           => 'playfair-open-sans',
      'sanitize_callback' => function( $val ) use ( $presets ) {
        return isset( $presets[ $val ] ) ? $val : 'playfair-open-sans';
      },
      'transport'         => 'postMessage',
    ] );

    // Direct instantiation of custom control
    $wp_customize->add_control( new Zero_Control_Typography(
      $wp_customize,
      'zero_typography_preset',
      [
        'label'       => __( 'Font Presets', 'zero' ),
        'description' => __( 'Click a card to choose your Heading/Body pair.', 'zero' ),
        'section'     => 'zero_typography_section',
        'choices'     => $presets,
      ]
    ) );
    error_log( 'Zero: added custom-control for zero_typography_preset' );
  }

  public function enqueue_control_assets() {
    // Load panel CSS & JS
    wp_enqueue_style(
      'zero-customizer-controls',
      get_stylesheet_directory_uri() . '/assets/dist/css/main.min.css',
      [], filemtime( get_stylesheet_directory() . '/assets/dist/css/main.min.css' )
    );
    wp_enqueue_script(
      'zero-customizer-controls',
      get_stylesheet_directory_uri() . '/assets/dist/js/customizer.bundle.js',
      [ 'jquery','customize-controls' ],
      filemtime( get_stylesheet_directory() . '/assets/dist/js/customizer.bundle.js' ),
      true
    );
  }

  public function enqueue_preview_assets() {
    // Load iframe JS for live preview
    wp_enqueue_script(
      'zero-customizer-preview',
      get_stylesheet_directory_uri() . '/assets/dist/js/customizer.bundle.js',
      [ 'jquery','customize-preview' ],
      filemtime( get_stylesheet_directory() . '/assets/dist/js/customizer.bundle.js' ),
      true
    );
  }
}

add_action( 'after_setup_theme', [ 'Zero_Customizer', 'get_instance' ] );

Custom Control in inc/customizer/controls/class-zero-control-typography.php:

<?php
if ( ! class_exists( 'WP_Customize_Control' ) ) {
  return;
}
if ( ! class_exists( 'Zero_Control_Typography' ) ) {
  class Zero_Control_Typography extends WP_Customize_Control {
    public $type = 'typography';

    public function render_content() {
      error_log( 'Zero: Zero_Control_Typography::render_content()' );
      if ( empty( $this->choices ) ) {
        return;
      }

      echo '<span class="customize-control-title">' . esc_html( $this->label ) . '</span>';
      if ( $this->description ) {
        echo '<span class="description customize-control-description">'
             . esc_html( $this->description ) . '</span>';
      }

      echo '<ul>';
      foreach ( $this->choices as $slug => $name ) {
        $checked = checked( $this->value(), $slug, false );
        list( $h, $b ) = explode( '-', $slug, 2 );
        printf(
          '<li><label class="preset-card">'
          . '<input type="radio" data-customize-setting-link="%1$s" value="%2$s"%3$s />'
          . '<span class="preset-card__heading" style="font-family:\'%4$s\';">Heading</span>'
          . '<span class="preset-card__body"    style="font-family:\'%5$s\';">Body text</span>'
          . '</label></li>',
          esc_attr( $this->id ), esc_attr( $slug ), $checked,
          esc_attr( ucwords( str_replace('-', ' ', $h)) ),
          esc_attr( ucwords( str_replace('-', ' ', $b)) )
        );
      }
      echo '</ul>';
    }
  }
}

SCSS & JS

  • Imported into my normal assets/css/sass/main.scss and assets/js/customizer.js builds.
  • Enqueued in the panel via customize_controls_enqueue_scripts and preview via customize_preview_init

Errors & Symptoms

  • Blank Typography section, despite register_typography_control() firing in the logs.

  • If I try array‐style or register_control_type(), it instead renders a plain or radio list.

  • Encountered duplicate‐class “Cannot declare class Zero_Control_Typography” until I deleted old files.

  • Tried OPcache resets, restarting Docker, multiple include patterns—still no card UI.

Questions

  • Why is WP not rendering my Zero_Control_Typography::render_content() output?
  • Is there a necessary hook priority or missing argument I’m overlooking?
  • What’s the minimal, fool-proof way to ensure WP uses my custom control subclass rather than falling back?
  • Are there any Astra‐style patterns (specific enqueue hooks, control registration order) I should mimic?

Any guidance or working minimal example would be hugely appreciated—thanks!

Share Improve this question asked May 2 at 16:11 Rajiv SarkarRajiv Sarkar 1
Add a comment  | 

1 Answer 1

Reset to default 0

If your custom control's render_content() is not being called, the most likely cause is either a PHP error preventing the class from being loaded, or the file is not being included at all. Double-check your file paths and logs. Otherwise, your approach is solid and follows best practices for custom controls in the WordPress Customizer.

Minimal Working Example

1. Add to your theme's functions.php or a customizer file:

add_action( 'customize_register', 'zero_register_typography_control' );
function zero_register_typography_control( $wp_customize ) {
    require_once get_template_directory() . '/inc/customizer/controls/class-zero-control-typography.php';
$wp_customize->add_section( 'zero_typography_section', [
    'title'    => __( 'Typography', 'zero' ),
    'priority' => 10,
] );

$presets = [
    'playfair-open-sans' => esc_html__( 'Playfair Display / Open Sans', 'zero' ),
];

$wp_customize->add_setting( 'zero_typography_preset', [
    'default'           => 'playfair-open-sans',
    'sanitize_callback' => function( $val ) use ( $presets ) {
        return isset( $presets[ $val ] ) ? $val : 'playfair-open-sans';
    },
    'transport'         => 'postMessage',
] );

$wp_customize->add_control( new Zero_Control_Typography(
    $wp_customize,
    'zero_typography_preset',
    [
        'label'       => __( 'Font Presets', 'zero' ),
        'section'     => 'zero_typography_section',
        'choices'     => $presets,
    ]
) );

}

2. Place this in /inc/customizer/controls/class-zero-control-typography.php:

if ( ! class_exists( 'WP_Customize_Control' ) ) return;
class Zero_Control_Typography extends WP_Customize_Control {
    public $type = 'typography';
    public function render_content() {
        if ( empty( $this->choices ) ) return;
        echo '<span class="customize-control-title">' . esc_html( $this->label ) . '</span>';
        echo '<ul>';
        foreach ( $this->choices as $slug => $name ) {
            $checked = checked( $this->value(), $slug, false );
            list( $h, $b ) = explode( '-', $slug, 2 );
            printf(
                '<li><label class="preset-card">'
                . '<input type="radio" data-customize-setting-link="%1$s" value="%2$s"%3$s />'
                . '<span class="preset-card__heading" style="font-family:\'%4$s\';">Heading</span>'
                . '<span class="preset-card__body"    style="font-family:\'%5$s\';">Body text</span>'
                . '</label></li>',
                esc_attr( $this->id ), esc_attr( $slug ), $checked,
                esc_attr( ucwords( str_replace('-', ' ', $h)) ),
                esc_attr( ucwords( str_replace('-', ' ', $b)) )
            );
        }
        echo '</ul>';
    }
}
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1748635278a313681.html

最新回复(0)