To install the requisite software needed to maintain the theme, install Node. (There’s a handy walkthrough video, if that’s helpful.) Then, run this in the repo root:
npm install
Then, you can build the site by typing this:
grunt
grunt is a task runner we’re using to automate various, uh, tasks: whenever it’s run, it will concatenate various files, compress them, and run a few other tasks to keep the site as lightweight as possible. (More on that below.)
Additionally, we’ve included Browsersync for autoreload purposes. If that’s not welcome, that’s totally ditchable.
As it stands, the templates structure is fairly flat right now. Inside the tmpl/ folder, we have:
- Inside
scss/we’ve got all the Sass partials that power the site. We’re not using a ton of Sass’ functionality—mainly for light file organizational work, and a few variables—but feel free to add more.- Generally, most of the site’s styles are in
_core.css. But the structure should hopefully be fairly transparent.
- Generally, most of the site’s styles are in
js/is where our JavaScript’s found. As you’ll see in ourGruntfile, our JavaScript’s assembled from a bunch of tiny files into aninitial.js(concat.js_initial.src[]) and amain.js(concat.js_main.src[]). (More on this split below.)- Project-level configuration is included in
config.js. - If you need utilities and helper functions to be accessible throughout the theme,
utils.jsis a decent place to drop them.
- Project-level configuration is included in
svg/is where you should store all icons used on the front-end. We’re using grunticon to produce SVG icons for the site, with PNG fallbacks for older browsers that don’t understand SVGs. (Yep, there’re still plenty out there.) If you need to add a new icon, drop it in thetmpl/svg/directory, and rungrunt.- The theme itself doesn’t use many raster images, but when it does, they live in
img/.
Both tmpl/scss/ and tmpl/css/ contain a lib/ directory, which is where publicly available, third-party libraries can be found.
Any grunt operation will package distribution-ready files up, and deposit them in a dist/ folder. The location of that folder is, at the moment, source/base/static/base/_v2/dist/—to change that, edit _config.dir.output in our Gruntfile.
To add a new icon to the codebase:
- Save the SVG for the icon in the
tmpl/svg/folder.
- We use
#4D4D4Dfor our icons’ color, and they should be 20×20px or smaller.
- Run
gruntorgrunt grunticonfrom the command line.
To incorporate your new icon into a page:
- Note the name of the file you created. This is how you’ll refer to it in the templates.
- Add the filename to a given element’s
class, prefixed withicon-.
- If the filename is
location.svg, then addclass="icon-location"to the element you’d like to which you’re attaching the icon. (e.g.,<span class="icon-location">…</span>) - We have a
has-iconhelper class that adds a little left-edge padding to elements that, uh, have icons. (e.g.,<span class="has-icon icon-location">…</span>)
If you can’t attach a class to an element, there is a grunticon.svg.options.customselectors object in Gruntfile.js, where you can map CSS selectors to specific icons.
From a front-end standpoint, Source’s templates are built with two design principles in mind:
- Serve advanced styles and scripts only to advanced browsers.
- Get content into the browser as quickly as possible.
Let’s cover those in order.
If you open up tmpl/js/initial.js, you’ll see this a little more than halfway down:
if ( !( "querySelector" in document
&& "addEventListener" in window
&& "sessionStorage" in window
) ) {
return;
}
This asks the browser if it’s “sufficiently modern”, by looking for specific features we’ll use in the design. If it fails that little test, then the browser stops executing the JavaScript. But! If the browser passes the test, then a class of .enhanced is applied to the opening <html> tag, and more advanced styles and scripts are loaded.
In other words, we’re serving a baseline, lightweight design to every browser. But if they pass that test, we’ll upgrade them to the full responsive design.
This process is colloquially known as “cutting the mustard”, but is also a form of progressive enhancement. But more practically, it lets us focus on designing for more modern browsers and devices (like Chrome, Firefox, Safari, and Android), while still delivering a usable design to less modern ones (like older BlackBerry devices, older but still-popular versions of Android, IE7, and the like).
If you spend some time in the CSS, you’ll probably see instances of this throughout. For example:
.header-main {
display: flex;
text-align: center;
margin-bottom: 0.5em;
}
.enhanced .header-main {
text-align: left;
margin-bottom: 0;
}
This sets a baseline style on the .header-main element, but if we’re in a “modern” browser, we can use the .enhanced class to apply more advanced rules.
While there are helper classes throughout the CSS that perform a similar function (e.g., .supports-flex), the model is the same throughout the entire theme. We begin by designing and building a usable baseline, and enhance conditionally from there.
Additionally, we should keep the front-end fast. As a result, we’ve tried to minimize the number of blocking requests, and get the content into the page as early as possible.
To achieve this, we’re inlining as much code as possible. Namely:
- We’re writing critical CSS into the
headof each page, and then using a little JavaScript to fetch the full stylesheet. (We’re using Filament Group’s criticalCSS utility to manage this for us whenevergruntruns.)- Note: This assumes there’s a local web server capable of viewing the templates. Per the
Gruntfile, that’s currently set tohttp://local.source/tmpl/—if you use a different domain, please do change it!
- Note: This assumes there’s a local web server capable of viewing the templates. Per the
- As part of the “cut the mustard” technique above, we’re loading our full JavaScript only in advanced browsers. And we’re doing so in a deferred manner, so that it doesn’t prevent other files from downloading.
If there are JavaScript libraries that need to be used on a decent number of pages, it might be worth adding them to concat.js_main.src[] in our Gruntfile.js. That’ll add them to main.js, which gets loaded asynchronously in advanced browsers.
Of course, there will be times when we need to execute arbitrary JavaScript on Source pages. And that’s a good thing! But if it loads code in a blocking manner, it might cause the front-end to slow down.
TLDR: If you can load non-critical code in a deferred, asynchronous manner, that will keep Source running as fast as possible. 🙌
- Ethan Marcotte