Why HTML template engines are the nuts 🥜
Maximilian
Posted on February 7, 2021
const tldr = “Allows you to write DRY code and incorporate functions into HTML, which is a game changer for maintainability and debugging.”
In this article I will show you how I wrote dynamic markup for a mid-sized mobile menu using Pug (formerly Jade). You can see the menu in action here!
The main goals for the mobile navigation are as follows:
- Display all 18 pages in a user friendly way, by splitting them up into 4 categories: Design, Marketing, Events and More
- When the user opens the menu, it opens on the category they’re currently in, with the page they’re on highlighted
- As the user browses other categories, the page they’re currently on remains highlighted
With our goals in mind, before we get into the code, have a think about how you would go about building something like this.
Pug and HTML templating — an explanation
If you’re not sure what an HTML templating language is, this bit is for you.
Think of an HTML Templating language as a ‘pre-processor’ to HTML. This is to say, you write code in the templating language of your choice (in our case, Pug), and when you’re done, you compile it (or output it) to good old fashioned HTML files.
Now, why the hell would you want to do this?
const tldr = “because we can write DRY code, cleaner code, less code, dynamic code and thus, more maintainable code. And it’s fun.”
Well, now would be a good time to remember that HTML is just markup. It doesn’t really do anything. HTML Template languages allow us to write dynamic code that can be broken up into smaller chunks and files. Aside from the added benefits of being able to write functions and conditional statements in HTML (there will be examples later), the main benefit I’ve found is being able to write DRY code. (Don’t Repeat Yourself).
For maintainability, DRY code is essential to any project, even for the smallest static website. Let me explain:
Say you’re building a small portfolio site with 5 pages and, of course, you have a navigation menu. In plain HTML, that navigation menu exists on all 5 pages. That’s 5 times you have to write the menu. Want to update it? You have to update it in 5 places. Made a spelling mistake? You have to fix the mistake in 5 places. It’s also prone to mistakes that appear in random places as you have the ability to make mistakes in multiple locations, so error checking can be difficult.
Using an HTML templating language allows you to write less HTML, but more excitingly, fun HTML! The syntax is also, subjectively, simpler and clearer.
How to run a templating language
In order to use a templating language, we need a way of compiling our files to HTML. For this project, I used a Node.js environment and Gulp. My favourite templating language is Pug (formerly Jade), but you should experiment with others and see what you like. To my knowledge, they’re all pretty much capable of the same things. Other popular languages in the Node.js environment are EJS and Handlebars, so I would start there if you’re at all unfamiliar.
Let’s Build Our Menu
My plan and the pseudo code for this menu is as follows:
mobileMenu(mainMenu, subMenu)
div class = “mobileMenuContainer”
div class = “mobileMenuCategories”
p Design class=(mainMenu == “design” ? “active” : “”)
p Marketing class=(mainMenu == “marketing” ? “active” : “”)
p Events class=(mainMenu == “events” ? “active” : “”)
p More class=(mainMenu == “more” ? “active” : “”)
div mobileMenuSubMenu
div DesignMenu
[Design Pages] class=(subMenu == *page* ? “Active” : “”)
div MarketingMenu
[Marketing Pages] class=(subMenu == *page* ? “Active” : “”)
div EventsMenu
[Events Pages] class=(subMenu == *page* ? “Active” : “”)
div MoreMenu
[More Pages] class=(subMenu == *page* ? “Active” : “”)
The idea here is to make the whole mobile menu a re-usable function that takes in 2 arguments - mainMenu
and subMenu
. The mainMenu
argument will serve as a way of using standard CSS class names to dynamically select which category is currently active, and the subMenu
argument will serve as a way of dynamically selecting which page is currently active. (e.g. on our design category list item: “does mainMenu == "design"
? If so, add the class name active
, otherwise, don’t.")
This means that on the “Website Design” page, we would simply include our mobile menu in the following way:
+mobileMenu(“design”, “website design”)
That’s it! That’s the markup for our whole menu, dynamically generated on our Website Design page with custom class names specific to that page.
It follows then, that if we were on the Freelance page, which is under the ‘marketing’ section of the site, we would use the following code on our page:
+mobileMenu(“marketing”, “freelance”)
Cool huh?
Ok, now on to some actual code (although with Pug, we’re not far off):
In Pug, reusable blocks of code that have the option to take in arguments (like a function) are called ‘Mixins’.
There are three things to quickly note if you’re unfamiliar with pug:
- To define a
<div>
with a classname, we simply write.this-is-the-class-name
which will output the HTML<div class=“this-is-the-class-name”></div>
- To give an HTML element an
id
, we do the same thing as above, but with#
. E.g.#this-is-the-id
will output<div id=“this-is-the-id”></div>
. - Pug relies on indentation to place elements inside other elements. e.g.
.mobile-menu__menu
nav.mobilemenu__main-nav
Will output:
<div class=“mobile-menu__menu”>
<nav class=“mobilemenu__main-nav”>
</nav>
</div>
I hope you’re starting to see how much cleaner Pug is to read and write!
In our mobile menu file, let’s define our Mixin:
mixin mobileMenu(main, sub)
Inside our Mixin, we’ll start making our code block with a single div which will contain everything, starting with the main navigation element which will house an unordered list and some list items.
mixin mobileMenu(main, sub)
.mobile-menu__menu
nav.mobile-menu__main-nav
ul
li.main-nav__list-item#main-menu__design
li.main-nav__list-item#main-menu__marketing
li.main-nav__list-item#main-menu__events
li.main-nav__list-item#main-menu__more
Just to make sure you’re on track with what the above code means, this should output the following HTML. I won’t reference the HTML again, as it should be self explanatory from this point on. Bear in mind, we’re not making use of our Mixin parameters yet.
<div class=“mobile-menu__menu”>
<nav class=“mobile-menu__main-nav>
<ul>
<li class=“main-nav__list-item id=“main-menu__design>
</li>
<li class=“main-nav__list-item id=“main-menu__marketing>
</li>
<li class=“main-nav__list-item id=“main-menu__events>
</li>
<li class=“main-nav__list-item id=“main-menu__more>
</li>
</ul>
</nav>
</div>
Now, we’ll add the contents of each list item and (finally) make use of our first parameter:
mixin mobileMenu(main, sub)
.mobile-menu__menu
nav.mobile-menu__main-nav
ul
li.main-nav__list-item#main-menu__design(class = main == “design” ? “active” : “”)
p Design
li.main-nav__list-item#main-menu__marketing(class = main == “marketing” ? “active” : “”)
p Marketing
li.main-nav__list-item#main-menu__events(class = main == “events” ? “active” : “”)
p Events
li.main-nav__list-item#mani-menu__more(class = main == “more” ? “active” : “”)
p More
For each list item, we’re checking the value of main
whenever our mobileMenu Mixin is called, and applying the active
class name if it matches using the shorthand Javascript if statement:
main == “more” ? “active” : “”
Which is equivalent to:
if (main == “more”) {
“active"
} else {
“”
}
The contents of each list item just includes a <p>
tag with the title of each category name.
Now we’ll move onto our Sub Menu
It’s the same concept as the main menu above, so the code should start to feel familiar to you. We are now also making use of our second Mixin parameter.
nav.mobile-menu__secondary-nav
ul(id=“events-list” class = main == “events” ? “secondary-nav__list” : “secondary-nav__list remove fade-out”)
li
a(href=“./events” class = sub == "events" ? "active" : "") Events
li
a(href=“./event-management” class = sub == "event management" ? "active" : "") Event Management
li
a(href=“./markets” class = sub == "markets" ? "active" : "") Markets
The class names on the <ul>
elements may not make sense at the moment, but the idea is to apply the class name remove
(which will apply display: none;
using CSS) and fade-out
which we’ll use to apply a CSS animation when the sub-menu is changed.
Each <li>
element contains an <a>
tag linked to each page and includes a conditional CSS class name, exactly like we did for the main menu. E.g. For the Markets page, we check if sub == "markets"
? If so, add the class name active
, otherwise, don't!
Rinse and Repeat
Now we just repeat the code above for each sub menu and put it all together for our finished Mixin.
mixin mobileMenu(main, sub)
.mobile-menu__menu
nav.mobile-menu__main-nav
ul
li.main-nav__list-item#main-menu__design(class = main == "design" ? "active" : "")
p Design
li.main-nav__list-item#main-menu__marketing(class = main == "marketing" ? "active" : "")
p Marketing
li.main-nav__list-item#main-menu__events(class = main == "events" ? "active" : "")
p Events
li.main-nav__list-item#main-menu__more(class = main == "more" ? "active" : "")
p More
nav.mobile-menu__secondary-nav
ul(id="events-list" class = main == "events" ? "secondary-nav__list" : "secondary-nav__list remove fade-out")
li
a(href="./events" class = sub == "events" ? "active" : "") Events
li
a(href="./event-management" class = sub == "event management" ? "active" : "") Event Management
li
a(href="./markets" class = sub == "markets" ? "active" : "") Markets
ul(id="design-list", class = main == "design" ? "secondary-nav__list" : "secondary-nav__list remove fade-out" )
li
a(href="./graphic-design" class = sub == "design" ? "active" : "") Design
li
a(href="./website-design" class = sub == "website design" ? "active" : "") Website Design
li
a(href="./design-for-print" class = sub == "design for print" ? "active" : "") Design for Print
li
a(href="./logo-design" class = sub == "logo design" ? "active" : "") Logo Design
ul(id="marketing-list", class = main == "marketing" ? "secondary-nav__list" : "secondary-nav__list remove fade-out" )
li
a(href="./marketing" class = sub == "marketing" ? "active" : "") Marketing
li
a(href="./workshops" class = sub == "workshops" ? "active" : "") Workshops
li
a(href="./freelance-marketing" class = sub == "freelance" ? "active" : "") Freelance
li
a(href="./social-media" class = sub == "social media" ? "active" : "") Social Media
li
a(href="./copywriting-services" class = sub == "copywriting" ? "active" : "") Copywriting
li
a(href="./consultancy" class = sub == "consultancy" ? "active" : "") Consultancy
ul(id="more-list", class = main == "more" ? "secondary-nav__list" : "secondary-nav__list remove fade-out" )
li
a(href="./pricing" class = sub == "pricing" ? "active" : "") Pricing
li
a(href="./privacy" class = sub == "privacy" ? "active" : "") Privacy
li
a(href="./contact-us" class = sub == "contact" ? "active" : "") Contact
li
a(href="./sitemap" class = sub == "sitemap" ? "active" : "") Site Map
li
a(href="./testimonials" class = sub == "testimonials" ? "active" : "") Testimonials
Include the Mixins
On each page of our site, we can now call our Mixin with the two parameters we’ve defined, and the HTML markup will be included in the page with the appropriate class names defined by the arguments we pass it. If we want to edit the menu in any way, we have one place in which to edit it. Although I already covered it in pseudo-code, the actual code to include our markup in our other Pug files is as follows:
On the website design page:
+mobileMenu(“design”, “website design”)
On the freelance page:
+mobileMenu(“marketing”, “freelance”)
CONCLUSION
That’s the end of our time discussing Pug and HTML Templating. I hope I’ve proved the case for it already, but if I haven’t, I’d like to end on a quick story:
The client I built this mobile menu for (and their entire site) decided after I had designed and built the entire app that they wanted to change their name and logo.
Even though I made a song and dance about what a MONUMENTAL TASK this was, it actually took me about 5 minutes, but only thanks to templating.
Because everything only existed in one place, I only had a couple of things to change! Even the SVG logo was added as an ‘include’ to every file it was needed, so when I changed the SVG file, BAM — it was everywhere! And for the remaining text, VS Code took over and I used the ‘replace all’ feature inside the Find tool to replace their old name with their new name and…. Done!
What about the CSS and Javascript?
The JS and CSS was beyond the scope of this article BUT…. If you use your imagination, I’m sure you can guess what I did with these. I’ll summarise below, but if anyone actually reads this and is interested, let me know and I’ll cover them in a future post.
The Javascript
The JS gets all the elements in the menu and applies event listeners to the category links. When the user clicks a category, it adds and removes the respective class names which are then styled in CSS to make the appropriate sub menu appear and disappear. I also utilise timeouts to allow for animation times.
The CSS
I used Sass to make it all look pretty. Nothing crazy - just some transform: translateX()
, opacity
, and display: none
kinda stuff.
Posted on February 7, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.