Chapter 4. Themes

WordPress themes drive the frontend of your web app. In Chapter 1, we presented the analogy that WordPress themes are like views in a traditional MVC framework. The analogy isn’t perfect, but themes and views are similar in that they both control the way your app will look and are where your designers will spend most of their time.

The Theme Developer Handbook put together by the WordPress community is the definitive source for learning how to build themes for WordPress in a standards-based way. All theme developers should use that resource. This chapter covers areas of theme development especially important to app developers.

Themes Versus Plugins

At some level, all source files in your themes and plugins are just .php files loaded at different times by WordPress. In theory, your entire app code could reside in one theme or one plugin. In practice, you’ll want to reserve your theme for code related to the frontend (views) of your website and use plugins for your app’s backend (models and controllers).

Where you decide to put some code will depend on whether you are primarily building a full app or an individual plugin or theme.

Where to Place Code When Developing Apps

If you are building a full web app—basically one WordPress installation—you will have full access to the site and what themes and plugins are installed. Your code could go anywhere. Even so, you should follow some thought process when deciding whether a particular feature should be coded as a module of your app’s plugin or theme or as a separate plugin. The main benefactor of your good planning at this step will be your developers (maybe just you). Properly organizing your code is going to make it easier for you to maintain your app and develop it further.

When building apps, we try to use the following guidelines:

  • Use one main plugin to store the core app code, and one theme to manage the frontend code.

  • Any modular functionality that could be useful on other projects or potentially replaced by another plugin should be coded as a separate plugin.

  • Never hack the core!1

So what is core app code and what is frontend code? Again our pseudo-MVC framework looks like this:

Plugins = models

All of your code-defining data structures, business logic, and Ajax services should go into the core plugin. Things like definitions for CPTs and taxonomies, form processing, and class wrappers for the Post and User classes should also go into your core plugin.

Themes = views

All of your templating code and frontend logic should go in your theme. The frame of your website, header, footer, menu, and sidebars should be coded in your theme. Simple logic like if(is_user_logged_in()) { //show menu } else { //show login } should go into your theme.

One thing to consider when deciding where to code features is your development team. If you have a designer and programmer, you should put things the designer will be concerned with in the theme, and things that concern the programmer in the core plugin. Even if you have to finagle a little bit, clearly separating things like that will make it easier for your developers to find what they are looking for.

When Developing Plugins

If you are building a plugin to be used on other websites or modular features that can be used across projects, it makes sense to keep your code within one plugin. In these cases, you can store template files within your plugin to handle the UI components. It is common practice to allow these files to be overwritten by the active WordPress theme, which will be covered later in this chapter.

Where to Place Code When Developing Themes

Similarly, if you are developing a theme that will be distributed, and that relies on CPTs or other customization that would typically be coded in a plugin, it might make sense to instead include that within your theme. If your users must activate a plugin before your theme works at all, you might as well move the plugin code into your theme. If your theme makes large underlying changes to WordPress, consider putting that plugin-like code into a parent theme and putting your design-related code into a child theme. That way if your users want to change their site’s design without losing the other functionality provided by a theme, they can do so more easily.

On the other hand, if the code you are about to add to your theme is not crucial to the theme’s workings or there are other plugins that could be used as alternatives for your code, you should move that code into a plugin and distribute your theme as a bundle that includes the themes and recommended plugins. As an example, many premium themes add SEO-related fields to the edit post page to manage page titles, meta descriptions, and meta keywords. This makes sense, since these SEO-related fields represent a kind of view that is seen by Google and other web crawlers. However, there are a few really popular plugins that do this same functionality, and it’s hard to argue that your theme wouldn’t work without the SEO functionality installed. We would recommend theme developers put their SEO functionality into plugins or otherwise make it easy to disable so other plugins can be used.

In the end, the decision as to where to put what code and how to package things should be based on your users, both end users as well as the developers who will use your themes and plugins. Part of the beauty of WordPress is its flexibility in terms of how you can go about customizing it. There are no strict rules. Consider everything you read about this topic (including from us) as guidelines. If moving code from a plugin file to a theme file makes it easier to work with, do it.

The Template Hierarchy

When a user visits your site and navigates to a page, WordPress uses a system called the template hierarchy to determine which file in the active theme should be used to render the page. For example, if the user browses to a single-post page, WordPress will look for single-post.php. If this isn’t found, it will look for single.php. If that’s not found, it will look for index.php.

The index.php file is the fallback for all page loads, and with style.css, is the only required file for your theme. Typically, you’ll have a list of files like the following:

  • 404.php

  • author.php

  • archive.php

  • attachment.php

  • category.php

  • comments.php

  • date.php

  • footer.php

  • front-page.php

  • functions.php

  • header.php

  • home.php

  • image.php

  • index.php

  • page.php

  • search.php

  • sidebar.php

  • single.php

  • single-(post-type).php

  • style.css

  • tag.php

  • taxonomy.php

Some files in this list are loaded when you call a specific get function. For example, get_header() loads header.php, get_footer() loads footer.php, and get_sidebar() loads sidebar.php. Passing a name parameter to these functions will add it to the filename loaded; so, for example, get_header('alternate'); will load header-alternate.php from the theme folder.

The function comments_template() loads comments.php unless you pass a different filename as the first parameter.

The function get_search_form() looks for the file searchform.php in your theme folder or outputs the default WordPress search form if no file is found.

The WordPress template hierarchy documentation clearly lays out all the various files WordPress will look for in a theme folder when they are loaded. You can also check out the Twenty Nineteen Theme or another well-coded theme to see what filenames will be detected by WordPress. Read the comments in those themes to see when each page is loaded.

When developing apps with custom post types, it’s common to want to use a different template when viewing your post types on the frontend. You can override the single post and archive view for your post types by adding files with the names single-<post_type>.php and archive-<post_type>.php, where post_type is set to the value used when the post type was registered.

Page Templates

One of the easiest ways to get arbitrary PHP code running on a WordPress website is to build a page template into your theme and then use that template on one of your pages.

Some common templates found in WordPress themes include contact forms and landing page forms.

Sample Page Template

Example 4-1 is a pared-down version of a contact form template that you can drop into your theme’s folder.

Example 4-1. Sample page template
<?php
/*
Template Name: Page - Contact Form
*/

//get values possibly submitted by form
$email = sanitize_email( $_POST['email'] );
$cname = sanitize_text_field( $_POST['cname'] );
$phone = sanitize_text_field( $_POST['phone'] );
$message = sanitize_text_field( $_POST['message'] );
$sendemail = !empty( $_POST['sendemail'] );

// form submitted?
if ( !empty( $sendemail )
    && !empty( $cname )
	&& !empty( $email )
	&& empty( $lname ) ) {

	$mailto = get_bloginfo( 'admin_email' );
	$mailsubj = "Contact Form Submission from " . get_bloginfo( 'name' );
	$mailhead = "From: " . $cname . " <" . $email . ">\n";
	$mailbody = "Name: " . $cname . "\n\n";
	$mailbody .= "Email: $email\n\n";
	$mailbody .= "Phone: $phone\n\n";
	$mailbody .= "Message:\n" . $message;

	// send email to us
	wp_mail( $mailto, $mailsubj, $mailbody, $mailhead );

	// set message for this page and clear vars
	$msg = "Your message has been sent.";

	$email = "";
	$cname = "";
	$phone = "";
	$message = "";
}
elseif ( !empty( $sendemail ) && !is_email( $email ) )
	$msg = "Please enter a valid email address.";
elseif ( !empty( $lname ) )
	$msg = "Are you a spammer?";
elseif ( !empty( $sendemail ) && empty( $cname ) )
	$msg = "Please enter your name.";
elseif ( !empty( $sendemail ) && !empty( $cname ) && empty( $email ) )
	$msg = "Please enter your email address.";

// get the header
get_header();
?>
<div id="wrapper">
 <div id="content">
 <?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
  <h1><?php the_title(); ?></h1>
  <?php if ( !empty( $msg ) ) { ?>
   <div class="message"><?php echo $msg?></div>
  <?php } ?>
  <form class="general" action="<?php the_permalink(); ?>" method="post">
   <div class="form-row">
	<label for="cname">Name</label>
	<input type="text" name="cname" value="<?php echo esc_attr($cname);?>"/>
	<small class="red">* Required</small>
   </div>
   <div class="hidden">
	<label for="lname">Last Name</label>
	<input type="text" name="lname" value="<?php echo esc_attr($lname);?>"/>
	<small class="red">LEAVE THIS FIELD BLANK</small>
   </div>
   <div class="form-row">
	<label for="email">Email</label>
	<input type="text" name="email" value="<?php echo esc_attr($email);?>"/>
	<small class="red">* Required</small>
   </div>
   <div class="form-row">
	<label for="phone">Phone</label>
	<input type="text" name="phone" value="<?php echo esc_attr($phone);?>"/>
   </div>
   <div class="form-row">
	<label for="message">Question or Comment</label>
	<textarea class="textarea" id="message" name="message" rows="4" cols="55">
		<?php echo esc_textarea( $message )?>
	</textarea>
   </div>

   <div class="form-row">
	<label for="sendemail">&nbsp;</label>
	<input type="submit" id="sendemail" name="sendemail" value="Submit"/>
   </div>
  </form>
 <?php endwhile; endif; ?>
 </div>
</div>
<?php
// get the footer
get_footer();
?>

WordPress will scan all of the .php files in your active theme’s folder and subfolders (and the parent theme’s folder and subfolders) for templates. Any files found with a comment that includes the phrase Template Name: in it will then be made available as a template.

The template is loaded after the WordPress init and wp actions have already fired. The theme header and the wp_head action will not load until you call get_header() in your template. So you can use the top of your template file to process form input and potentially redirect before any headers are sent to the page.

Your template file will need to include the same HTML markup as your theme’s page.php or single-post template. In the preceding example, we include a wrapper <div> and content <div> around the content of the contact form.

The preceding code also uses the sanitize_text_field() and sanitize_email() functions to clean up values submitted by the form. Similarly, it uses the esc_attr() and esc_textarea() functions to prevent cross-site scripting attacks. These functions are covered more in Chapter 8.

The preceding contact form also incorporates a “honey pot.” A field called lname would be hidden using CSS, so normal users would not see this field and thus leave it blank when submitting the form. Bots looking to take advantage of your contact form to send you spam will see the lname field and will put some value into it. The code that processes the form checks to make sure that the lname field is blank before then sending out the email. Like as a honey pot draws in insects, the hidden lname field draws spammers into it so you don’t end up sending email on their behalf.

Using Hooks to Copy Templates

If you’d rather not change multiple template files when you update the ID or class names of your wrapper <div>s, you can create a template that uses the the_content filter or another action specific to your theme to place content into the main content area of your page. Then you can load another template file, like the core page.php template, which will include calls to load your site’s frame and default layout. Example 4-2 shows how to create a page template that loads the page.php template and adds further content below it on certain pages.

Example 4-2. Hooking template
<?php
/*
    Template Name: Hooking Template Example
*/

//use the default page template
require_once(dirname(__FILE__) . "/page.php");

//now add content using a function called during the the_content hook
function template_content($content)
{
    //get the current post in this loop
    global $post;

    //get the post object for the current page
    $queried_object = get_queried_object();

    //we don't want to filter posts that aren't the main post
    if(empty($queried_object) || $queried_object->ID != $post->ID)
        return $content;

    //capture output
    ob_start();
    ?>
    <p>This content will show up under the page content.</p>
    <?php
    $temp_content = ob_get_contents();
    ob_end_clean();

    //append and return template content
    return $content . $temp_content;
}
add_action("the_content", "template_content");

In this previous example, we do a little trick to check the current $post against the $queried_object. Typically, the global $post will be the main post of the page you have navigated to. However, other loops on your page will temporarily set the global $post to whatever post they are dealing with at the time. For example, if your template uses a WordPress menu, that is really a loop through posts of type menu. Many sidebars and footer sections will loop through other sets of posts.

The get_queried_object() function returns the main post object for the current page. The function returns a different but appropriate object if you are on a term or author page. The function returns null on archive pages. Because the previous example is a page template that will be loaded only on single page views, calling get_queried_object() there will always return a $post object for the current page.

You can also insert your own hook into your page.php and other core templates to do something similar. Just add something like do_action('my_template_hook'); at the point in your page template where you’d like to add in extra content.

When Should You Use a Theme Template?

In Chapter 3, we covered how to use shortcodes to create pages for your plugins. The shortcodes are useful because they allow you to add CMS-managed content above and below the shortcode in the post content field and to keep your code organized within your plugin. So, if you are distributing a plugin and need that page template to go along with it, you should use the shortcode method to generate your page.

Similarly, if you are distributing a theme by itself, you’ll need to include any templates needed for the theme within the theme folder. You could include code for shortcode-based templates within your theme, but templates are a more standard way of templating a page.

And finally, if your template needs to alter the HTML of your default page layouts, you will want to use a template file inside of your theme. Example 4-2 piggybacks on the page.php template to avoid having to rewrite the wrapping HTML. But if the whole point of the template is to rewrite the wrapping HTML (e.g., with a landing page template where you want to hide the default header, footer, and menu), you definitely need to use a template.

Theme-Related WordPress Functions

Next, we will discuss get_template_part( $slug,$name = null ). Here, the get_template_part() function can be used to load other .php files (template parts) into a file in your theme.

According to the Codex, $slug refers to “the slug name for the generic template,” and $name refers to “the name of the specialized template.” In reality, both parameters are simply concatenated with a dash to form the filename looked for: slug-name.php.

The Twenty Twelve theme uses get_template_part() to load a specific post format “content” part into the WordPress loop:

<?php /* Start the Loop */ ?>
<?php while ( have_posts() ) : the_post(); ?>
	<?php get_template_part( 'content', get_post_format() ); ?>
<?php endwhile; ?>

If your template part is in a subfolder of your theme, add the folder name to the front of the slug:

get_template_part('templates/content', 'page');

The get_template_part() function uses the locate_template() function of WordPress to find the template part specified, which then loads the file using the load_template() function. locate_template() first searches within the child theme. If no matching file is found in the child theme, the parent theme is searched.

Besides searching both the child and parent themes for a file, the other benefit to using get_template_part() over a standard PHP include or require call is that a set of WordPress global variables is set up before the file is included. The following example is the source for the load_template() function as of WordPress 4.9.7,2 showing the global variables that are set. Notice that the query_vars array is also extracted into the local scope. The query_vars $s is given special attention and escaped here since it’s a common vector for XSS attacks, and many themes forget to escape the search query when rendering it into the search field:

<?php
function load_template( $_template_file, $require_once = true ) {
	global $posts, $post, $wp_did_header, $wp_query, $wp_rewrite;
	global $wpdb, $wp_version, $wp, $id, $comment, $user_ID;

	if ( is_array( $wp_query->query_vars ) )
		extract( $wp_query->query_vars, EXTR_SKIP );

  if ( isset( $s ) )
		$s = esc_attr( $s );

	if ( $require_once )
		require_once( $_template_file );
	else
		require( $_template_file );
}
?>

Using locate_template in Your Plugins

A common design pattern used in plugins is to include templates in your plugin folder and allow users to override those templates by adding their own versions to the active theme. For example, in SchoolPress, teachers can invite students to their class. The invite form is stored in a template within the plugin:

//schoolpress/templates/invite-students.php
?>
<p>Enter</p>
<form action="" method="post">
	<label for="email">Email:</label>
<input type="text" id="email" name="email" value="" />
	<input type="submit" name="invite" value="Invite Student" />
</form>

SchoolPress is envisioned as an SaaS application, but we also plan to release a plugin version for others to use on their own sites. Users of the plugin may want to override the default template without editing the core plugin, since edits to the core plugin would be overwritten when the plugin was upgraded.

To enable our plugin users to override the invite template, we use code like the following when including the template file:

//schoolpress/shortcodes/invite-students.php
function sp_invite_students_shortcode($atts, $content=null, $code="")
{
	//start output buffering
  ob_start();

	//look for an invite-students template part in the active theme
	$template = locate_template('schoolpress/templates/invite-students.php');

	//if not found, use the default
	if(empty($template))
		$template = dirname(__FILE__) .
            '/../templates/invite-students.php';

	//load the template
	load_template($template);

	//get content from buffer and output it
	$temp_content = ob_get_contents();
	ob_end_clean();
	return $temp_content;
}
add_shortcode('invite-students', 'sp_invite_students_shortcode');

This code uses our shortcode template from Chapter 3. But instead of embedding the HTML into the shortcode function, we load it from a template file. We first use locate_template() to search for the template in the active child and parent themes. Then, if no file is found, we set $template to the path of the default template bundled with the plugin. The template is loaded using load_template().

Style.css

The style.css file of your theme must contain a comment used by WordPress to track the theme’s version and other information to show in the WordPress dashboard. Here is the comment from the top of style.css in the Twenty Nineteen theme:

/*
Theme Name: Twenty Nineteen
Theme URI: https://wordpress.org/themes/twentynineteen/
Author: the WordPress team
Author URI: https://wordpress.org/
Description: Our 2019 default theme is designed to show off the power of the
block editor. It features custom styles for all the default blocks, and is
built so that what you see in the editor looks like what you'll see on your
website. Twenty Nineteen is designed to be adaptable to a wide range of
websites, whether you’re running a photo blog, launching a new business, or
supporting a non-profit. Featuring ample whitespace and modern sans-serif
headlines paired with classic serif body text, it's built to be beautiful on
all screen sizes.
Requires at least: WordPress 4.9.6
Version: 1.4
License: GNU General Public License v2 or later
License URI: LICENSE
Text Domain: twentynineteen
Tags: one-column, flexible-header, accessibility-ready,
custom-colors, custom-menu, custom-logo, editor-style,
featured-images, footer-widgets, rtl-language-support,
sticky-post, threaded-comments, translation-ready
This theme, like WordPress, is licensed under the GPL.
Use it to make something cool, have fun, and share what
you've learned with others.
Twenty Nineteen is based on Underscores https://underscores.me/,
(C) 2012-2018 Automattic, Inc.
Underscores is distributed under the terms of the GNU GPL v2 or later.
Normalizing styles have been helped along thanks to the fine work of
Nicolas Gallagher and Jonathan Neal https://necolas.github.io/normalize.css/
*/

The style.css file of the active theme (and parent theme if applicable) is automatically enqueued by WordPress.

Versioning Your Theme’s CSS Files

It’s good practice to set a version for your CSS files when loading them through wp_enqueue_style(). This way, if you update your CSS, you can also update the version, and avoid having site users see a seemingly broken site that uses a version of the stylesheet cached by the browser.

When WordPress enqueues your theme’s style.css file for you, it uses the overall WordPress version when loading the stylesheet. The line output in your site’s head tag will look like this:

<link rel='stylesheet'
	id='twentynineteen-style-css'
	href='.../wp-content/themes/twentynineteen/style.css?ver=1.4'
	type='text/css'
	media='all' />

Updates to the stylesheet, your app’s version number, or even the version number set in the style.css comment won’t update the version added to the stylesheet when enqueued. It will always match the WordPress version number.

One solution is to remove all CSS from your style.css file into other CSS files in your theme and load those CSS files through wp_enqueue_style() calls in the theme’s functions.php file. It would look like this for style.css:

/*
   Theme Name: SchoolPress
   Version: 1.0

   That's it! All CSS can be found in the "css" folder of the theme.
*/

and like this for functions.php:

<?php
define( 'SCHOOLPRESS_VERSION', '1.0' );
function sp_enqueue_theme_styles() {
	if ( !is_admin() ) {
		wp_enqueue_style( 'schoolpress-theme',
			get_stylesheet_directory_uri() . '/css/main.css',
			NULL,
			SCHOOLPRESS_VERSION
		);
	}
}
add_action( 'init', 'sp_enqueue_theme_styles' );
?>

A constant like SCHOOLPRESS_VERSION would typically be defined in our main plugin file, but it’s included here for clarity. The preceding code loads our new /css/main.css file with the main app version appended so new versions of the app won’t conflict with browser-cached stylesheets.

There is another way to change the version of the main style.css file without moving it to another file entirely. We use the wp_default_styles filter. This filter passes an object containing the default values used when a stylesheet is enqeued. One of those values is the default_version, which can be changed like so:

define('SCHOOLPRESS_VERSION', '1.0');
function sp_wp_default_styles($styles)
{
    //use release version for stylesheets
	$styles->default_version = SCHOOLPRESS_VERSION;
}
add_action("wp_default_styles", "sp_wp_default_styles");

Now our main stylesheet will be loaded using the SchoolPress app version instead of the main WordPress version. We can keep our CSS in style.css if we want to, though it’s often a good idea to move at least some parts of the CSS into separate files in a “css” folder of your theme.

functions.php

The functions.php file of your active theme (and parent theme if applicable) is loaded every time WordPress loads. For this reason, the functions.php file is a popular place to add little hacks and other random bits of code. On a typical WordPress site, the functions.php file can quickly become a mess.

However, we’re developing a well-planned WordPress app, and our function.php files don’t need to be a mess. Just like we break up the core functions of our main app plugin into smaller includes, you should do the same with your theme’s functions.php. You could add files similar to the following to your theme’s folder:

/includes/functions.php

Where you really place helper functions.

/includes/settings.php

For code related to theme settings and options.

/includes/sidebars.php

To define sidebars/widget areas.

Additionally, make sure that code you are adding to your theme’s functions.php is related to the frontend display of your site. Code that applies to the WordPress dashboard, backend processing for your app, or your entire app in general should most likely be added somewhere within the main app plugin.

Themes and CPTs

As CPTs are just posts, by default, your CPTs will be rendered using the single.php template, or index.php if no single.php template is available. You can also add a file to your theme of the form single-<post_type>.php, where <post_type> is the slug of your CPT. If available, this file is used to render the single-post view of that post type.

Similarly, you can add an archive-<post_type>.php file to render the archive view of your CPT if the has_archive flag is set in the CPT definition. We cover CPTs (including specifying templates for them) in more detail in Chapter 5.

Popular Theme Frameworks

When building apps with WordPress, there are many theme frameworks—both WordPress-specific frameworks and general-purpose HTML/CSS frameworks—you can use. Whether you intend to use the theme framework to build a quick proof of concept or to use it as a core component of your custom-built theme, using a theme framework can save you a lot of time.

We briefly cover some common theme frameworks and dive deeper into how to use two of the most popular ones for WordPress app development.

But first, what does a theme framework provide?

WordPress Theme Frameworks

WordPress theme frameworks are themes that are meant to be used as parent themes or starter themes to jump-start your frontend development. Theme frameworks typically include basic styles and layouts for blog posts, as well as archives, pages, sidebars, and menus. They are of varying weights, and some include CSS classes, shortcodes, and other handy bits of code to help you create new layouts and add UI elements to your pages. All frameworks will likely save you a lot of time.

There are two reasons to choose one theme framework over another: you either choose a child theme that visually looks very close to your vision for your app, or you choose a framework coded in a way that feels right when you work with it.

_s (underscores)

The starter theme _s (pronounced “underscores”) is published by Automattic, and has all the common components you need in a WordPress theme. Unlike most other frameworks, _s is not meant to be used as a parent theme, but as a starting point for your own parent theme. Most themes developed by Automattic for WordPress.com are based on the _s theme.

To use _s, download the code and change the directory name and all references to _s with the name of your theme. You can find good instructions for doing this in the project’s README file or, even better, a tool to automatically do it for you on the underscores website.

The stylesheet in _s is very minimal with no real styling, just a bit of code for layout and some common readability and usability settings. _s is best for designers who are able to and who want to build their own theme from scratch. It’s basically code you would have to write somehow for your theme yourself. The _s code is not abstracted as heavily as some other theme frameworks, and so using the framework should be easier to pick up for designers more familiar with HTML and CSS than PHP.

Memberlite

Memberlite is a theme written by Jason Coleman and Kimberly Coleman of Stranger Studios. This theme was built with membership sites in mind, but can be used on a wide variety of sites. The “lite” part of the title describes the theme’s lightweight (technically and aesthetically) design.

Memberlite is designed responsively to fit on screens of varying sizes. It has templates and sections for everything a modern website needs. It has a couple of companion plugins (Memberlite Elements and Memberlite Shortcodes), with tools to add finer control over sidebars, banners, page layouts, and other aspects of your website.

Memberlite is best for designer-developers, and is really our choice for starting themes based on its balance of framework support on the design and coding side of theme development.

Genesis

Genesis is a theme framework developed by StudioPress and used in more than 40 child themes published by StudioPress and in many more themes published by third-party designers. The Genesis theme is meant to be used as a parent theme. StudioPress has child themes that are appropriate across a number of business and website types. Or you can create your own child theme that inherits from Genesis.

The Genesis framework abstracts the underlying HTML and CSS more than the other frameworks listed here. We find this makes it a little harder to work with when doing larger customizations. However, Genesis would be a good choice if you find one of their child themes is 80% of the way toward the look you want or if you find their framework easier to work with than other options.

Non-WordPress Theme Frameworks

In addition to WordPress theme frameworks, there are also application UI frameworks that provide markup, stylesheets, and images for common UI patterns and elements. Some popular UI frameworks include Twitter’s Bootstrap and Zurb’s Foundation.

Incorporating a UI framework into your theme can be as easy as copying a few files into the theme folder and enqueueing the stylesheets and JavaScript. This gives you easy access to styled UI elements like buttons, tabs, pagination, breadcrumbs, labels, alerts, and progress bars.

Next, we’ll cover how to add Bootstrap assets into a Memberlite child theme, but the same process should work for other combinations of WordPress themes and UI frameworks.

Creating a Child Theme for Memberlite

To create your theme, you’ll need to follow these steps:

  1. Create a new folder in your wp-content/themes folder. Then, give it the name memberlite-child.

  2. Create a style.css file in the memberlite-child folder.

  3. Paste the following into your style.css file:

    /*
    THEME NAME: Memberlite Child
    THEME URI: http://bwawwp.com/wp-content/themes/memberlite-child/
    DESCRIPTION: Memberlite Child Theme
    VERSION: 0.1
    AUTHOR: Jason Coleman
    AUTHOR Uri: http://bwawwp.com
    TAGS: memberlite, child, tag
    TEMPLATE: memberlite
    */
    @import url("../memberlite/style.css");

    The key field in the comment is the TEMPLATE field, which needs to match the folder of the parent theme, in this case memberlite. The only required file for a child theme is style.css. So at this point, you’ve created a child theme.

    You can either copy all the CSS from the parent theme’s style.css into the child theme’s style.css and edit what you want to, or use @import_url like we’ve done here to import the rules from the parent theme’s stylesheet and add more rules below to override the parent theme’s styles.

    To enqueue the bootstrap files, you will also need a functions.php file.

  4. Create an empty functions.php file in the memberlite-child folder for now.

Including Bootstrap in Your App’s Theme

In general, importing Bootstrap into the Memberlite theme is kind of silly compared to finding a theme based on Bootstrap or just copying in the CSS rules you need. However, importing frameworks and libraries into your theme is something you might run into. The following will give you an idea of how to go about importing other libraries and frameworks into your theme.

Download the Bootstrap ZIP file into your memberlite-child folder. After unzipping it, you will have a dist folder containing the CSS and JavaScript files for Bootstrap. You can rename this folder to bootstrap and delete the Bootstrap ZIP file. Your child theme folder should now look like this:

  • memberlite-child

    • bootstrap

      • css

      • js

    • functions.php

    • style.css

Now, we’ll enqueue the Bootstrap CSS and JavaScript by adding this code into the functions.php file inside your child theme:

<?php
function memberlite_child_init() {
	wp_enqueue_style(
		'bootstrap',
		get_stylesheet_directory_uri() .
            '/bootstrap/css/bootstrap.min.css',
		'style',
		'3.0'
	);
	wp_enqueue_script(
		'bootstrap',
		get_stylesheet_directory_uri() .
            '/bootstrap/js/bootstrap.min.js',
		'jquery',
		'3.0'
	);
}
add_action( 'init', 'memberlite_child_init' );
?>

Note that we set the dependencies for the Bootstrap CSS to style, which ensures that the Bootstrap stylesheet loads after the Memberlite stylesheet. We also set the Bootstrap JavaScript to depend on jquery and set the version of both files to 3.0 to match the version of Bootstrap used.

At this point you could use any of your favorite Bootstrap styles or JavaScript in your WordPress theme. Many of the Bootstrap styles for columns and layout aren’t being used in the Memberlite markup (Memberlite has its own layout system), and so they won’t be applicable to your theme. But the styles for form elements and buttons would be useful for app developers.

Menus

Menus are an important part of most apps, and apps often have special needs for their menus that other websites don’t have. Some apps have multiple menus. Many mobile apps have a main navigational menu at the top and a toolbar-like menu along the bottom. Some apps have dynamic menus. Many apps have different menus or menu items for logged-in users than for logged-out users. Menu items can be based on a user’s membership level or admin capabilities.

Before we get into how to build more complicated menus and navigational elements with WordPress, let’s cover the standard way to add a menu to your theme.

Navigation Menus

Since WordPress version 3.0, the standard method for adding navigation menus to themes involved registering the menu in the theme’s code, designating where in the theme the menu will appear, and then managing the menu through the WordPress dashboard.

The main benefit to using WordPress’s built-in menu functionality is that end users can control the content of their menus using the dashboard GUI. Even if you are a developer with full control over your app, it is still a good idea to use the built-in menus in WordPress since you may have stakeholders who would want to manage menus or you may want to distribute your theme to others in the future. The WordPress navigation menus are also very easy to reposition and can take advantage of other code using menu-related hooks or CSS styles.

To register a new navigational menu, use the register_nav_menu( $location, $description ) function. The $location parameter is a unique slug used to identify the menu. The $description parameter is a longer title for the menu shown in the drop-down in my menu tool in the dashboard:

register_nav_menu('main', 'Main Menu');

You can also register many menus at once using the register_nav_menus() (with an s) variant. This function accepts an array of locations in which the keys are the $location slugs and the values are the $description titles:

register_nav_menus(array(
	'main' => 'Main',
	'logged-in' => 'Logged-In'
));

To place a navigational menu into your theme, use the wp_nav_menu() function:

wp_nav_menu( array('theme_location' => 'main' ));

The theme_location parameter should be set to the $location set with register_nav_menu(). The wp_nav_menu() function can take many other parameters to change the behavior and markup of the menu. This WordPress Codex page on navigation menus is a good resource as the various parameters to the wp_nav_menu() function and other ways to customize menus. We cover some of our favorite recipes in the following sections.

Dynamic Menus

There are two main methods to make your WordPress menus dynamic so that different menu items show up on different pages or under different circumstances. The first is to set up two menus and load a different menu depending on the case. Here is a code example from the Codex showing how to display a different menu to logged-in users and logged-out users:

if ( is_user_logged_in() ) {
     wp_nav_menu( array( 'theme_location' => 'logged-in-menu' ) );
} else {
     wp_nav_menu( array( 'theme_location' => 'logged-out-menu' ) );
}

The other way to make your menu dynamic is to use the nav_menu_css_class filter to add extra CSS classes to specific menu items. Then you can use CSS to hide/show certain menu items based on their CSS class.

Say you want to remove a login link from a menu when you’re on the login page.3 You could use code like this:

function remove_login_link($classes, $item)
{
	if(is_page('login') && $item->title == 'Login')
    $classes[] = 'hide';	//hide this item

  return $classes;
}
add_filter('nav_menu_css_class', 'sp_nav_menu_css_class', 10, 2);

You can also customize the markup of your menus by using Custom Walker classes (see Chapter 7).

Responsive Design

We could write another whole book about responsive design. Luckily for us, many people already have, including Clarissa Peterson, who wrote Learning Responsive Web Design (O’Reilly). The general concept behind responsive design is to detect properties of the client device and to adjust your app’s layout, design, and functionality to work best for that device. Now, let’s get into a few different techniques for doing this.

Device and Display Detection in CSS

Media queries are the main method of device detection in CSS They’re used in stylesheets or added as a property of the <link> tag used to embed a stylesheet to limit the scope of the stylesheet’s CSS rules to a specific media type or case in which a particular media feature is available. Mozilla does a good job explaining media queries, and listing the various properties and operators you can use to construct a media query.

A common use of media queries is to hide certain elements and adjust font and element sizes when someone is printing. You would specify that media query in a <link> tag, inside a stylesheet, and through a wp_enqueue_style call, as follows:

<link rel="stylesheet" media="print" href="example.css" />

<style>
@media print
{
	.hide-from-print {display: none;}
	.show-when-printing {display: auto;}
}
</style>

<?php
	wp_enqueue_style('example', 'example.css', NULL, '1.0', 'print');
?>

A more typical example in the responsive design world is to check for a min-width and/or max-width in the media query to adjust styles as the screen becomes smaller or larger. The following is an example from the Bootstrap responsive stylesheet that adjusts CSS rules for screens between 768 and 979 pixels (the current width of a typical browser window on a monitor). Sizes greater than 979 pixels could be considered extra wide:

@media (min-width: 768px) and (max-width: 979px) {
  .hidden-desktop {
    display: inherit !important;
  }
  .visible-desktop {
    display: none !important ;
  }
  .visible-tablet {
    display: inherit !important;
  }
  .hidden-tablet {
    display: none !important;
  }
}

Another common task handled with media queries is to change styles, and specifically swap images, when a browser has a Retina high-resolution screen.4

Here is a mix of media queries used in some WordPress dashboard CSS for detecting a high-resolution display. The queries test against pixel ratio and DPI. Values vary among displays, but most standard-definition displays will have a 1:1 pixel ratio and 96 DPI. A Retina display has a pixel ratio of 2:1 and DPI of 196 or higher, but we can test for minimal values somewhere between standard definition and Retina-level definition to catch other high-resolution displays:

@media(-o-min-device-pixel-ratio: 5/4),                 /* Opera */
        (-webkit-min-device-pixel-ratio: 1.25),         /* Webkit */
        (min-resolution: 120dpi) {                      /* Others */
                /* add your high res CSS here */
        }

Media queries are powerful, and you can use them to make UIs that are very flexible. Browsers and CSS standards evolve constantly. It’s important to stay on top of things so current phones, tablets, and monitors show your app as you intended.

Which properties to look out for and how to adjust your stylesheet to accommodate them is outside the scope of this book, but hopefully you get the idea and understand how to incorporate media queries into your WordPress themes.

Device and Feature Detection in JavaScript

Your app’s JavaScript can also benefit from device and feature detection. jQuery offers methods to detect the window and screen sizes and other information about the browser. Many HTML5 features that may or may not be available in a certain browser can be tested before being put to use.

Detecting the screen and window size with JavaScript and jQuery

JavaScript makes the screen width and height available in the screen.width and screen.height properties. You can also use screen.availWidth and screen.availHeight to get the available width and height, which accounts for pixels taken up by toolbars and sidebar panels in the browser window.

If you are already using jQuery, you can use the width() method on any element on your page to get its width, but you can also use it on the $(document) and $(window) objects to get the width of the document and window, respectively. You can also use the height() property on the document and window objects and any element on your page.

The values for $(window).width() and $(window).height() should be the same as screen.availWidth and screen.availHeight. This is the available size of the browser viewport, minus any toolbars or sidebar panels, or, more accurately, how much room you have for displaying HTML.

The width and height of the $(document) will return the total scrollable width and height of your rendered web page. When using the width and height in your JavaScript code, you will often want to update things if the window size changes. This can happen if someone resizes a browser window on their desktop, rotates a phone from portrait to landscape, or does any number of things that could change the width or height of the window. jQuery offers an easy way to detect these changes so you can update your layout accordingly:

//bind an event to run when the window is resized
jQuery(window).resize(function() {
  width = jQuery(window).width();
  height = jQuery(window).height();
  //update your layout, etc
});

You can bind a resize event to any element, not just the full window. Elements on your page might grow and contract as a user interacts with your page, possibly adding elements through Ajax forms, dragging resizable elements on the screen, or otherwise moving things around.

Feature detection in JavaScript

When you’re building a modern app UI using HTML5 features, you will sometimes want to detect whether a certain HTML5 feature is unavailable so you can provide an alternative or fallback. Mark Pilgrim’s Dive into HTML5 has a good list of general methods for detecting HTML5 features:

  1. Check whether a certain property exists on a global object (such as window or navigator).

  2. Create an element, then check whether a certain property exists on that element.

  3. Create an element, check if a certain method exists on that element, and then call the method and check the value it returns.

  4. Create an element, set a property to a certain value, and then check whether the property has retained its value.

If you need to do only one such detection, some of the examples on the Dive into HTML5 site will give you ideas on how to roll your own bit of detection. If you need to do a lot of feature detection, a library like Modernizr.js will help.

To use Modernizr.js, first grab the version of the script you need from their site: Modernizr offers a tool that will ask you which parts of the script you need, and then generate a minimized .js file containing only those bits. Place this file in your theme or plugin folder and enqueue it:

<?php
function sp_wp_footer_modernizr() {
	wp_enqueue_script(
		'modernizr',
		get_stylesheet_directory_uri() . '/js/modernizr.min.js'
	);?>
	<script>
		//change search inputs to text if unsupported
		if(!Modernizr.inputtypes.search)
			jQuery('input[type=search]').attr('type', 'text');
	</script>
	<?php
}
add_action( 'wp_footer', 'sp_wp_footer_modernizr' );
?>

The Modernizr documentation contains a list of features detectable with Modernizr.js.

jQuery also provides a similar set of checks, limited to things that jQuery needs to check itself through the jQuery.support object. If a check you are trying to do is done by jQuery already, you can avoid the overhead of Modernizr.js by using the jQuery check. A list of feature flags set by jQuery.support can be found on their website:

jQuery(document).ready(function() {
	//only load AJAX code if AJAX is available
	if(jQuery.support.ajax)
	{
		//AJAX code goes here
	}
});

Device Detection in PHP

Device detection in PHP is based on the $_SERVER['HTTP_USER_AGENT'] global created by PHP. This value is set by the browser itself and so is definitely not standardized, often misleading, and potentially spoofed by web crawlers and other bots. It’s then best to avoid PHP-based browser detection if you can by making your code as standards based as possible and using the CSS and JavaScript methods described for feature detection.

If you want a general idea of the kind of browser accessing your app, the user agent string is the best we have. Here is a simple test script echoing the user agent string and an example of what one will look like:

<?php
echo $_SERVER['HTTP_USER_AGENT'];

/*
	Outputs something like:
	Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4)
	AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36
*/
?>

This user agent string includes useful information, but perhaps too much. There are no fewer than five different browser names in that string. So which browser is it? Mozilla, KHTML, Gecko, Chrome, or Safari? In this case, we were running Chrome on a MacBook Air running OS X.

Did we already mention that there is no standard for the user agent string browsers will send? Historically, browsers include the names of older browsers to basically say, “I can do everything this browser does, too.”

WebAIM contains a funny summary of the history of various user agent strings, including this bit explaining the pedigree of the Chrome browser:

And then Google built Chrome, and Chrome used Webkit, and it was like Safari, and wanted pages built for Safari, and so pretended to be Safari. And thus Chrome used WebKit, and pretended to be Safari, and WebKit pretended to be KHTML, and KHTML pretended to be Gecko, and all browsers pretended to be Mozilla, and Chrome called itself Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13, and the user agent string was a complete mess, and near useless, and everyone pretended to be everyone else, and confusion abounded.

Aaron Anderson

Browser detection in WordPress core

Luckily, WordPress has done a bit of the work behind parsing the user agent string and exposes some global variables and a couple of methods that cover the most common browser detection–related questions. The following globals are set by WordPress in wp-includes/vars.php:

  • $is_lynx

  • $is_gecko

  • $is_winIE

  • $is_macIE

  • $is_opera

  • $is_NS4

  • $is_safari

  • $is_chrome

  • $is_iphone

  • $is_IE

And for detecting certain servers, we have the following:

  • $is_apache

  • $is_IIS

  • $is_iis7

Finally, you can use the wp_is_mobile() function, which checks for the word mobile in the user agent string as well as a few common mobile browsers.

Here is a quick example showing how you might use these globals to load different scripts and CSS:

<?php
function sp_init_browser_hacks() {
	global $is_IE;
	if ( $is_IE ) {
		//check version and load CSS
		$user_agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
		if ( strpos( 'msie 6.', $user_agent ) !== false &&
			strpos( 'opera', $user_agent ) === false ) {
			wp_enqueue_style(
				'ie6-hacks',
				get_stylesheet_directory_uri() . '/css/ie6.css'
			);
		}
	}

	if ( wp_is_mobile() ) {
		//load our mobile CSS and JS
		wp_enqueue_style(
			'sp-mobile',
			get_stylesheet_directory_uri() . '/css/mobile.css'
		);
		wp_enqueue_script(
			'sp-mobile',
			get_stylesheet_directory_uri() . '/js/mobile.js'
		);
	}
}
add_action( 'init', 'sp_init_browser_hacks' );
?>

Browser detection with PHP’s get_browser()

PHP actually has a great function for browser detection built in: get_browser(). Here is a simple example calling get_browser() and displaying some typical results:

<?php
$browser = get_browser();
print_r($browser);

/*
	Would produce output like:

	stdClass Object (
[browser_name_regex] => §^mozilla/5\.0 \(.*intel mac os x.*\)
applewebkit/.* \(khtml, like gecko\).*chrome/28\..*safari/.*$§
[browser_name_pattern] => Mozilla/5.0 (*Intel Mac OS X*)
AppleWebKit/* (KHTML, like Gecko)*Chrome/28.*Safari/*
[parent] => Chrome 28.0
[platform] => MacOSX
[win32] =>
[comment] => Chrome 28.0
[browser] => Chrome
[version] => 28.0
[majorver] => 28
[minorver] => 0
[frames] => 1
[iframes] => 1
[tables] => 1
[cookies] => 1
[javascript] => 1
[javaapplets] => 1
[cssversion] => 3
[platform_version] => unknown
[alpha] =>
[beta] =>
[win16] =>
[win64] =>
[backgroundsounds] =>
[vbscript] =>
[activexcontrols] =>
[ismobiledevice] =>
[issyndicationreader] =>
[crawler] =>
[aolversion] => 0
)
*/

This is pretty amazing stuff! So why is this function last in the section on detecting a browser with PHP? The answer is that the get_browser() function is unavailable or out of date on most servers. For the function to give you useful information, or in most cases work at all, you need to download an up-to-date browscap.ini file and configure PHP to find it. If you are distributing your app, you’ll want to use a different method to detect browser capabilities. However, if you are running your own app on your own servers, get_browser() is fair game.

An up-to-date browscap.ini file can be found at the Browser Capabilities Project website. Make sure you get one of the files formatted for PHP. We recommend the lite_php_browscap.ini file, which is half the size but contains information on the most popular browsers.

Once you have the .ini file on your server, you’ll need to update your php.ini file to point to it. Your php.ini file probably has a line for browscap commented out. Uncomment it and make sure it’s pointing to the location of the .ini file you downloaded. It should look something like this:

[browscap]
browscap = /etc/lite_php_browscap.ini

Now restart your web server (Apache, Nginx, etc.) and get_browser() should be working.

Final Note on Browser Detection

We spent a lot of space here on browser detection, but in practice, you should only use it as a last resort. When a certain browser is giving you pain with a piece of design or functionality, it is tempting to try to detect it and code around it. However, if it’s possible to find another workaround that gets a similar result without singling out specific browsers, it’s usually better to go with that solution.

For one thing, as we’ve seen here, the user agent string has no standards, and you might need to regularly update your code to parse it to account for new browsers and browser versions.

Second, in some cases, a browser-specific issue is a symptom of a bigger problem in your code. There may be a way to simplify your design or functionality to work better across multiple browsers, devices, and screen sizes.

The goal with responsive design and programming is to build something that will be flexible enough to account for all of the various browsers and clients accessing your app, whether you know about them or not.

1 If you find that you must hack the core files to get something to work, first reconsider whether you really need to do this. If you do need to change a core WordPress file, add hooks instead, and submit those hooks as a patch to the next version of WordPress.

2 We added a line break and removed some curly braces so it would fit better in print.

3 You could check $_SERVER['PHP_SELF'] to see whether you’re on the wp-login.php page. In this example, we assume our login is on a WordPress page with the slug login.

4 Retina is Apple’s brand name for their high-resolution displays. However, you may find the term “retina” used in code comments and documentation to refer to any high-resolution display.

Get Building Web Apps with WordPress, 2nd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.