How to Add Customizer Panels, Sections & Controls
WordPress Customizer Controls are the form elements that you see inside the WordPress Customizer which provide options such as color pickers, layouts, styling and widget area selectors . These Controls get assigned to accordion-like groups for organization (panels), making it easier for user to find the options they want.
Customizer controls may be added in both Extensions and Child Themes. The following assumes you have already setup your Child Theme or Layers Extension and are at the point where you are adding custom functions.
Defaults
You may add defaults for the pre-existing Layers controls via the layers_customizer_control_defaults hook as described in the Styling section of this guide. This is recommended in Child Themes where you need to reset the default color controls to ensure your CSS is honored. Click the hook link above to view detailed instructions.
Panels, Sections and Controls
To understand where your customizations will appear, and which element you need to add, the following illustrates the terms we use:
- Panel
- Section
- Control
- Option
The first tier, high level, groups are know as Panels, and those inside them are called Sections, and finally those open up to reveal the Controls containing all the options. WordPress allows theme and plugin developers to add sections and panels using the add_section() function. Controls are then added to these sections by first registering a ‘theme_mod’ type setting using add_setting() and then assigning the setting to a Control using add_control() that will decide how the setting is presented to the user.
We make this process easier by providing a set of filters and keys for building custom controls in a more efficient way.
You can choose to add custom panels to house your new sections and controls, or you can add sections to existing panels or controls to existing sections. Adding to our config array will neatly initialize your controls at the same time the Layers controls are initialized.
Filter Structure
Plugin
You first need to create a custom class to hold your controls inside a new file which we call controls.php.This class is setup just like the main plugin class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class Layers_Demo_Extension_Controls { private static $instance; /** * Get Instance creates a singleton class that's cached to stop duplicate instances */ public static function get_instance() { if ( ! self::$instance ) { self::$instance = new self(); self::$instance->init(); } return self::$instance; } /** * Init behaves like, and replaces, construct */ public function init(){ // custom public functions go here } // Initialize Layers_Demo_Extension_Controls::get_instance(); |
Include this file from inside your main plugin class __construct() or init() function with a simple require_once():
1 2 |
// Includes require_once( LAYERS_DEMO_EXTENSION_DIR . 'includes/controls.php' ); // Customizer Controls |
Back to your controls class, setup a handful of helper filters for the panels, sections and controls inside your constructor or initializer. For demonstration we will use init() here so you can see the (small) difference between init() and __construct(). These filters can be used as shown here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * Init behaves like, and replaces, construct */ public function init(){ // Add Customizer Panel add_filter( 'layers_customizer_panels', array( $this, 'modify_customizer_panels' ), 60 ); // Add Customizer Section add_filter( 'layers_customizer_sections', array( $this, 'modify_customizer_sections' ), 60 ); // Add Customizer Control add_filter( 'layers_customizer_controls', array( $this, 'modify_customizer_controls' ), 60 ); } //end init |
The corresponding functions are added along with other public functions in your main plugin class, after the init() function, explained in more detail below.
Child Theme
Your custom controls function in a child theme opens almost identically to an extension, but does not need to define public visibility.
The filter and custom function containing your controls can go into the child theme’s functions.php and requires a unique callback
Example:
1 2 3 |
add_filter('layers_customizer_panels','layers_child_customizer_panels', 60); add_filter('layers_customizer_sections','layers_child_customizer_sections', 100); add_filter( 'layers_customizer_controls', 'layers_child_customizer_controls', 100 ); |
The following explains each function in detail and how to format it based on whether you are adding new or adding to.
Custom Panels
Reference: layers_customizer_panels
Plugin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * Add Customizer Panel (plugin) */ public function modify_customizer_panels( $panels ) { // Add Custom panel. $panels['demoextension'] = array( 'title' => __( 'Demo Extension' , LAYERS_DEMO_EXTENSION_SLUG ), 'description'=> __( 'Your custom description.' , LAYERS_DEMO_EXTENSION_SLUG ), 'priority' => 130 ); return $panels; } |
Child Theme
1 2 3 4 5 6 7 8 9 |
if( !function_exists( 'layers_child_customizer_panels' ) ) { function layers_child_customizer_panels( $panels ){ $panels['demoextension'] = array( 'title' => __( 'Demo Extension' , 'child' ), 'description'=> __( 'Your custom description.' , LAYERS_DEMO_EXTENSION_SLUG ), 'priority' => 130 ); return $panels; } |
- Line 1: Safety condition ensures a conflict does not occur if a function with the same name is already run. Choosing unique function names helps avoid this too.
- Line 2: setup your function to modify the $panels index.
- Line 3: Set the key or name for your panel, ie ‘mytheme-theme-options’. These should be lower-case and use only – or _ to separate words.
- Line 4: Set the ‘title’ for your section that displays in the Customizer. Strings should always use l10n methods, ie
__('string', 'textdomain') - Line 5: Set the ‘description’ (optional). This is a short bit of text you can use as instruction or clarification.
- Line 6: Set the ‘priority’ which is a numeric value that determines where the panel sits sin the list. 130 is a safe choice as it ensures your panel loads below and after the core Layers panels.
Custom Sections
Reference: layers_customizer_sections
Plugin
1 2 3 4 5 6 7 8 9 10 11 12 |
/** * Add Customizer Section */ public function modify_customizer_sections( $sections ) { $sections['cpt-options'] = array( 'title' => __( 'Custom Post Type Options', LAYERS_DEMO_EXTENSION_SLUG ), 'panel' => 'demoextension', ); return $sections; } |
The panel value corresponds to the $panels key, set in the above section as demoextension . If you are not creating a custom panel, then this would be a layers panel ID (see the linked reference above)
Child Theme
Each section should define a title and the panel it will be added to. This example shows how you would add a custom section called header-social-media (Social Media Profiles) to our custom panel called demoextension (Demo Extension)
1 2 3 4 5 6 7 8 9 |
if( !function_exists( 'layers_child_customizer_sections' ) ) { function layers_child_customizer_sections( $sections ){ $sections[ 'header-social-media' ] = array( 'title' => __( 'Social Media Profiles' , 'layers-child-demo' ), 'panel' => 'demoextension', ); return $sections; } } |
Adding Sections to Existing Panels
This example shows how you would add our custom section called header-social-media (Social Media Profiles) to the existing Header ( header ) panel in Layers. The primary difference here is the array_merge which takes your section and merges it with the existing ones. You must merge on existing panels, whereas you don’t if using a custom panel.
1 2 3 4 5 6 7 8 9 10 11 |
if( !function_exists( 'layers_child_customizer_sections' ) ) { function layers_child_customizer_sections( $sections ){ $sections[ 'header-social-media' ] = array( 'title' => __( 'Social Media Profiles' , 'layers-child-demo' ), 'panel' => 'header', ); $sections = array_merge( $sections, $sections[ 'header-social-media' ] ); return $sections; } } |
The following panels are created by Layers:
Panel: Site Settings
site-settings
Panel: Header
header
Panel: Blog
1 |
blog-archive-single |
Panel: Footer
footer
Panel: WooCommerce
woocommerce
You can also add custom sections to existing non-Layers panels like the default WordPress panels using the WordPress Core API instead of the Layers filters.
Custom Controls
Reference: layers_customizer_controls
Plugin
In our extension tutorial, we create a custom post type and add a few custom fields to it like a Credit Name, URL and Photo description. Our custom controls will add the option to show or hide this meta on single posts using our post type, in addition to basic meta like the date or sidebars.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
/** * Add Customizer Controls */ public function modify_customizer_controls( $controls ){ $demo_extension_controls['cpt-options'] = array( 'demo-post-options-heading-styling' => array( 'type' => 'layers-heading', 'heading_divider' => __( 'Display', 'layers-pro' ) . $showcase_badge, 'description' => __( 'Choose what you display on your single demoextension page.', LAYERS_DEMO_EXTENSION_SLUG ), ), 'demo-post-single-credit' => array( 'type' => 'layers-checkbox', 'label' => __( 'Credit', LAYERS_DEMO_EXTENSION_SLUG ), 'default' => 'yes', ), 'demo-post-single-source' => array( 'type' => 'layers-checkbox', 'label' => __( 'Source', LAYERS_DEMO_EXTENSION_SLUG ), 'default' => 'yes', ), 'demo-post-description' => array( 'type' => 'layers-checkbox', 'label' => __( 'Description', LAYERS_DEMO_EXTENSION_SLUG ), 'default' => 'yes', ), 'demo-post-single-date' => array( 'type' => 'layers-checkbox', 'label' => __( 'Dates', LAYERS_DEMO_EXTENSION_SLUG ), 'default' => 'yes', ), 'demo-post-single-sidebar' => array( 'type' => 'layers-checkbox', 'label' => __( 'Sidebar', LAYERS_DEMO_EXTENSION_SLUG ), 'default' => 'yes', ), ); return array_merge_recursive( $controls, $demo_extension_controls ); } |
The important bit here is the array merge, which takes these custom controls and adds them to the overall controls array for loading.
Each control group uses an array to define itself, ie demo-post-options . At a minimum you need a type and label within each individual option.
Child Theme
This example shows how you would add some text fields for entering Social Network URLs in the custom section header-social-media referenced in our custom section above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
if( !function_exists( 'layers_child_customizer_controls' ) ) { function layers_child_customizer_controls( $controls ){ $controls[ 'header-social-media' ] = array( 'social-twitter' => array( 'type' => 'layers-text', 'label' => __( 'Twitter Username' , 'layers-child-demo' ) ), 'social-facebook' => array( 'type' => 'layers-text', 'label' => __( 'Facebook Vanity URL' , 'layers-child-demo' ) ), 'social-instagram' => array( 'type' => 'layers-text', 'label' => __( 'Instagram Username' , 'layers-child-demo') ) ); return $controls; } } |
$controls [‘section-name’] = In our example, our section is called header-social-media . Each option sets up its own array that must have a title and label.
type
Defines the type of control.
1 |
'type' => 'layers-text' |
See layers_customizer_controls for a list of available types
label
The title. This should use l10n method
__()
to wrap the value
1 |
'label' => __( 'Choose Background' , 'your-textdomain' ) |
placeholder
Used in combination with a type of layers-text , layers-textarea , layers-rte or layers-code element, and sets some default text inside the field to use an example or instructions.
1 |
'placeholder' => "Enter URL", |
description
Long-form text describing what your control is used for (optional). Descriptions should be formatted with sprintf and use l10n
1 2 |
'description' => sprintf( __( 'For more information, see <a href="%s" target="_blank">follow this link</a>.', 'yourtheme_textdonmain' ), 'http://www.your-url.com' ), ) |
choices
Used in combination with a select type to define the drop-down options. The below example shows how we add a Widget Areas option using the layers-select type and populate the drop-down with 0-4 using choices.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
'footer-sidebar-count' => array( 'type' => 'layers-select', 'label' => __( 'Widget Areas' , 'layerswp' ), 'default' => 4, 'sanitize_callback' => 'layers_sanitize_number', 'choices' => array( '0' => __( 'None' , 'layerswp' ), '1' => __( '1' , 'layerswp' ), '2' => __( '2' , 'layerswp' ), '3' => __( '3' , 'layerswp' ), '4' => __( '4' , 'layerswp' ), ) ) // layout |
Adding Controls to Existing Sections
In the above example we set the section name in the $controls array, then return $controls. When adding controls to an existing section, we need to use an array_merge before returning the $controls to set the section.
1 |
$controls['section-name'] = array_merge( $controls['section-name'], $social_media_controls ); |
You can reference the available key names for the Layers Sections in the $controls array in core/customizer/config.php
In the following example, we setup a custom controls array $social_media_controls , then merge it with the $controls array in the header-layout section. See how the syntax varies slightly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
if( !function_exists( 'mytheme_customizer_controls' ) ) { function mytheme_customizer_sections( $controls){ $social_media_controls = array( 'social-twitter' => array( 'type' => 'layers-text', 'label' => __( 'Twitter Username' , 'mytheme_textdodmain' ), ), 'social-facebook' => array( 'type' => 'layers-text', 'label' => __( 'Facebook Vanity URL' , 'mytheme_textdodmain' ), ), 'social-pinterest' => array( 'type' => 'layers-text', 'label' => __( 'Pinterest Username' , 'mytheme_textdodmain' ), ), ); $controls['header-layout'] = array_merge( $controls['header-layout'], $social_media_controls ); return $controls; } } |
The following Panels and Sections already exist in Layers, which can be extended by adding your custom controls to the relevant section where header-layout is used in the above example:
- Panel: Site Settings
site-settings- Section: Logo & Title
title_tagline - Section: Navigation
nav - Section: Site Colors
site-colors - Section: Sidebars
content-sidebars - Section: Fonts
fonts
- Section: Logo & Title
- Panel: Header
header- Section: Layout
header-layout - Section: Additional Scripts
header-scripts
- Section: Layout
- Panel: Footer
footer- Section: Layout
footer-layout - Section: Customization
footer-customization - Section: Text
footer-text - Section: Additional Scripts
footer-scripts
- Section: Layout
- Panel: WooCommerce
woocommerce
WordPress offers the following defaults:
- title_tagline – Site Title & Tagline
- colors – Colors
- header_image – Header Image
- background_image – Background Image
- nav – Navigation
- static_front_page – Static Front Page
You can also add custom controls to existing non-Layers sections like the default WordPress sections using the WordPress Core API instead of the Layers filters.
This should give you an idea of the general format of a control, and for more examples you can have a look in core/customizer/config.php where you’ll see all the controls that we initialize in Layers.
Removing or De-registering Existing Controls
We do not recommend doing this in commercial prodicts. See layers_remove_config_element() for details on how to use this filter when necessary.
Setting Selectors for Color Controls
If you have setup a custom control with a type of layers-color , you will need to link it to the element it needs to modify, then output the CSS correctly using layers_inline_styles . This ensures the customizer option overrides everything else, absolutely. It also allows you to tap into the invert control that helps text elements adjust automatically depending on if the user chooses a light or dark background color. This can help you set shortcuts and cut down on the number of color controls.
You can see how we do this for the default color controls in core/helpers/template.php inside the layers_apply_customizer_styles() function.
1 |
add_action( 'wp_enqueue_scripts', 'layers_child_customizer_styles', 100 ); |
The first value wp_enqueue_scripts allows you to hook your function into the pre-existing lineup of scripts and let’s WordPress manage when the script fires and where in the source it is output. The second value defines your function name and the third is the priority. 100 is a safe number and is more important to plugin authors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
if( !function_exists( 'layers_child_customizer_styles' ) ) { function layers_child_customizer_styles() { 'widget-title-color' , TRUE ); if( '' != $widget_title_color ) { layers_inline_styles( array( 'selectors' => array( '.sidebar .section-nav-title'), 'css' => array( 'color' => $widget_title_color, ), )); } } } |
- Line 1-2 : The above example creates a new function
layers_child_customizer_styles - Line 3: Set up a variable
$widget_title_color
that will represent the user’s choice. We use
layers_get_theme_mod()
to grab our custom control
widget-title-color
defined on line 4 (assuming we created one with this key name using the above Custom Control methods) - Line 5:TRUE corresponds to$allow_empty and should always be true for colors, so don’t worry about that!
- Line 7: A condition to ensure the option is set. If it is blank, no inline CSS will be output.
- Line 8: Finally, we setup our inline css using layers_inline_styles()
This code goes in your theme’s functions.php or inside your plugin’s class where all other custom hooks go, NOT inside your custom controls, sections or panels functions!
Using Option Settings on the Front-End
To utilize a setting in the front end on Layers, we use a helper function called layers_get_theme_mod() which works just like the WordPress core get_option().
Plugin
Each $option for layers_get_theme_mod($option) is the custom option name you setup in your custom controls array. This example shows how we would use a condition to determine whether a sidebar is added to the demo post view depending on the option choice.
1 2 3 |
<?php if( '' != layers_get_theme_mod( 'demo-post-single-sidebar' )) { get_sidebar('right'); }?> |
Child Theme
In the following example we will add some social icons, corresponding to the networks set in these options, to the header of Layers using the layers_after_logo hook. This goes into your functons.php or plugin class where all other hooks are set.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function my_header_social_media_icons(){ ?> <div class="pull-right"> <?php if( '' != layers_get_theme_mod( 'social-facebook' ) { ?> <a href="<?php echo layers_get_theme_mod( 'social-facebook' ); ?>"><i class="i-facebook">facebook</i></a> <?php } ?> <?php if( '' != layers_get_theme_mod( 'social-twitter' ) { ?> <a href="<?php echo layers_get_theme_mod( 'social-twitter' ); ?>"><i class="i-twitter">facebook</i></a> <?php } ?> <?php if( '' != layers_get_theme_mod( 'social-instagram' ) { ?> <a href="<?php echo layers_get_theme_mod( 'social-pinterest' ); ?>"><i class="i-instagram">instagram</i></a> <?php } ?> </div> <?php }; } |