Overview

What is a WordPress theme?

So what exactly is a WordPress theme? Technically, a WordPress theme is nothing more than a collection of files that work together to display content to the user. WordPress itself ships with three of its own default themes. These are meant to showcase the diversity and potential of themes, while also enabling the user to customize how their site appears to visitors on the front-end.

A WordPress theme is just like a skin of your web page or website which allows you to modify the looks of the content of your website. You might be astonished to know that WordPress theme development is actually not that much difficult once you’ve got the hang of it.

Themes enable users to tap into the power of WordPress to display and manage their content exactly as desired. As we’ll see, there are themes for just about every niche and clique imaginable. Users can download themes for blogs, shops, portfolios, magazines, and so much more. Indeed, themes bring together the power of WordPress with the creativity of designers, developers, and savvy users. Once installed and activated, themes may be customized in numerous ways. Most of the newer, cutting-edge themes ship with loads of options and features, enabling users to dial in the perfect theme without touching a line of code. Other themes, such as starter templates and simple themes, may include no options whatsoever. So in order to customize things, it may be necessary to work directly with template files. Either way, themes provide full control over how content is displayed on the front-end. In terms of customizing site appearance and functionality, the theme is where it all happens.

Themes can be very general like the typical default themes, or very specific like a real-estate theme. Themes can be very simple, using as few as two template files, or they can be very complex, requiring hundreds of files. The Twenty Fourteen WordPress theme, for example, is a magazine-style theme consisting of around 70 files. Further, consider that WordPress-powered sites are dynamic, meaning that content is stored in a database and displayed on the front-end based on the active theme’s template code. In other words, the theme provides a user-interface for accessing database content. Themes also enable customization of the WordPress Admin Area. For example, the 2020 theme includes a set of functions that customize various aspects of the Admin Area, such as the Login Page, Dashboard, Toolbar, and footer text. Although modifying the Admin Area is best left to plugins, it’s nice to know that themes can do it too.

WordPress themes have access to all of the functionality provided by WordPress. This means that we can tap into WordPress and use the functions that are needed to display our content as desired. So WordPress provides the functionality, the database stores the content, plugins extend and enhance WordPress, and themes bring it all together with a flexible template system. Another important thing that themes do is separate the user-interface (UI) from the core WordPress files. This degree of separation enables you to load up on functionality without touching any of the core files. This means that sites can update the WordPress core without changing anything in the theme, so millions of users to stay current with the latest and greatest. Indeed, themes play an important role on the Web, enabling users to make the most of WordPress.

What can themes do?

Themes play a key role by bringing together a site’s content, design, and functionality. In doing so, themes tap into WordPress’ vast arsenal of functions, hooks, and tags to generate a UI for interacting with the database. Essentially, themes enable you to harness the power of WordPress to create a unique, highly customized design that suits your site’s purpose, scope, and goals.

Theme Anatomy

Understanding Page Views

Page Views play an important role in how WordPress themes operate. In WordPress, the Page View refers to the type of web page generated by WordPress. For example, when viewing a single blog post, you’re looking at a “single view” or “single-post view”. Likewise, when viewing the archives, you’re looking at an “archive view”. It’s a simple yet important concept that is key to understanding how themes work on the front-end. To get a better idea of how it works, check out the popout box, “WordPress Page Views”, on this page. As seen in the chart above, Page Views are associated with specific URLs. For example, to access a single-post view, we could visit http://example.com/?p=1 in a browser. If no content is found for the requested URL, WordPress delivers a 404 (Not Found) page.

Page ViewExample URL
Home Page (page 1)http://example.com/
Home Page (page 2, 3, …)http://example.com/?paged=2
Single Posthttp://example.com/?p=1
Single Pagehttp://example.com/?page_id=2
Tag Archivehttp://example.com/?tag=test
Category Archivehttp://example.com/?cat=1
Author Archivehttp://example.com/?author=1
Date Archive (year)http://example.com/?m=2014
Date Archive (year/month)http://example.com/?m=201404
Date Archive (year/month/day)http://example.com/?m=20140407
RSS Feedhttp://example.com/?feed=rss2
404 ( Not Found)http://example.com/dfgdfgdfgdg
Search Resultshttp://example.com/?s=searchterm

 

Every theme displays something for each type of page view. What that “something” is depends on the requested page and which template files are included in the theme. For example, if an author-archive view is requested, WordPress checks the theme to see which template files are available to display the page. In general, the process goes like this:

  • WordPress first checks for an author-specific template to generate the requested page.
  • If an author-specific template is not found, WordPress checks for an archive-specific template.
  • Lastly, if no archive-specific template is found, WordPress generates the page using the default template.

This process of using the most specific template to display the requested page is referred to as the Template Hierarchy.

The Template Hierarchy

WordPress’ Template Hierarchy refers to the order in which template files are used to display various types of web pages. Each theme may have a completely different set of template files, but WordPress will always use the Template Hierarchy to determine which files are used to render the requested page. WordPress themes may include as many or as few different template files as needed depending on the design. For example, a complex theme may include a distinct template for each type of Page View, while a minimal theme may use only two or three templates to handle all of them. It’s entirely up to the theme designer to determine which templates are necessary based on the Template Hierarchy.

Before getting into theme template files, let’s take a look at a simplified overview of Template Hierarchy shown on this page. There we see how the hierarchy applies to different page views. For example, when a single-post view is requested, WordPress first checks for a template file named single.php. If that’s not available, WordPress generates the page based on index.php, which is one of the two default template files required for any theme.

To build the simplest possible theme, we need include only two files:

  • php
  • css

Here is an overview of the Template Hierarchy. It includes the most commonly used template files and their associated Page Views.

Page View                 Theme Template
 if existselseelse
Home Page (posts)home.php index.php
Home Page (page)front-page.php index.php
Single Postsingle.phpsingular.phpindex.php
Single Pagepage.phpsingular.phpindex.php
404 ( Not Found)404.php index.php
Tag Archivetag.phparchive.phpindex.php
Category Archivecategory.phparchive.phpindex.php
Author Archiveauthor.phparchive.phpindex.php
Date Archivearchive.phparchive.phpindex.php
Search Resultssearch.phparchive.phpindex.php

This simplified look at the hierarchy shows some of the most commonly seen template files and their associated page views. Also common, but not shown in the chart, are custom template files for posts, pages, and archives. For example, you can create a custom page template for your “About” page by creating a file named page-about.php.

WordPress theme development tutorial for beginners

Exploring Template Files

Good themes may vary wildly in terms of which files are included and which code is used, but they all employ the same general principles and should adhere as closely as possible to the WordPress API. So while the number of contents of various template files may vary, most themes share enough common elements to make possible a “tour” of template files included in the typical theme. As we look at each file, we’ll consider its general purpose, structure, and contents. Along the way, we’ll examine some key techniques for modifying and enhancing default functionality. While staying focused on the code, we’ll also look at how each template file is displayed on the site’s front-end, highlighting cool features and seeing how it all fits together. In this chapter, we’ll be exploring the following template files:

  • author.php
  • category.php
  • date.php
  • search.php
  • tag.php
  • 404.php
  • style.css
  • index.php
  • header.php
  • sidebar.php
  • footer.php
  • single.php
  • page.php
  • comments.php
  • functions.php
  • content.php
  • searchform.php
  • front-page.php
  • home.php
  • archive.php
  • attachment.php

style.css

style.css is the theme’s main stylesheet. It is required for a theme to be recognized by WordPress, and it must always be included in the theme’s root directory. Further, style.css must always include a proper file header. The style.css header includes the theme’s name, description, author, license, and other essential information.

A typical style.css header looks like this:

/*

Theme Name: General Theme

Theme URI: http://example.com/general-theme/

Author: Theme Designer Author URI: http://example.com/

Description: A hypothethetical, generalized typical theme

Version: 1.0 License: GNU General Public License v2 or later

License URI: http://www.gnu.org/licenses/gpl-2.0.html

Tags: black, white, light, dark, two-columns

Text Domain: generaltheme

*/

The header is included as an inline comment. When writing CSS, inline comments give designers a place to include non-CSS content in the stylesheet. So, for example:

/* this is a single-line inline comment */

In general, the style.css header should include as much information as possible, however, technically, only the Theme Name is required in order for WordPress to recognize and utilize the theme. Other header information is optional, but should be included if the theme is to be made available to the public. We’ll learn more about this in Chapter 10, where we learn how to prepare our themes for public use. For the sake of completeness, here is a quick explanation of each line in the style.css header, along with any relevant details.

  •  Theme URI – the online location of the theme’s homepage
  •  Author – the name of the author, should be the author name, or the author’s username if registered at wordpress.org
  •  Author URI – the online location of the author’s homepage
  •  Description – a clear, succinct description of the theme
  •  Version – the version number of the theme
  •  License – the name/summary/link of the theme license
  •  License URI – the online location of the license
  •  Tags – tags used to describe the theme, also used to help users find new themes when searching the WP Theme Directory
  •  Text Domain – the theme’s text domain, used in localization and theme translation
  •  Domain Path – the path to the theme’s localization files, relative to the themes directory

index.php

This file contains the codes that make up the structure of the WordPress theme. This is the main template file. It acts as the reserve template when other templates are unavailable. This is the file where the WordPress loop is added.

header.php

This file contains the elements of a website like a document type, language attribute, website title, charset, metas and various links and more. It also contains the theme’s “header” and “main menu”. This file must have the wp_head() action hook because it is used for WordPress functions and plugins. This file is called by the index.php file and other templates by using the get_header() function.

footer.php

As by name, this file contains the footer part of your theme which includes links, theme info, and footer widgets if any. This file is called by the index.php file and other templates by using the get_footer() function.

comments.php

it usually outputs the comment form and individual comments. This file can be called by adding comments_template() on templates that display single post or page.

WordPress Theme Development

To put things into perspective, WordPress theme development is just a small part of the much larger field of dynamic web development. Anything that can be built with regular coding languages like PHP, HTML, JavaScript, and CSS, also can be built with WordPress. Of course, the more you know about these languages the easier it’s going to be to learn how to build WordPress themes, but there is no need to be an expert in everything. In this chapter, you will see how easy it is to get started with WordPress theme development, and learn the principles and methodology needed to go further.

Before diving in, it’s important to know exactly where we’re at and where we’re going. At this point in the book, we have a solid understanding of WordPress theme structure and how template files are used to display content on the front-end. We’re now ready to apply this information and build some amazing themes.

To get there, we’ll need to stay focused on the theme itself, without veering too far into any particular aspect of web design. It would be easy to get sidetracked on, say, the graphic design aspect of web design. Or spend an entire chapter on the latest UX/UI wizadry.

Step 1: Create a folder to hold your theme files

If we are going to be building themes, we need to know where the files that make up a WordPress theme live in a WordPress Installation. This is pretty easy. We know that a WordPress installation typically has a root directory named wordpress. Here is what our root directory looks like in PHP Storm.

This directory contains the following files and folders:

Files

  • composer.json
  • index.php
  • license.txt
  • readme.html
  • wp-activate.php
  • wp-blog-header.php
  • wp-comments-post.php
  • wp-config.php
  • wp-config-sample.php
  • wp-cron.php
  • wp-links-opml.php
  • wp-load.php
  • wp-login.php
  • wp-mail.php
  • wp-settings.php
  • wp-signup.php
  • wp-trackback.php
  • xmlrpc.php

Folders

  • wp-admin
  • wp-content
  • wp-includes

The folder that we are most interested in right now is the wp-contentfolder. Within the wp-content folder is a folder named themes. Do you know what this folder is for? Yep, that’s right! It is the folder that holds one or more themes that you would like to use with your WordPress website. In this themes folder we find three additional folders of twentyfifteentwentysixteen, and twentyseventeen. These folders contain the three themes that WordPress ships with by default. Notice below that there is also a folder named general-theme.  Here general-theme is user defined folder name. This folder will contain all of the theme files. Go ahead and create that folder as well in your installation as this is where we will be creating our WordPress theme from scratch.

Step 2: Create style.css and index.php in your custom theme folder

We now know where WordPress theme files are in the file system. We also have created a new folder named general-theme in our themes folder. We are now going to create two empty files in this directory. One is called index.php and the other is called style.css.

Let us now populate these files with the bare minimum we need to get a new theme going in WordPress.

style.css

WordPress actually reads the comments that you place in the style.css file. This is where you specify specific information about the theme you are building.

/*

Theme Name: General Theme

Theme URI: http://example.com/general-theme/

Author: Theme Designer Author URI: http://example.com/

Description: A hypothethetical, generalized typical theme

Version: 1.0 License: GNU General Public License v2 or later

License URI: http://www.gnu.org/licenses/gpl-2.0.html

Tags: black, white, light, dark, two-columns

Text Domain: generaltheme

*/

Index.php

The main template file. This is the most generic template file in a WordPress theme and one of the two required files for a theme (the other being style.css). It is used to display a page when nothing more specific matches a query. For example, it puts together the home page when no home.php file exists.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<?php

 

get_header();

 

if ( have_posts() ) :

            while ( have_posts() ) : the_post(); ?>

 

        <h2><a href=”<?php the_permalink() ?>”><?php the_title() ?></a></h2>

                        <?php the_content() ?>

           

            <?php endwhile;

 

else :

            echo<p>There are no posts!</p>‘;

 

endif;

 

get_footer();

 

?>

Step 3: Add a Header and Footer To The Custom Theme

The Title and the Post Content are central to creating a good theme. Almost as important is having a header and footer section of your theme. These sections would contain the content that is always visible on all pages of the website. These sections are above and below the post content. To do this, you guessed it, we will make use of special functions provided by WordPress to get the header and to get the footer. Do you see a pattern starting to develop yet? Almost anything you can do as a theme developer in WordPress has already been done for you by way of these custom functions. So you will find that it pays to memorize these commonly used functions in WordPress theme development. Let’s go ahead and add the get_header() and get_footer() functions to our theme file now.

header.php

Let’s describe the content of the header.php. The first code to enter is:

<!DOCTYPE html>

<html <?php language_attributes( )?>>

<head>

<meta charset=<?php bloginfo( ‘charset’ ); ?>” />

<title><?php wp_title( ‘|’, true, ‘right’ )?></title>

<link rel=”pingback” href=<?php bloginfo( ‘pingback_url’ );  ?>” />

<?php wp_head()?>

</head>

Always make sure to add wp_head(); just before the closing . That action hook is needed to add and modify the contents of the header.php.

After thecodes, the next part is the header where you will display site name and site description.

Note that the termsand Header are different things. Theis a part of the HTML document that is not visible on the browser and handles the site’s Title, Favicon, Meta, Stylesheets, Scripts and a lot more. The header is the part of a theme that is displayed at the very top where the Name and Description of the website are usually shown. It may also show a custom header image but that’s for another tutorial. For now, we’ll focus on the very basics of creating a WordPress theme.

Now, we add container tag and opening body. Then we add the header title and description.

<body <?php body_class()?>>
<div id="container">
    <div id="header">
        <div id="header-info-wrap">
            <h1 id="site-title"><?php bloginfo( 'name' )?></h1>
            <h4 id="site-description"><?php bloginfo( 'description' )?></h4>
        </div>
    </div>

Now add The Menu on the header.php

<nav id ="nav">
    <?php 
        wp_nav_menu( array(
            'theme_location' =>  'menu-primary',
            'container'      =>  false,
            'fallback_cb'    =>  'wp_page_menu
        ) );
    ?> </nav>

Step 4: Add Code to Output The Post Title and Post Text

The WordPress Loop

The WordPress loop is responsible for showing the website’s content Posts and Pages.

It is via this loop, that theme developers check for posts and display them on the page as needed. If follows the format as follows. If the database has posts, let’s loop over them while there are still posts, otherwise we will let the user know there are no posts. It looks like this in PHP code.

<?php get_header()?>

<?php if ( have_posts() ) : while have_posts() ) : the_post()?>
    <div id="post-<?php the_ID()?><?php post_class()?>>
<h1> <a href="<?php the permalink()?>"><?php the_title()?></a> </h1>
     <?php the_content(); ?>
    </div>
<?php endwhileelse: ?>
    <h2> Post or Page not Found! </h2>
<?php endif?>

Notice that The Loop makes use of two functions in it’s most basic form. Those are have_posts() and the_post(). The have_posts() function does only one thing. It tells you if there are any posts in the database to loop over. This function will return either true or false and that is it. If it returns true, then there are posts available to loop over. If it returns false, then there are no posts to loop over. The other function, the_post() does not return anything. It’s job is to get WordPress ready to output posts. Specifically, it retrieves the next post, sets up the post, sets the in_the_loop property to true. So far, our page will still not output any information about our blog posts, but we can update that now in our index.php file.

We have  made use of two WordPress functions:

  • the_title() 
  •  the_content()

Most often, these functions are used inside the loop and what they do is to fetch the title and the content of each post as the loop iterates over each one in the database. So as the loop runs, it will come across the first post. At that time the_title() function will output the title of the post to the page and the_content() will output the body of that post to the page. On the next loop these functions will again fetch the next title and content and output them to the page. So with these in place, we should now see some information about our posts getting sent to the screen.

What about linking to each individual post so that we can view a post on it’s own rather than as part of just the homepage. We can do that quite easily, again with special functions that WordPress provides. For this task, we can make use of the the_permalink() function.

Summary of all loop functions:

the_title() – Gets the title of the post or page. It accepts three arguments: HTML tag for before the title (named $before), HTML tag for after the title (named $after), and $echo, the actual title itself.

the_time() – Gets the date the post or page was pub­lished. The only argument it accepts is the format in which that the date should be printed. These arguments are the same ones that the PHP date function31 accepts. The default is whatever is listed in the WordPress admin panel, under Settings 􀁹 General

the_content() – This will display the content of the post or page (that is, whatever you entered as the body text in the visual editor of the WordPress admin. It accepts two argu­ments, the first of which tells WordPress what to display as  the “more” text. I’ll talk about this at length in the next sec­tion. The second argument, called $stripteaser is simply documented as a Boolean that will, “Strip teaser content before the “more” text.”32 There are no examples on the Codex, and there is some debate over what it actually does.

the_excerpt() – Gets the first 55 words of the post, appending “ […] ” to the end of the string. It accepts no argu­ments. Both the number of words and the ellipsis (…) can be changed within the functions file using filters.

the_category() – Gets the category (or categories) in which the posts are listed. For arguments, it accepts: the separator, which will be printed in between categories, how to handle displaying parent categories, and the post ID, which of course defaults to the current post.

the_tags() – Gets the tags added to the post. The argu­ments it accepts are: what to print before the list of tags, how to separate each tag, and what to print after each tag.

the_permalink() – Gets the post’s or page’s URL in the format defined by the WordPress admin panel in Settings 􀁹 Permalinks.

Step 5: Add a functions.php file to your theme

At this point, we have four files in our custom theme. Those are index.phpstyle.cssheader.php, and footer.php. Probably the next most important file we need to have is the functions.php file.

The functions.php file in WordPress does many things for your theme. It is the file where you place code to modify the default behavior of WordPress. You can almost think of the functions.php as a form of a plugin for WordPress with a few key points to remember:

  • Does not require unique Header text
  • Stored in the folder that holds your theme files
  • Executes only when in the currently activated theme’s directory
  • Applies only to the current theme
  • Can call PHP functions, WordPress functions, or custom functions

One thing we need badly in our theme is some better styling! Let’s create a function in our functions.php file to include the style.css file into our theme. Here is how we can achieve that goal.

<?php
 
function custom_theme_assets() {
wp_enqueue_style( style, get_stylesheet_uri() );
}
 
add_action( wp_enqueue_scripts, custom_theme_assets );

This piece of code will include, or make active, the stylesheet of our custom theme.

Register the Dynamic Sidebar

Place the following code in the functions.php file:

<?php
    register_sidebar(array(
        'name'  =>  'sidebar' ,
        'id'  =>  'sidebar_widgets' ,
        'before_widget'  =>  '<div id="%1$s">',
        'after_widget'  =>  '</div>' ,
        'before_title'  =>  '<h4>' ,
        'after_title'  => '</h4>' , )
    );
?>

Registering a New Navigation Menu Area

// Register main navigation menu
function themetechs_register_menu( ) {
    register_nav_menu( 'main-nav', 'Main Nav' );
}
add_action( 'init', 'themetechs_register_menu' );

This block uses a WordPress function, register_nav_menu(), to register a new navigation menu. This function call is wrapped in another function which we’ve written: themetechs_register_menu(). To do the registering, we use the WordPress function add_action() to hook themetechs_register_menu() onto a WordPress action hook called init.

Don’t worry too much about that terminology right now: we’ll explain it in WordPress Hooks, Actions, and Filters: What They Do and How They Work. What it means is that themetechs_register_menu() will now run every time WordPress runs its init process—which WordPress does near the beginning of every webpage load.

So we’re able to make themetechs_register_menu() run toward the beginning of every webpage load. And what does themetechs_register_menu() do? It uses register_nav_menu()to register—cause WordPress to know about—a new navigation menu area titled “Main Nav.”

Step-6:   Setting Up Customizer 

The Customize API is object-oriented. There are four main types of Customizer objects: Panels, Sections, Settings, and Controls. Settings associate UI elements (controls) with settings that are saved in the database. Sections are UI containers for controls, to improve their organization. Panels are containers for sections, allowing multiple sections to be grouped together.

Each Customizer object is represented by a PHP class, and all of the objects are managed by the Customize Manager object, WP_Customize_Manager.

WordPress theme development tutorial for beginners

To add, remove, or modify any Customizer object, and to access the Customizer Manager, use the customize_register hook:

function themeslug_customize_register( $wp_customize ) {
  // Do stuff with $wp_customize, the WP_Customize_Manager object.
}
add_action( 'customize_register', 'themeslug_customize_register' );

The Customizer Manager provides add_, get_, and remove_ methods for each Customizer object type; each works with an id. The get_ methods allow for direct modification of parameters specified when adding a control.

add_action('customize_register','my_customize_register');
function my_customize_register( $wp_customize ) {
  $wp_customize->add_panel();
  $wp_customize->get_panel();
  $wp_customize->remove_panel();
 
  $wp_customize->add_section();
  $wp_customize->get_section();
  $wp_customize->remove_section();
 
  $wp_customize->add_setting();
  $wp_customize->get_setting();
  $wp_customize->remove_setting();

Settings

Settings handle live-previewing, saving, and sanitization of your customizer objects. Each setting is managed by a control object. There are several parameters available when adding a new setting:

$wp_customize->add_setting( 'setting_id', array(
  'type' => 'theme_mod', // or 'option'
  'capability' => 'edit_theme_options',
  'theme_supports' => '', // Rarely needed.
  'default' => '',
  'transport' => 'refresh', // or postMessage
  'sanitize_callback' => '',
  'sanitize_js_callback' => '', // Basically to_json.
) );

There are two primary types of settings: options and theme modifications. Options are stored directly in the wp_options table of the WordPress database and apply to the site regardless of the active theme. Themes should rarely if ever add settings of the option type. Theme mods, on the other hand, are specific to a particular theme. Most theme options should be theme_mods. For example, a custom CSS plugin could register a custom theme css setting as a theme_mod, allowing each theme to have a unique set of CSS rules without losing the CSS when switching themes then switching back.

$wp_customize->add_setting( 'setting_id', array(
  'type' => 'theme_mod',
  
) );
$wp_customize->add_setting( 'setting_id', array(
  'type' => 'option',
  
) );

It is usually most important to set the default value of the setting as well as its sanitization callback, which will ensure that no unsafe data is stored in the database. Typical theme usage:

$wp_customize->add_setting( 'accent_color', array(
  'default' => '#f72525',
  'sanitize_callback' => 'sanitize_hex_color',
) );
 
//typical plugins use:
 
$wp_customize->add_setting( 'myplugin_options[color]', array(
  'type' => 'option',
  'capability' => 'manage_options',
  'default' => '#ff2525',
  'sanitize_callback' => 'sanitize_hex_color',
) );

Note that the Customizer can handle options stored as keyed arrays for settings using the option type. This allows multiple settings to be stored in a single option that isn’t a theme mod. To retrieve and use the values of your Customizer options, use get_theme_mod() and get_option() with the setting id:

function my_custom_css_output() {
  echo '<style type="text/css" id="custom-theme-css">' .
  get_theme_mod( 'custom_theme_css', '' ) . '</style>';
  echo '<style type="text/css" id="custom-plugin-css">' .
  get_option( 'custom_plugin_css', '' ) . '</style>';
}
add_action( 'wp_head', 'my_custom_css_output');

Note that the second argument for get_theme_mod() and get_option() is the default value, which should match the default you set when adding the setting.

Controls

Controls are the primary Customizer object for creating UI.  Specifically, every control must be associated with a setting, and that setting will save user-entered data from the control to the database (in addition to displaying it in the live-preview and sanitizing it). Controls can be added by the Customizer Manager and provide a robust set of UI options with minimal effort:

$wp_customize->add_control( 'setting_id', array(
  'type' => 'date',
  'priority' => 10, // Within the section.
  'section' => 'colors', // Required, core or custom.
  'label' => __( 'Date' ),
  'description' => __( 'This is a date control with a red border.' ),
  'input_attrs' => array(
    'class' => 'my-custom-class-for-js',
    'style' => 'border: 1px solid #900',
    'placeholder' => __( 'mm/dd/yyyy' ),
  ),
  'active_callback' => 'is_front_page',
) );

The most important parameter when adding a control is its type — this determines what type of UI the Customizer will display. Core provides several built-in control types:

  • <input> elements with any allowed type (see below)
  • checkbox
  • textarea
  • radio (pass a keyed array of values => labels to the choices argument)
  • select (pass a keyed array of values => labels to the choices argument)
  • dropdown-pages (use the allow_addition argument to allow users to add new pages from the control)

For any input type supported by the html input element, simply pass the type attribute value to the type parameter when adding the control. This allows support for control types such as texthiddennumberrangeurltelemailsearchtimedatedatetime, and week, pending browser support.

Controls must be added to a section before they will be displayed (and sections must contain controls to be displayed). This is done by specifying the section parameter when adding the control. Here is an example for adding a basic textarea control:

$wp_customize->add_control( 'custom_theme_css', array(
  'label' => __( 'Custom Theme CSS' ),
  'type' => 'textarea',
  'section' => 'custom_css',
) );

And here’s an example of a basic range (slider) control. Note that most browsers will not display the numeric value of this control because the range input type is designed for settings where the exact value is unimportant. If the numeric value is important, consider using the number type. The input_attrs parameter will map a keyed array of attributes => values to attributes on the input element, and can be used for purposes ranging from placeholder text to data- JavaScript-referenced data in custom scripts. For number and range controls, it allows us to set the minimum, maximum, and step values.

$wp_customize->add_control( 'setting_id', array(
  'type' => 'range',
  'section' => 'title_tagline',
  'label' => __( 'Range' ),
  'description' => __( 'This is the range control description.' ),
  'input_attrs' => array(
    'min' => 0,
    'max' => 10,
    'step' => 2,
  ),
) );

Core Custom Controls

If none of the basic control types suit your needs, you can easily create and add custom controls. Custom controls are explained more fully later in this post, but they are essentially subclasses of the base WP_Customize_Control object that allow any arbitrary html markup and functionality that you might need. Core features several built-in custom controls that allow developers to implement rich JavaScript-driven features with ease. A color picker control can be added as follows:

$wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'color_control', array(
  'label' => __( 'Accent Color', 'theme_textdomain' ),
  'section' => 'media',
) ) );

WordPress 4.1 and 4.2 also added support for any type of multimedia content, with the Media control. The media control implements the native WordPress media manager, allowing users to select files from their library or upload new ones. By specifying the mime_type parameter when adding the control, you can instruct the media library show to a specific type such as images or audio:

$wp_customize->add_control( new WP_Customize_Media_Control( $wp_customize, 'image_control', array(
  'label' => __( 'Featured Home Page Image', 'theme_textdomain' ),
  'section' => 'media',
  'mime_type' => 'image',
) ) );
 
$wp_customize->add_control( new WP_Customize_Media_Control( $wp_customize, 'audio_control', array(
  'label' => _( 'Featured Home Page Recording', 'theme_textdomain' ),
  'section' => 'media',
  'mime_type' => 'audio',
) ) );

Note that settings associated with WP_Customize_Media_Control save the associated attachment ID, while all other media-related controls (children of WP_Customize_Upload_Control) save the media file URL to the setting. More information is available on Make WordPress Core.

Additionally, WordPress 4.3 introduced the WP_Customize_Cropped_Image_Control, which provides an interface for cropping an image after selecting it. This is useful for instances where a particular aspect ratio is needed.

Sections

Sections are UI containers for Customizer controls. While you can add custom controls to the core sections, if you have more than a few options you may want to add one or more custom sections. Use the add_section method of the WP_Customize_Manager object to add a new section:

$wp_customize->add_section( 'custom_css', array(
  'title' => __( 'Custom CSS' ),
  'description' => __( 'Add custom CSS here' ),
  'panel' => '', // Not typically needed.
  'priority' => 160,
  'capability' => 'edit_theme_options',
  'theme_supports' => '', // Rarely needed.
) );

You only need to include fields that you want to override the default values of. For example, the default priority (order of appearance) is typically acceptable, and most sections shouldn’t require descriptive text if your options are self-explanatory. If you do want to change the location of your custom section, the priorities of the core sections are below:

Title

ID

Priority

Site Title & Tagline

title_tagline

20

Colors

colors

40

Header Image

header_image

60

Background Image

background_image

80

In most cases, sections can be added with only one or two parameters being specified. Here’s an example for adding a section for options that pertain to a theme’s footer:

// Add a footer/copyright information section.
$wp_customize->add_section( 'footer' , array(
  'title' => __( 'Footer', 'themename' ),
  'priority' => 105, // Before Widgets.
) );

Panels

The Customizer Panels API was introduced in WordPress 4.0, and allows developers to create an additional layer of hierarchy beyond controls and sections. More than simply grouping sections of controls, panels are designed to provide distinct contexts for the Customizer, such as Customizing Widgets, Menus, or perhaps in the future, editing posts. There is an important technical distinction between the section and panel objects.

Themes should not register their own panels in most cases. Sections do not need to be nested under a panel, and each section should generally contain multiple controls. Controls should also be added to the Sections that core provides, such as adding color options to the colors Section. Also make sure that your options are as streamlined and efficient as possible; see the WordPress philosophy. Panels are designed as contexts for entire features such as Widgets, Menus, or Posts, not as wrappers for generic sections. If you absolutely must use Panels, you’ll find that the API is nearly identical to that for Sections:

$wp_customize->add_panel( 'menus', array(
  'title' => __( 'Menus' ),
  'description' => $description, // Include html tags such as <p>.
  'priority' => 160, // Mixed with top-level-section hierarchy.
) );
$wp_customize->add_section( $section_id , array(
  'title' => $menu->name,
  'panel' => 'menus',
) );

Panels must contain at least one Section, which must contain at least one Control, to be displayed. As you can see in the above example, Sections can be added to Panels similarly to how Controls are added to Sections. However, unlike with controls, if the Panel parameter is empty when registering a Section, it will be displayed on the main, top-level Customizer context, as most sections should not be contained with a panel.

Custom Controls, Sections, and Panels

Custom Controls, Sections, and Panels can be easily created by subclassing the PHP objects associated with each Customizer object: WP_Customize_ControlWP_Customize_Section, and WP_Customize_Panel (this can also be done for WP_Customize_Setting, but custom settings are typically better implemented using custom setting types, as outlined in the next section). Here’s an example for a basic custom control:

class WP_New_Menu_Customize_Control extends WP_Customize_Control {
  public $type = 'new_menu';
  /**
  * Render the control's content.
  */
  public function render_content() {
  ?>
    <button class="button button-primary" id="create-new-menu-submit" tabindex="0"><?php _e( 'Create Menu' ); ?></button>
  <?php
  }
}

By subclassing the base control class, you can override any functionality with custom functionality or use the core functionality depending on your needs. The most common function to override is render_content() as it allows you to create custom UI from scratch with HTML. Custom Controls should be used with caution, however, as they may introduce UI that is inconsistent with the surrounding core UI and cause confusion for users. Custom Customizer objects can be added similarly to how default controls, sections, and panels are added:

$wp_customize->add_control(
  new WP_Customize_Color_Control(
    $wp_customize, // WP_Customize_Manager
    'accent_color', // Setting id
    array( // Args, including any custom ones.
      'label' => __( 'Accent Color' ),
      'section' => 'colors',
    )
  )
);

Parameters passed when adding controls are mapped to class variables within the control class, so you can add and use custom ones where certain parts of your custom object are different across different instances.

When creating custom Controls, Sections, or Panels, it is strongly recommended to reference the core code, in order to fully understand the available functionality that can be overridden. Core also includes examples of custom objects of each type. This can be found in wp-includes/class-wp-customize-control.php, wp-includes/class-wp-customize-section.php, and wp-includes/class-wp-customize-panel.php. There is also a JavaScript API for each Customizer object type, which can be extended with custom objects; see the Customizer JavaScript API section for more details.

Customizer UI Standards 

Custom customizer controls, sections, and panels should match core UI practices wherever possible. This includes relying on standards from wp-admin, such as using the .button and .button-primary classes, for example. There are also a few standards specific to the customizer (as of WordPress 4.7):

  • White background colors are used only to indicate navigation and actionable items (such as inputs)
  • The general #eee background color provides visual contrast against the white elements
  • 1px #ddd borders separate navigational elements from background margins and from each other
  • 15px of spacing is provided between elements where visual separation is desired
  • 4px borders are used on one side of a navigation element to show hover or focus, with a color of #0073aa
  • Customizer text uses color: #555d66, with #0073aa for hover and focus states on navigation elements

Custom Setting Types 

By default, the Customizer supports saving settings as options or theme modifications. But this behavior can be easily overwritten to manually save and preview settings outside of the wp_options table of the WordPress database, or to apply other custom handling. To get started, specify a type other than option or theme_mod when adding your setting (you can use pretty much any string):

$wp_customize->add_setting( $nav_menu_setting_id, array(
  'type' => 'nav_menu',
  'default' => $item_ids,
) );

The setting will no longer be saved or previewed when its value is changed in the associated control. Now, you can use the customize_update_$setting->type andcustomize_preview_$setting->type actions to implement custom saving and previewing functionality. Here is an example for saving a menu item’s order property from the Menu Customizer project (the value of the setting is an ordered array of menu ids):

function menu_customizer_update_nav_menu( $value, $setting ) {
  $menu_id = str_replace( 'nav_menu_', '', $setting->id );
  // ...
  $i = 0;
  foreach( $value as $item_id ) { // $value is ordered array of item ids.
    menu_customizer_update_menu_item_order( $menu_id, $item_id, $i );
  $i++;
  }
}
add_action( 'customize_update_nav_menu', 'menu_customizer_update_nav_menu', 10, 2 );