If you’re starting with a blank theme as described here, it will probably still have a lot of things you won’t need. These are the first things I do before actually starting theme and site development.
Format code. This is really optional and depends on what you consider more important, legibility and PHP PSR Standards or internal coding style. WordPress has it’s own style that does not follow PHP PSR’s, which is perfectly fine. It is a matter of personal nit-pickiness I prefer to follow PSR, which I discuss at length in PSR’s and Legibility.
We are constantly reading old code as part of the effort to write new code. … making it easy to read makes it easier to write.
Most developers can usually sort their way through lengthy code and determine its intent, but the more time you spend figuring out what the code is doing, the less time you spend actually coding. To make things go faster in the long run I go through the code, make it legible and easier to navigate so at a glance I can see exactly what it happening. My first step is to go through all theme files and format them consistently. *
Hook Out the Cruft. Another one of the awesome things about WordPress is the ability to hook out functionality without "hacking the core.**" Most of this code will be found in functions.php in the theme directory. First decide what you’re going to remove and what you’re going to leave in. For example, if pingback or WordPress emoji functionality is important to you, don’t hook them out.
Begin by loading some content in your pages, and viewing the source code. What needs to be there? What doesn’t? What changes would you like to see in how the code is output? What can you move into static CSS or Javascript files?
Separate Front End Resources. One of the most important concepts of SOLID is the "S," or SRP, the Single Responsibility Principle. Even though WordPress and theme coding is generally not object oriented, applying this one principle can improve your skills as a coder, make your code more legible, and make for much easier debugging by isolating functionality.
In WordPress the front end script and CSS loading is often lumped in with all the other functions in functions.php. The first thing I do is separate out the styles and scripts that load in the front end into another theme file. The styles and scripts queuing can get quite complex in some cases, and this logic is easily separated into it’s own area (and do this with multiple chunks of code in functions.php.) At the top of functions.php,
if(!defined("TEMPLATEPATH")){ define("TEMPLATEPATH", get_template_directory()); } require_once(TEMPLATEPATH . "/functions/styles-scripts.php");
The standard WordPress-y approach is to use get_template_directory() wherever it’s needed, but this can add up to a lot more function calls. By setting it as a constant it is now a static value and I reduce nanoseconds by not calling a function over and over.
At this point, the content of styles-scripts.php is pretty simple, two functions to load the CSS and scripts that I need and remove what I don’t. As of version 5.6, WordPress added (forced) the Gutenberg Block Editor on us for page editing. I’m one of the guys that doesn’t like it, so I’ve installed the Classic Editor plugin (at this point, the only active plugin) and hook out any scripts/css loaded for the block editor. I have three style sheets, one for font import, one for common styles, a responsive mobile sheet, and one small Javascript file that depends on jQuery.
<?php /** * Load styles for the front end. Note we have added .htaccess rules to nix the WP paths. * @return void */ function load_styles_front() { // Never going to use! wp_deregister_style('wp-block-library-css'); wp_dequeue_style('wp-block-library' ); wp_dequeue_style('wp-block-library-theme'); wp_dequeue_style('wc-block-style' ); // Font Stylesheet wp_deregister_style('font_stylesheet'); wp_register_style('font_stylesheet', get_template_directory_uri() . "/css/font.min.css"); wp_enqueue_style('font_stylesheet'); // Main Stylesheet wp_deregister_style('main_style'); wp_register_style('main_style', get_stylesheet_directory_uri() . "/css/style.min.css"); wp_enqueue_style('main_style'); // responsive - I'm using Mobile_Detect.php to set constant IS_MOBILE // Note if that constant does not get defined, it will always load. if (! defined('IS_MOBILE') || (defined('IS_MOBILE') && (IS_MOBILE === true))) { wp_deregister_style('responsive_style'); wp_register_style('responsive_style', get_stylesheet_directory_uri() . "/css/responsive.min.css"); wp_enqueue_style('responsive_style'); } } /** * Load scripts for the front end * @return void */ function load_scripts_front() { wp_deregister_script('wp-embed'); wp_deregister_script('jquery'); wp_register_script('jquery', '/wp-includes/js/jquery/jquery.js', false, null, true); wp_enqueue_script('jquery'); wp_deregister_script('common_js'); wp_register_script('common_js', site_url() . "/wp-content/themes/my-theme/js/common.min.js", ['jquery'], '1.0', true); wp_enqueue_script('common_js'); }
Truth be told, if you view source you will see I am not actually loading using get_stylesheet_directory_uri() or the hard coded paths above. I employ an .htaccess rewrite that allows me to eliminate the WordPress paths from the request.
Next, I build the theme setup function to hook out all the stuff I don’t need. You can do a web search for what each of these filters and hooks do, I only describe a couple because they’re a bit obscure.
<?php /** * This sets up the theme and hooks/filters out all the stuff I don't need. * @return void */ function my_theme_setup() { // See https://codex.wordpress.org/Content_Width global $content_width; if (! isset($content_width)) { $content_width = 1920; } load_theme_textdomain('my_theme', TEMPLATEPATH . '/languages'); add_theme_support('title-tag'); add_theme_support('post-thumbnails'); add_theme_support('html5', ['search-form']); // removes type=text/javascript but not single // quotes - see hack modify_css_js_tags below add_theme_support('html5', ['script', 'style']); // Stops auto formatting that breaks post/page shortcodes. remove_filter('the_content', 'wpautop'); remove_filter('the_excerpt', 'wpautop'); remove_action('wp_head', 'rsd_link'); remove_action ('wp_head', 'wp_generator'); remove_action('wp_head', 'wlwmanifest_link'); remove_action('wp_head', 'wp_shortlink_wp_head'); remove_action('wp_head', 'feed_links', 2); remove_action('wp_head', 'feed_links_extra', 3); remove_action('wp_head', 'rest_output_link_wp_head', 10); remove_action('wp_head', 'wp_oembed_add_discovery_links', 10); remove_action('wp_head', 'start_post_rel_link', 10); remove_action('wp_head', 'parent_post_rel_link', 10); remove_action('wp_head', 'adjacent_posts_rel_link_wp_head'); remove_action('wp_head', 'print_emoji_detection_script', 7); remove_action('admin_print_scripts', 'print_emoji_detection_script'); remove_action('wp_print_styles', 'print_emoji_styles'); remove_action('admin_print_styles', 'print_emoji_styles'); remove_filter('the_content_feed', 'wp_staticize_emoji'); remove_filter('comment_text_rss', 'wp_staticize_emoji'); remove_filter('wp_mail', 'wp_staticize_emoji_for_email'); // This removes the preload rel for emojis add_filter('emoji_svg_url', '__return_false'); // Stop WordPress from rewriting .htaccess rules add_filter('flush_rewrite_rules_hard','__return_false'); add_filter('excerpt_more', 'my_theme_read_more_link'); add_filter('max_srcset_image_width', function() { return 1; }); add_action('wp_head', 'my_theme_meta_headers', 1); add_action('wp_head', 'add_font_preload_links', 8); // because I strip off all full URL's, it breaks the canonical tag, //so put it back in with this add_action('wp_head', 'my_theme_canonical_tag'); add_action('pre_ping', 'disable_self_trackback'); // Strips off all full URL's, makes URL's domain -relative add_action('template_redirect', 'rw_relative_urls'); // Allows me to customize the title tag without plugins add_action('document_title_parts', 'filter_document_title_parts'); // Sheer obsessive nit-pickiness on these two, see next article add_action('script_loader_tag', 'modify_css_js_tags'); add_action('style_loader_tag', 'modify_css_js_tags'); add_filter('navigation_markup_template', 'my_theme_navigation_template'); add_action('wp_enqueue_scripts', 'load_styles_front'); add_action('wp_enqueue_scripts', 'load_scripts_front'); register_nav_menus([ 'main-menu' => esc_html__('Main Menu', 'my_theme'), 'bottom-menu' => esc_html__('Bottom Menu', 'my_theme'), ]); } add_action('after_setup_theme', 'my_theme_setup');
If you use a stock WordPress .htaccess without customized rules, do not use the flush_rewrite_rules_hard filter. Otherwise when WordPress updates, it will not update the .htaccess file.
There are several custom functions I’ve added in the add_action() calls. We will cover those in the next article, adding basic functionality.
* Of course you would never do this in an existing code base, unless you want to see your way out the door your first day. We are discussing a brand new install.
** "Hacking the core" means to go into the core system files – in this case anything in wp-admin or wp-includes – to change code to complete a task. This is a horrible idea in ANY framework because any future updates will overwrite whatever you change, and you’ll forget what you did.
- Return to WordPress
- Start With a Blank Theme
- Stripping the Cruft
- Add Basic Functionality
- Get Good With ShortCodes
- Summary