Why I developed my own Wordpress Theme engine
You can think of WordPress as blog engine that help non-technical peoples to design their own website without hassles using themes and plugin that are on the market.
But, there are also people like me, developers that hopefully know how to code, that love to customize it, to adapt it to the customer specific needs: to build quite amusing websites, not just blogs.
I’ve been through many WordPress themes in the latest years; i’ve learned the hard way that they may satisfy your urgent needs in the short time but in the long time they’re going to be hard to maintain.
Also, if you look at the source code of this themes you will panic. Almost 80–90% of the code is dedicated to support generic features that I will never need, or vendor’s marketing stuff (up-selling, cross-selling, newsletter, etc.) or, even worst, obfuscated code that will contain scary stuffs.
This also mean that your website is going to have a performance penalty due to the bigger source code, the many files the server has to read from the disks, the extra SQL queries that will slow down the database, etc.
Not to mention that all those themes and plugins inject extra CSS and JS files in your HTML page slowing down even more the overall loading time of the website when the customer browse it.
The “cure” that the market created is even more scary: more plugins! We now have plugins that introduce different kind of cache systems, that try to concatenate and minify CSS and JS files, etc. They are not strictly speaking a bad thing but I hope you understand that from my point of view its seems like curing a disease with the same disease.
So, the goals I tried to achieve developing my own theme engine are:
- remove all unwanted features from WP;
- provide, out-of-the-box, only a minimum viable product;
- allow to opt-in for any extra features;
- reduce CSS and JS at the minimum; avoid JQuery;
- provide built-in alternatives for the most common used plugins;
However, the main goal I wish to achieve, it is to have a common playground for all the projects and then just use a child theme to decide witch features to enable and witch do not. About this, my experience says that it is better to start with something simple and then just add, instead of trying to remove stuffs from a complex thing.
But lets see what I did in the past 10 days…
… its a very long list of self referential features, I know. But I did it to try explain you even better what are the features that I expect to have from WordPress and why commercial themes are not good for my own use case.
Unwanted Features
For speed, security and other motivations I worked hard to clean-up some unwanted features off from WordPress as much as possibile.
In the front-end area:
- cleaned up the HEAD tags from all the things not strictly required (feed links, rsd, wlw, generator, links, emojy, oEmbed, etc.);
- removed all the extra CSS (comment, default Gutenberg CSS, etc.) except the main style.css;
- blocked some XML RPC and pingbacks;
- removed the admin bar (it causes the loading of unwanted CSS e JS in the head); an “edit” link will appear in the bottom right corner of the page if you’re logged in;
- comments, breadcrumbs and post’s meta are hidden by default;
- the author’s archive and the attachment’s pages are disabled by default;
In the admin area:
- removed menus that are not needed;
- removed dashboard items that are not needed;
- hide the upgrade notice for non-admin users;
- custom TinyMCE button bars for the classic editor;
- removed some Gutenberg blocks that are not needed;
- all the default image sizes (thumbnail, medium, medium_large, large) are removed, only the “post-thumbnail” and the “full” size are left; its up to you to use add_size() to restore them or add new fancy image’s sizes.
MVP Features
Those are the main features that I wanted to be enabled by default inside the core theme engine.
In the front-end area:
- revisited loop — almost all the elements of the page template and the loop can be overridden by using a custom action or a template file;
- error handling — if anything bad happen then an HTTP 503 status is generated, this will prevent the search engine to index your page with error and they will return later;
header( 'HTTP/1.1 503 Service Temporarily Unavailable', true, 503 );
header( 'Retry-After: 3600' );
- site layout — you can choose the page layout from each different combination of the “content” plus 2 sidebars;
- lazy load CSS and JS — using bp_register_lazyload_style or bp_register_lazyload_script you can delay the loading of non-important files after the DOMContentLoaded event;
- headers and footer widgets — you can filter how many rows and cols of widgets you want in the header and footer: up to 3 rows and 4 columns, the widget’s content will adapt using flexbox CSS automatically; see this example:
bp_set_sidebars_header(2,2);
bp_set_sidebars_footer(2,2 );
The above example will generate those widgets.
- google fonts — by just adding the constant GOOGLE_FONT with the font’s url it will be lazy loaded and the meta tags for dns-prefetch and preconnect automatically generated;
<link rel="dns-prefetch" href="https://fonts.gstatic.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"/>
- google analytics — by just adding the constant GOOGLE_ANALYTICS with the GA’s ID then a snipped for loading it will be automatically included and the meta tags for dns-prefetch will be added automatically generated;
- redirect url — by adding a custom field called external_url the user will be redirected to that url when accessing the content;
- responsive video — videos are wrapped by a div with a CSS class that will make them responsive;
- preload or include main CSS — the main style.css can be included directly in the HEAD or preloaded using the new rel=”preload” syntax;
<link rel='preload' id='framework-style-css' href='/wp-content/themes/child/style.css' type='text/css' media='all' as='style' onload?='this.rel="stylesheet"' />
<noscript><link rel='stylesheet' id='framework-style-css' href='/wp-content/themes/child/style.css' type='text/css' media='all' /></noscript>
- archive page with content and grid style — an archive’s page will try to look as a normal page using the title and the description as content; the elements in the loop can be styled using pre-built grid CSS rules;
.wrap {
display: grid;
grid-template-columns: 2fr 1fr;
grid-template-rows: auto 1fr;
}
.entry-title {
grid-area: 1 / 1 / 2 / 2;
padding-right: .5rem;
}
.entry-image {
grid-area: 1 / 2 / 3 / 3;
}
.entry-summary {
grid-area: 2 / 1 / 3 / 2;
padding-right: .5rem;
}
In the admin area:
- support for custom logo, header and backgrounds using a widget or via the customizer;
- support for Gutenberg’s special align style: left, wide and full;
- custom login page — the login page will use the website’s own logo and will link to the homepage’s url instead of wordpress.com;
- media page — will display the info about the media dimensions;
- pages admin — a column with the featured image will be displayed and also another column with the “order” value; pages will be sorted automatically using the “order” column; in the page editor you will aso be able to edit “excerpt” and “tags” like posts;
- posts admin — will have an extra column with the featured image;
- image sizes selector — if you add custom size via add_image() you’ll be able to select them where appropriate;
- PDF overwrite — when uploading a new PDF file it will override the old one, with the same filename, instead of creating a new version;
- maximum image upload — when uploading a large image it will be automatically reduced to the default size of 1920 pixel;
Extra Features
There are more features available beside the one i mentioned above. Those are not included by default but can be automatically loaded via a filter or by calling “bp_$type_require(…)”.
Those are organized in different categories:
- lib & class — library that contains functions (or classes) that can be used by some extra features but not required inside the core;
- shortcode, metabox, widget, custom-types— typical WordPress ecosystem elements;
- feature — those files contains lots of action hooks, filters and recall other stuffs to achieve complex features;
Every category has its own special filter, or function, that a child theme can use to include or exclude them.
bp_add_features( array(
'nav-menu-walker',
'excerpt-readmore',
'comments',
'breadcrumbs',
'child-pages',
'social-share',
'related-post',
'genesis-navigation',
'comments-open',
'admin-page-sort-order'
) );
add_filter( 'bpf_preload_metaboxes', function ( $list ) {
$list[] = 'subtitle';
return $list;
} );
From all the once i developed i think its worth to mention those one:
- metabox and widget class helpers — those will help to create metaboxes and widgets easily: all the component I built uses those classes;
<?php
require_once BP_THEME_DIR . '/class/metabox.php';
require_once BP_THEME_DIR . '/class/field/textfield.php';
require_once BP_THEME_DIR . '/class/field/textareafield.php';
require_once BP_THEME_DIR . '/class/field/checkboxfield.php';
class BadPenguinSeoMetaBox extends BP_Class_MetaBox {
protected $mb_context = 'side';
function __construct() {
parent::__construct( 'badpenguin_seo_metabox', 'SEO' );
}
protected function setup() {
parent::setup();
$this->addField( 'BadPenguinTextField', 'seo_title' )->setLabel( 'Title' )->setAttribute( 'size', 80 );
$this->addField( 'BadPenguinTextareaField', 'seo_description' )->setLabel( 'DescriptionTitle' )->setAttribute( 'rows', 2 );
$this->addField( 'BadPenguinCheckboxField', 'seo_robots_noindex' )->setLabel( 'Disable robots' )->setContent( 'Hide from google search engine' );
}
}
// Bootstrap
new BadPenguinSeoMetaBox();
- navbar widget — allow to create a complete navbar with logo and mobile menù support (hambuger buttom + overlay menù) that works in pure CSS without JS;
- template shortcode — allow you to include any HTML template from the directory “parts”; if used programmatically via bp_get_template_part() (that replaces get_template_part) you can pass additional parameters to the template that can be reused inside of it;
- child pages — if a page has a hierarchy with children then, at the end of its content, a menù will appear with all of them; it can be used automatically or via a shortcode;
- embed github’s gists;
- genesis navigation next/prev — inspired by a plugin for genesis, this will add two big arrows at the center of the screen to navigate to the next or previous content; if you don’t like it there is another simpler version that will display the two links at the end of the content;
- medium content — if enabled, it will try mimic the aspect of a medium.com post. I’ve already developed a test example in past before this one: https://github.com/badpenguin/mimic-medium-typography ;
- page cover — the featured image will appear covering the whole viewport of the browser with title and subtitle, this is another extra metabox that will create a custom field that you can use to display it below the title;
- copyright — will block copy, right click and drag’n’drop of images since them are protected by copyright;
CSS Management
Having a light CSS is critical. I’ve used many CSS’s frameworks in the past since the born of the 12-columns grid systems: Twitter Bootstrap up to version 4, Foundation 4 and 5, UiKit (my favourite, check it at https://getuikit.com/ ); just to mentions only the most famous.
For those CSS frameworks the same problem I mentioned above about the WordPress commercial themes is still valid: they are huge because they need to support general purpose features.
They are no less then 70KB without counting their javascript dependency counterparts.
To avoid this I gathered all my SASS snippets and created my own customizable set of CSS rules: https://github.com/badpenguin/vanilla-css
The resulting style.css of my first website that I developed with my own custom theme was so little, 9KB (theme style + vanilla CSS), that I decided to include style.css directly in the HEAD of the page.
So, my theme’s main style.css is generated by style.scss that has some custom SASS and also include “some” of the feature from Vanilla’s CSS.
And if you develop a child theme its easier to include it via SASS directly and sets the SASS variables to customize colors and other stuffs.
JS without JQuery
The same as CSS is replicated on the javascript side. The first goal is to avoid JQuery and this is done by simple unregistering it and avoiding any script that will use it as dependency.
I developed https://github.com/badpenguin/vanilla-js to implement at least a minimal lightbox feature for gallery or single images. It has a core.js mandatory script with some DOM utilities that I use then the rest is totally optional.
Built-in plugins
So, using both Vanilla CSS and Vanilla JS i created custom features that try to resemble the core functionalities of some of the most common plugins that I’m forced to use:
- SEO by Yoast
- Image Watermark
- Lightbox
- Custom Related Post
- WP Social Sharing
and more.
SEO Functionalities
A SEO metabox will appear in the left Gutenberg ‘s sidebar. Here you can specify a custom title, description or a checkbox for disabling robots for that page.
Indexing is automatically disabled by default for attachments, tags and any other archive’s page except categories. You can use a filter to customize it.
If a featured image is present then the minimum Open Graph’s meta tags for Twitter, Facebook and Pinterest will be generated.
You an see the source code used to generate the above metabox in the previous chapter where i was talking about “lib and classes”.
Watermark Functionalities
Inspired by “Image Watermark”, this is a very simple plugin that can generate image with a text watermark, customizable using a filter. The original image is back-upped so it can be restored if you want to remove the watermark. Images that are too small (usually below 640 pixel) will not be watermarked by default.
Gallery Functionality
I’ve created a simple lightbox that will open a modal and navigate through one or more image depending on your configuration. There are arrows and a close button to click or you can navigate with the keyboard or swiping. A simple cross fade animation is used to change the slide.
You can see it in action here: https://visitaretenerife.it/quando-andare-a-tenerife/
Lazy Load Image Functionality
Every image in the site’s content and in the footer will be configured automatically. This plugin renames the src and a srcset attribute of the images to prevent the browser from loading them. Then, using a javascript intersectionObservable, the images will be lazy loaded. When entering the viewport the initial src and srcset will be restored and the image will appear.
With website with long content and lots of larges images this will speed up greatly the initial load of the page.
TOC — Table of Content
This is another typical situation where you can use 40 lines of code instead of big complex plugins. I use a shortcode to place the TOC in the preferred position, usually after a few paragraph in a big page with multiple headers, and it will automatically create named anchor for every H2 and H3 headers and create an UL > LI list with the index.
Example of TOC on an italian website
Other plugins
And in the end i replaced other classic plugins:
- related post — it just calculate the best 3 related posts and display them using a grid;
- social share — add a CSS-only sharebox that will link the most common social sharing urls: Facebook, Twitter, Pinterest, Linkedin; no javascript is used; no counter; no fuzzy.
Closing Words
While looking back at this post I got a little scared about how much work I did in the latest 10 days during my free time.
I really hope that I’ve finally got the ultimate swiss-knife for all my projects that are based on WordPress. I trust that the “on demand” API I’ve developed will help me to maintain all the projects with a single code base where, with just a small child theme, I can decided what to include and what not.
At this time, July 2019, I plan to built 3 more websites in the next 2 months with this theme framework before to release the source code on github. I wish to clean up and polish it a little before to release it to the public.
You will no wonder that the temporary name I’m using for it is “Vanilla WP”, just to continue the “Vanilla” saga of KISS projects.
So, please, stay tuned and remember my motto: “More Hard More Fun”.
About Antonio
I have got 30+ years experience in Open Source technologies. I am a Developer, Speaker and Trainer. Please check my Linkedin profile or https://antoniogallo.it