Adding a Mobile Menu in WordPress
Content on mobile screens can get cluttered quickly as multiple menu links are added for site navigation. A responsive mobile menu is great way to clean up the design and make the site navigation available on-demand. The possibilities for the layout and behavior are limitless, so for this site I wanted to keep it as minimalistic as possible to match the rest of the overall design. The method below is lightweight and can be easily modified.
First enqueue a new theme file in the functions.php file like this.
wp_enqueue_script( 'evo-menu-js', get_template_directory_uri() . '/js/mobile-menu.js', array( 'jquery' ), true );
Then create a new file named mobile-menu.js with the JavaScript code below saved to it in the /js directory, inside the theme’s directory.
(function($) {
$('#toggle').toggle(
function() {
$('#popout').animate({ left: 0 }, 'fast', function() {
$('#toggle').html('<svg role="img" xmlns="http://www.w3.org/2000/svg" width="40px" height="40px" viewBox="0 0 24 24" stroke="var(--txt-color)" stroke-width="1.8" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="var(--txt-color)" aria-label="close menu"><title>Close Menu</title><path d="M6.34314575 6.34314575L17.6568542 17.6568542M6.34314575 17.6568542L17.6568542 6.34314575"/></svg>');
});
},
function() {
$('#popout').animate({ left: -940 }, 'fast', function() {
$('#toggle').html('<svg role="img" xmlns="http://www.w3.org/2000/svg" width="40px" height="40px" viewBox="0 0 24 24" stroke="var(--txt-color)" stroke-width="1.8" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="var(--txt-color)" aria-label="menu"><title>Menu</title><path d="M6 7L18 7M6 12L18 12M6 17L18 17"/></svg>');
});
}
);
})(jQuery);
Then update the existing menu navigation in the header.php file like this.
<nav id="menu"> /* <-- the existing nav#menu tag */
<div id="toggle">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="40px" height="40px" viewBox="0 0 24 24" stroke="var(--txt-color)" stroke-width="1.8" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="var(--txt-color)" aria-label="menu"><title>Menu</title><path d="M6 7L18 7M6 12L18 12M6 17L18 17"/></svg>
</div>
<div id="popout">
<?php wp_nav_menu( array( 'theme_location' => 'main-menu' ) ); ?> /* <-- the existing wp_nav_menu php */
</div>
</nav>
Finally, add the CSS below to the theme’s existing stylesheet.
@media (min-width: 940px) {
#toggle {
display: none;
}
}
@media (max-width: 939px) {
#popout {
background: var(--block-color);
opacity: 0.97;
z-index: 98;
position: absolute;
top: 0;
left: -940px;
width: 940px;
max-width: 100%;
overflow: hidden;
}
#toggle {
float: right;
z-index: 99;
position: absolute;
top: calc(10vw - 8px);
right: calc(10vw - 8px);
}
#toggle:hover {
cursor: pointer;
}
ul#menu-primary-menu.menu li {
border-bottom: var(--std-brdr) solid var(--txt-color);
margin-bottom: var(--std-brdr);
padding: 10vw;
width: 100%;
}
ul#menu-primary-menu.menu li a {
text-decoration: none;
width: 100%;
}
}
Note that the method above uses inline SVG icons for the toggle buttons, and CSS variables like in this previous post. I’ve set the responsive breakpoint to 940px to match my theme’s layout (this should also include most tablets in portrait-orientation).
Optimizing the WordPress Head Tag Section
The initial WordPress head tag section is a bit bloated. There are some quick optimizations via the functions.php file that will improve a WordPress site’s loading time and increase security. The key elements the method below addresses:
• Remove Query Strings from Static Resources – This can improve caching and fix some speed test warnings – Note that this can break how some external resources load (ex: enqueuing a Google Fonts CSS link)
• Header Cleanup – This removes all the unnecessary tags: RSD link, WordPress version number (increasing site’s security), wlwmanifest link, shortlink, and extra feed links (primary RSS feed still functions correctly)
• Disable the Emojis – This removes the emoji support WordPress has built-in for older browsers that isn’t needed for modern browsers
// Remove query strings from static resources
function _remove_script_version( $src ) {
$parts = explode( '?', $src );
return $parts[0];
}
add_filter( 'script_loader_src', '_remove_script_version', 15, 1 );
add_filter( 'style_loader_src', '_remove_script_version', 15, 1 );
// Header cleanup
add_action( 'do_feed_rss2_comments', 'disable_feeds', -1 );
add_action( 'do_feed_atom_comments', 'disable_feeds', -1 );
add_action( 'feed_links_show_comments_feed', '__return_false', -1 );
remove_action( 'wp_head', 'feed_links_extra', 3 );
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', 10, 0 );
// Disable the emojis
function disable_emojis() {
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' );
add_filter( 'tiny_mce_plugins', 'disable_emojis_tinymce' );
add_filter( 'wp_resource_hints', 'disable_emojis_remove_dns_prefetch', 10, 2 );
}
add_action( 'init', 'disable_emojis' );
function disable_emojis_tinymce( $plugins ) {
if ( is_array( $plugins ) ) {
return array_diff( $plugins, array( 'wpemoji' ) );
} else {
return array();
}
}
function disable_emojis_remove_dns_prefetch( $urls, $relation_type ) {
if ( 'dns-prefetch' == $relation_type ) {
$emoji_svg_url = apply_filters( 'emoji_svg_url', 'https://s.w.org/images/core/emoji/2/svg/' );
$urls = array_diff( $urls, array( $emoji_svg_url ) );
}
return $urls;
}
Prism Syntax Highlighter
I’ve updated the site to use Prism syntax highlighter for styling posted code blocks. This free and open source project is built by a community of contributors and is super-configurable, lightweight and easy to use. It’s a great option to make code blocks easier to read.
Prism is a lightweight, extensible syntax highlighter, built with modern web standards in mind. It’s used in thousands of websites, including some of those you visit daily.
To use Prism, I installed the Code Syntax Block plugin so I can simply pick the corresponding code language in the default Code Blocks in each blog post.
The plugin will check for a custom theme file here: assets/prism/prism.css, and use that file if it exists. Add your custom file in that location, and it will be used.
Optimizing Sites with the .htaccess File
Slow websites suck. Luckily, there are some quick optimizations via the .htaccess file that will improve a site’s loading time and boost your speed score. This is a good opportunity to increase the site’s security too. The key elements the method below addresses:
• Force HTTPS – This requires that you have an active SSL Certificate on your domain (Let’s Encrypt is a good free option) and forces all requests to use HTTPS for stronger encryption
• Accept-Encoding – This tells the browser whether or not the client can handle compressed versions of the content
• Expires Caching – This sets the time duration files will stay cached on visitor’s browsers, so when they revisit the site it loads much faster
• Gzip Compression – This compresses the website files reducing the overall size of the site
# FORCE HTTPS
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# ACCEPT-ENCODING
<IfModule mod_headers.c>
<FilesMatch ".(js|css|xml|png|gz|html|woff2|slim.min.js)$">
Header append Vary: Accept-Encoding
</FilesMatch>
</IfModule>
# EXPIRES CACHING
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access plus 90 days"
ExpiresByType image/jpeg "access plus 90 days"
ExpiresByType image/gif "access plus 90 days"
ExpiresByType image/png "access plus 90 days"
ExpiresByType image/svg "access plus 90 days"
ExpiresByType image/x-icon "access plus 90 days"
ExpiresByType video/mp4 "access plus 90 days"
ExpiresByType video/mpeg "access plus 90 days"
ExpiresByType text/css "access plus 1 week"
ExpiresByType text/javascript "access plus 1 month"
ExpiresByType text/x-javascript "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
ExpiresByType application/pdf "access plus 90 days"
ExpiresDefault "access plus 1 week"
</IfModule>
# GZIP COMPRESSION
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE application/x-font
AddOutputFilterByType DEFLATE application/x-font-opentype
AddOutputFilterByType DEFLATE application/x-font-otf
AddOutputFilterByType DEFLATE application/x-font-truetype
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
</IfModule>
Pure CSS Masonry Grid
Masonry grid layouts are great when you want to show a lot of content over an entire page or section of a page. JavaScript is a typical go-to for these layouts, and it’s great if you need your content blocks to load left-to-right across the page/section, but if you can live with your content blocks loading top-to-bottom in the columns, the method below is a solid pure CSS option without any JavaScript dependencies. This method also uses media queries to make the column-count responsive across devices.
.masonry-container {
-moz-column-count: 4;
-webkit-column-count: 4;
column-count: 4;
-moz-column-gap: 1rem;
-webkit-column-gap: 1rem;
column-gap: 1rem;
}
.masonry-item {
display: inline-block;
margin: 0 0 1rem;
width: 100%;
}
@media (max-width: 1024px) {
.masonry-container {
-moz-column-count: 3;
-webkit-column-count: 3;
column-count: 3;
}
}
@media (max-width: 768px) {
.masonry-container {
-moz-column-count: 2;
-webkit-column-count: 2;
column-count: 2;
}
}
@media (max-width: 480px) {
.masonry-container {
-moz-column-count: 1;
-webkit-column-count: 1;
column-count: 1;
}
}