Speclate

The London Node User group website is a static site hosted on github pages, it renders at build time, in the browser and works offline using Speclate.

The idea behind Speclate is you have a single file (a spec) which defines how your routes, pages and components fit together. A spec can be used to generate a static site and lots more too.  With the help of speclate-router we generate a router which swaps out the appropriate HTML elements to transition nicely between pages.

Let's look at a simple example:

module.exports = {  
     '/': {  
         page: 'home',  
         selectors: {  
             title: 'Home',  
             'a.home': {  
                 className: 'active'  
             }  
         }  
     },  
     '/contact.html': {  
         page: 'contact',  
         selectors: {  
             title: 'Contact',  
             'a.contact': {  
                 className: 'active'  
             }  
        }  
     }  
};

This spec will produce index.html and contact.html based on the contents of /pages/home/home.html and /pages/contact/contact.html and the values in the selectors object.

At the moment Speclate relies on a couple of conventions. /pages/layout.html needs to contain an element with an id of "container"for the pages to be appended to. This will move to config eventually.

I've created a simple example app. You can see the demo running on Netlify, and get the source code on GitHub. You can also read more about the spec format in the speclate readme.

Rendering

Underneath the hood Speclate uses Sizlate which makes extensive use of Sizzle, the selector engine from jQuery.  On the server, Cheerio provides a fast DOM implementation for generating the markup.

Rendering can be broken down into three steps. Firstly we replace the main page (using #container), then we apply the page spec, this will apply selectors and components to the page and lastly we apply the global selectors to the markup. This allows us to effect elements in the main layout such as the page title.

In the example there are three commands in the package.json to generate the site:

"scripts": {
    "markup": "speclate",
    "client": "browserify ./client/router.js > ./client/router-compiled.js",
    "build": "npm run client && npm run markup"
}

'npm run markup' will generate a functional HTML site. 'npm run client' uses browserify to generate the client-side router and 'npm run build' generates the whole site by running both commands.

Offline

Currently, the LNUG website works offline using an AppCache.manifest file. That file is generated from the spec like so:

speclate.site.appCache(spec, [  
'/css.css',  
'/images/lnug-logo.svg'  
])

We use appCache Nanny to handle updates but the experience is still not great. The latest releases of Speclate now use the fetch API so it opens up lots of possibilities with Service Workers.