Get started


1.  You'll need Node.js and NPM or Yarn


  npx degit platframe/new my-app

  or download the latest release

3.  cd my-app && git init && yarn


Spin up a development environment with  yarn develop
By default, the auto-sync development server runs at  localhost:3000


Run your test suite with  yarn test
Produce and serve a production build for inspection:  yarn preflight


For an optimized, production-targeted build:  yarn build
More complex deployment logic can be run with  yarn deploy


The platform aims to scale seamlessly with consistent patterns for each part of the frontend stack. The following diagram provide a condensed overview of Platframe's architecture (fig. 2.1).

architecture 2.1


The root directory contains your project. It is the parent of the src, dev and prd directories (explained below). It also contains files not present on the above schematic, such as configuration, tooling and metadata. Be sure to change "root" to something more descriptive, as you see fit for your project.


The source directory is home to the project's source code. Descending one level down the tree reveals 6 sub-directories that separates the different asset groups.


This is where the build system deploys and serves the project during development. Where applicable, assets are optimized for developmental purposes.


The production build is located here. As illustrated by the schematic, directory layout is similar to the dev folder (fig. 2.1).


Project markup is comprised of partials and views in the templates directory and if used, components. As components are prepackaged functionality that does not form part of the foundational templates and markup we are going to discuss here, we deal with them in a dedicated section.

Your app's core markup files live in templates and, depending on their scope, is conceptually separated into "global" and "local". Global markup is confined to root.pug and the partials contained in its sibling directory, _. The views folder is home to all local markup and splits into index.pug and any amount of sections that the project could be comprised of.


The purpose of global markup is to supply the project's view files with the resources they share in a maintainable and DRY fashion. These shared resources generally consist of foundational markup, mixins, metadata, various project assets and more static elements such as headers and footers. Files with global reach are root.pug and those in the project's root-level partials directory (_), namely mixins.pug, head.pug and body.pug.


doctype html

block context

html(lang="en" class=rootClass)

    include _/mixins

    include _/head

    block body

By looking at figure 3.1, we can see that root.pug declares the doctype, introduces a document context and sets the root HTML element with two default attributes. It then goes on to pull in the dependencies, serving as the common ancestor for all markup. It loads the global partials (contained in the sibling _ directory) in a dependency-sensitive order.

First, with block context we reserve a "context" block that will allow us to inject data into the page that can then be consumed by all the descendant nodes of the root document object.

Next, we include mixins.pug, used for your own mixin collection as well as referencing those of components that the project make use of.




    block description
        meta(name="description" content="Lorem ipsum dolor sit amet.")

    title= pageTitle || 'Or Fallback to Default Title'

    block links
        - if (ENV === 'production')
            link(rel="stylesheet" href="//")
        link(rel="stylesheet" href="/assets/style/root.css")

    block scripts
        - if (ENV === 'development')
        - else
            script(defer src="//")
        script(defer src="/assets/logic/js/root.js")

The above code sample is a modified excerpt from head.pug, another partial living in _. In the HEAD element, we declare 4 blocks and a variable for the page title. The document description, keywords and title will typically be different on each page, so we declare blocks for any part of HEAD that could be subject to change, including links and scripts. The keywords meta tag is optional while the scripts block is only of use when an external library requires loading from within <head>.


extends ../root

block body

    body(id=bodyId class=bodyClass)

        block header
            +header-1-1("/#home", "logo", "Header Text", [
                    href: "/somewhere/",
                    name: "Menu Entry 1"

        block main

        block footer
            include ../../components/footers/column_3/1/_

        noscript Please enable JavaScript

The body.pug global partial extends root.pug and makes use of the latter's reserved block body for correct placement. body.pug serves as the default base structure for all views, providing a single location from which to manage the header, footer and additional dependency provisioning for your pages.

As we continue with figure 3.3, notice the placeholder ID and CLASS attribute variables we assigned to the BODY element for future use.

block header is declared to serve as the global page header, specifically placed in a block in case we need to override it at some point. To serve as the default, we call one of our primary (1st level) header components, in particular its Pug mixin. For more on "header-1", have a look at its writeup.

block main is where the dependants of body.pug (views) will expose their content and alludes to the MAIN element that will be used for this purpose.

Since a footer is not necessarily static across a project, we declare another block to allow for superseding this default in specific views. Note that due to the complexity of the footer included in the example above, the author of the component decided not to encapsulate it within a mixin. We therefore modify and include it directly from its location in components. For more on "footer-1", have a look at its description.

The bottom of body.pug can be used for whatever seems sensible, permitting that it should have a project-wide reach and be provisioned last. The default is for Platframe to ship with this section being used for <noscript> and loading root.js


We refer to markup files that do not have a global, application-wide impact as being local. A local file's reach can however stretch beyond itself, such as when a selection of sibling or descendant files are extending it and thus inherit from it. Think of local markup as just having variably less reach.


extends ../_/body

block context
        bodyId = 'wxyz';
        bodyClass = 'index';
        pageTitle = 'Page Title';

block main




                h2 Lorem ipsum dolor sit amet

                p Consectetur adipisicing elit...

As the name suggests, the views directory contains the project's views. Views are the markup files that holds the content and define the structure of your project's pages. As views make up the individual pages of your application, they should be unique in terms of their content and share only those features present in their common ancestors - the globals.

When dealing with a multi-category project, the views directory can branch out into a hierarchy of interconnected sections that follows a set pattern, regardless of relational extent or depth - see nesting below.

When a view is the landing page of any particular section, it will be identified as index.pug at the root level of the directory. For instance, the first index.pug we encounter as a direct child of views is the main landing page for the site and in the case of an SPA may be the only view file.

Views would normally inherit its basic structure and resources from the globals we covered above. Should the need arise, basic provision for overriding inherited markup is already in place and can be further customized through the use of block's. Views are compiled into HTML files upon build. For instance, index.pug becomes index.html and hello.pug becomes hello.html.


         ├─  index.pug
         ├─  about ──  index.pug
         ├─  menu ─┐
         |           ├─  index.pug
         |           ├─  _        ──  nav-2.pug
         |           ├─  starters ──  index.pug
         |           ├─  main     ──  index.pug
         |           ├─  desserts ──  index.pug
         |           └─  drinks ─┐
         |                         ├─  index.pug
         |                         ├─  juice  ──  index.pug
         |                         ├─  coffee ──  index.pug
         |                         └─  beer ─┐
         |                                     ├─  index.pug
         |                                     ├─  draft ──  index.pug
         |                                     ├─  craft ──  index.pug
         |                                     └─  local ──  index.pug
         ├─  reservations ──  index.pug
         └─  contact      ──  index.pug

Applications with nested categories follows a recursive structural pattern regardless of hierarchical depth. The above visualization demonstrates a typical nesting scenario. Each nested category has its own index.pug page to serve as the landing view for that particular section (level) of the parent category. Again, when a sub-category has to divide into one or more child (offspring) categories, the pattern repeats.


Depending on how they are used, partials can be beneficial in a variety of ways. For example, they are great at making your templates DRY, easier to manage and reducing the overall maintenance burden.

Platframe uses an underscore to denote a partial directory or file. There are two ways we can indicate partials in our project's file system: either by grouping several related partial files in a directory named "_" or by just prefixing each partial file with an underscore. If grouped in a folder, the prefix is negated. Figure 3.5 illustrates the former method, while figure 3.6 shows the latter. It is recommended to only use the prefix method (figure 3.6) when dealing with content partials.


 templates ─┐
              ├─  root.pug
              ├─  views
              └─  _ ─┐
                       ├─  head.pug
                       ├─  body.pug
                       └─  mixins ─┐
                                     ├─  index.pug
                                     ├─  analytics.pug

The files contained in the partial folder that is a direct child of the root template directory, templates, are global in that they are inherited by all views. Figure 3.6 illustrates the global partials in _: head.pug, body.pug and mixins.


Partials that relate only to a specific section or view are referred to as being local. We distinguish between two types of local partials, sectional and content.


An example of a sectional partial would be the markup of a navigation component aimed at providing menu support adapted for the requirements of sub-sections (referred to as "2nd level" navigation within components). In Figure 3.5, nav-2.pug is housed in the partial directory (_) in the "menu" section. Being restricted to the menu section indicates that it should only be consumed by views in that section.

 section x ─┐
              ├─  _1.pug
              ├─  _2.pug
              ├─  _3.pug
              ├─  _4.pug
              ├─  _5.pug
              └─  index.pug

Depending on the amount of content a view holds, it might make sense to partialize the constituent parts for ease of management. We then unify the separate parts by including them at their proper positions in the main view. As they are only relevant to a single view file, it is not necessary to separate content partials into an underscored directory that signifies a group of partials. If we go this route however, we need to ensure that each partial name is prefixed with an underscore (_) to signify that they are partials (figure 3.7). In the example above, we split the content of index.pug from section x into five partials, from _1.pug to _5.pug.


Markup mixins live in the global partials directory under mixins. Note that the mixins defined here are not appended with a hyphenated numerical ID as those that form part of a component.



This mixin simplifies the inclusion of the Google Analytics tracking code. You can place it in the "scripts" block of head.pug — it takes the property's unique tracking ID as its only argument. Since you typically only want it to be active in a production build, you can determine inclusion with an if clause as shown below in figure 3.8.

block scripts

    - if (ENV === 'production')



 styles ─┐
          ├─  _
          ├─  views
          └─  root.styl

The styles directory is the central location from which we manage the project's presentation. It expands into _, views and root.styl. Styles are structured similar to templates in that both have their content logically grouped with respect to its scope, either global or local.


General styles that have a site-wide impact are grouped according to type and referred to as being global. These are root.styl and those contained in _ (the partials folder), which are modules, plugins and elements.

@require '_/modules'
@require '_/utilities'
@require '_/plugins'
@require '_/elements'

@require 'views'

As the name suggests, root.styl serves to unify the integral parts of our styles. To ensure all dependencies are met at build time, we first load the partials from _ after which we process the individual view styles.


_ is a direct child of styles and houses the global partials. root.styl is responsible for including these partials in a dependency-sensitive manner. We first pull in modules, containing our base styles. We then fetch the utility class modules from utilities, comprised of high-level functional classes. Up next is plugins which in turn makes all component and other external styles available for use. Finally, elements is where we manage elements (or sections) of the project that are common to all views, like headers and footers.


 modules ─┐
            ├─  cache.styl      · various placeholder styles
            ├─  equalize.styl   · normalize / reset rules
            ├─  forms.styl      · form defaults
            ├─  globals.styl    · sundry global variables
            ├─  graphics.styl   · image/graphic defaults
            ├─  layout.styl     · basic structural styles
            ├─  media.styl      · media and platform defaults
            ├─  tables.styl     · table defaults
            ├─  color ─┐
            |            ├─  index.styl    · set primary scheme
            |            ├─  palette.styl  · global defaults
            |            ├─  keys.styl     · shorthand references
            |            └─  schemes ─┐
            |                           ├─  dark  · scheme package
            |                           ├─  gray
            |                           ├─  light ─┐
            |                           ┆            ├─  index.styl
            |                                        ├─  palette.styl
            |                                        └─  dictionary.styl
            ├─  mixins ─┐  · manage mixins / functions
            |             |
            |             ├─  color.styl
            |             ├─  conversion.styl
            |             ├─  effects.styl
            |             ├─  interaction.styl
            |             ├─  layout.styl
            |             ├─  platform.styl
            |             └─  text.styl
            └─  text ─┐
                        ├─  index.styl
                        ├─  domains ─┐  · defaults for markup domains
                        |              |
                        |              ├─  anchors.styl
                        |              ├─  body.styl
                        |              ├─  custom.styl
                        |              ├─  forms.styl
                        |              ├─  headings.styl
                        |              ├─  lists.styl
                        |              ├─  modifiers.styl
                        |              ├─  monospaced.styl
                        |              └─  paragraphs.styl
                        ├─  fonts ─┐
                        |            ├─  font.styl  · face rules
                        |            ┆
                        └─  stacks ─┐
                                      ├─  serif.styl

The modules directory group together all the generic, foundational styles. Use index.styl for both activating existing modules and adding new ones. Figure 4.3 presents a terse summary of each module's function. While most modules are self-explanatory, we'll cover the more involved ones next.


Colors are managed centrally from within the color module. Platframe has a unique color system that combines the characteristics of schemes and scopes with the cascade. This enables rapid, consistent and precise color modification.


Default colors that do not belong to a scheme package are managed from the top-level palette.styl. This may include colors that belong to a custom theme, gradients and sundry color classes.


A color scheme is comprised of a package consisting of 3 files. Schemes are self-contained and thus good for sharing between disparate Platframe projects.

  1. index.styl  →  pulls in the dependants
  2. palette.styl  →  tie color values to special scheme variables
  3. dictionary.styl  →  hooks into the palette to build scheme
    color: scheme.default
    background: scheme.background

Color dictionaries enable both internal as well as component styles to reference common, generic color combinations. This means that color values which are authored against Platframe's scheme protocol are decoupled from arbitrary colors and can thus receive its ink from the active (parent) scheme. Figure 4.4 illustrate rules with basic dictionary references.

    color: scheme.header.default
        fill: scheme.header.vector

Dictionaries are multi-dimensional to allow for more precision through the use of "scopes". Scopes are special name-spaced sections defined in palettes and their related dictionaries to target specific areas of the interface. For example, areas like nav, header and footer make a good case for scope creation as they often require some degree of deviation from the basic scheme. Figure 4.5 samples a header scope in action.


keys.styl contains shorthand references to the hierarchical color relationships of the active scheme.


Naming conventions help to avoid namespace collision and makes it easier to identify a variable's purpose.

// general colors: single underscore prefix
_color = #FF6347
// scheme colors: double underscore prefix
__scheme_default = #000
// gradients: triple underscore prefix
___gradient = #FFF 0%, #AAA 100%

The style module ships with a number of convenience methods to speed up development. They are categorized according to their functionality and grouped within sub-modules contained in mixins.



Provides the ability to supersede the active global scheme on a local (view) level. Useful in scenarios where a sub-section is themed differently from the rest of the project. Call this mixin at the root level of the parent selector common to the target section(s) and pass it a valid scheme name from a package in schemes. Also affects nested components.


anchors(scheme, [scope])

Exclusively target anchor elements for scheme change. May pass an existing scheme-scope as a second argument to increase precision.

// target general anchors

// override link values in scheme scope
    anchors(dark, footer)


Convert pixel values to rem units with rem() (fig. 4.10)

font-size: rem(32px) // 2rem; :root = 16px

+min(val) | +max(val) | +between(val, val)

These dimensional media query helpers allow granular control in responsive design. Acceptable arguments are literal values as well as numbers or keyword identifiers correlating to the viewport scale as specified in media.styl (fig. 4.11).

// target first breakpoint range
// pass an arbitrary literal
// target range between 2nd & 3rd breakpoints
+between(second, third)

+min-res(val) | +max-res(val)

Target the platform's pixel density with resolution helpers.

// dots per pixel
// dots per inch


The grid routine enables responsive, grid-based development (fig. 4.12). Check out layout.styl in the mixins module for more configuration options.

definition = 12    // set granularity
interstice = 15px  // set gutter

// 1 column / parent's full width

// 3 columns & active gutter
   grid('third', true)

// 2 columns, active gutter, override gutter
   grid(6, true, 2%)


 utilities ─┐
              ├─  index.styl
              ├─  button.styl

Platframe has two types of utility classes: lower-level utilities like those in the layout module, and the higher-level "surface" classes. Low level utilities are all contained within the bounds of modules. The latter type, "surface" utilities live in the top-level utilities directory as denoted by the diagram above. "Surface" utilities are so called because they tend to be more abstract and are positioned to be consumers of the foundational code in modules. Whether the occasion calls for it, or whether it is the preferred approach to styling, utilities are suited for functional CSS classes with various degrees of abstraction and composition. Platframe ships with button.styl which is an example of transforming the button() mixin into a set of utility classes.


 plugins ─┐
            ├─  index.styl
            ├─  plugin-a ─  plugin.styl
            └─  plugin-b ─  plugin.css

External style resources can be used by linking to it from index.styl in plugins. Since Platframe's native components are essentially plugins too, their styles are made available for use throughout our primary sheets by linking to them from here.


 elements ─┐
             ├─  index.styl
             ├─  header.styl
             └─  footer.styl

The styling of features that are commonly shared among views can be defined in elements. Figure 4.14 illustrates how the style rules for the <header> and <footer> elements are segregated by default. We take specificity into account by deploying these features only as direct children of the <body> element, allowing for easier overriding in scenarios where the default needs adaptation or replacement.


Style files with local, page-level scope are confined to the views directory.


 views ─┐
          ├─  index.styl
          ├─  home ─────┐
          |               ├─  _hello.styl
          |               ├─  _story.styl
          |               ├─  _convert.styl
          |               └─  index.styl
          ├─  category-1 ─  index.styl
          ├─  category-2 ─  index.styl
          └─  category-3 ─┐
                            ├─ index.styl
                            ├─  sub-1 ─  index.styl
                            └─  sub-2 ─  index.styl

View styles are modular and pertain to specific sections and the pages within them. Referring to the example directory structure above (figure 4.15), we see that each section has a primary stylesheet in the form of index.styl and may divide into any number of sub-sections. Akin to templates, this is a structural pattern that holds regardless of hierarchical depth or breadth.

You may notice parity in how our style views are structured in relation to their counterpart markup views within templates. Keeping with this pattern leads to a more intuitive file system.

View Partials

Similar to templates, we deal with 2 types of partials in styles: global and local. Having covered global partials in the beginning of this section, let's focus on partials as they're used in views.

In line with convention, partial files not housed in a dedicated folder are individually prepended with an underscore (refer to the section on partials in templates). Looking at figure 4.15, we see that home contains 3 partials: _hello.styl, _story.styl and _convert.styl. We'll use the main stylesheet for the home section (index.styl) to gather these parts in their proper positions by using the file import ability of Stylus.

    // partials
    @require '*_'

The above is a simplified sample that serves to illustrate a method for including all the partials in a given directory (sectional level) by using @require (figure 4.16). Whilst taking in mind the cascade, the partials are set to only be targeted as children of .home as instructed by the indentation.


 logic ─┐
          ├─  root.js
          ├─  libs ─┐
          |           ├─  math.js
          |           ├─  types.js
          |           ├─  animation.js
          |           └─  external ─┐
          |                           └─  lib-a ─┐
          |                                        ├─  x.x.x.js
          |                                        └─  x.x.x.min.js
          └─  modules ─┐
                         ├─  form.js
                         ├─  scroll.js

Scripted functionality is contained in logic. While it is set up for JavaScript development by default, it may also be used to house logic from other languages (Fig. 5.1).


Your project's JavaScript is managed from the logic directory. Its immediate children are root.js, libs and modules.


// general functionality
import { routine } from 'modules/module';

// component logic
import header1 from 'headers/1/_';

// external package from node_modules
import 'fancylib';

// manage execution
document.addEventListener('DOMContentLoaded', () => {

As the main entry point for your app's JavaScript, root.js consume both the project's internal logic as well as NPM dependencies.


The libs folder contains the respective native and 3rd party libraries that your app consumes. It aims to be a collection of utility modules such as math.js and types.js (fig. 5.1). They are separated by type and consist of low-level, more "atomic" functionality that essentially derives its usefulness from being part of larger compilations. They serve to make your code DRY by being shared between higher level functionality in modules and components. Utilities may also be interdependent. Note that any library not inside the external folder are automatically treated as internal, or "native".


external contains your project's external (3rd party), client-side libraries that are not provisioned with a package manager like NPM.


Logic that are neither part of a component nor qualify as a simple utility should be placed in modules. You decide how modules are structured as it may depend on factors beyond the framework's reach, such as the development pattern in use and the scale of your project.

Included modules

Platframe ships with some built-in modules.


The form module can be used for transmitting form data to a server. It supports version 3 of Google's reCAPTCHA service which is a less intrusive and more user-friendly method of spam prevention.

import Form from 'modules/form';

document.addEventListener('DOMContentLoaded', () => {

    // instance that handles the contact page form
    new Form({
        id: 'contact_1', // optional
        form: '.form-contact', // optional: target a form
        carrier: 'xhr', // optional: uses fetch() by default
        success: '/contact/success.html', // optional: path or function
        failure: '/contact/failure.html', // optional: path or function
        progress: doWhileSending, // optional: function
        destination: '', // FQDN of server
        recaptcha: 'site API key for reCAPTCHA v3', // optional


While the id key is optional, it serves two purposes: it identifies as the action for Google's reCAPTCHA service, and can also be used to identify the particular form on your server. Since id is also used by reCAPTCHA it is constrained to the characters A-Za-z/_. To activate the built-in Google™ reCAPTCHA v3 support, set your "site" API key as the value of the recaptcha property.

If you only have a single form on the site you may leave the form key out as that form will automatically be selected. For sites with more than one form, be sure to assign a different selector to each instance of Form.

Form uses the browser's FormData API, so ensure that your server is prepared to handle its multipart/form-data encoding. The server should return a JSON object with a responseCode of 0 for success, and any non-zero value if sending failed. In accordance with the server's response, Form will execute either success or failure on the client side.

Be sure to check out Platframe's form service for a compatible API.


The scroll module currently only enables smooth scrolling for in-page anchors.

import { smooth } from 'modules/scroll';

document.addEventListener('DOMContentLoaded', () => {




The images directory prescribes a logical grouping of assets against their respective scope and function, ensuring a flexible, yet intuitive file system. It forks into subject groups, which in turn are organized by implementation identifiers.

 images ─┐
           ├─  global ─┐
           |             ├─  brand ─┐
           |             |             _symbols ─┐
           |             |                         └─  logo.svg
           |             └─  _linked ─┐
           |                            └─  favicon.png
           ├─  home ─┐
           |           ├─  icons
           |           |  └─  _views ─┐
           |           |                ├─  icon-1.svg
           |           |                └─  icon-2.svg
           |           ├─  _inline ─┐
           |           |              └─  background.jpg
           |           |
           |           └─  _linked ─┐
           |                          └─  image.webp
           └─  about ─┐
                        └─  _linked
                           └─  _verbatim ─┐
                                            ├─  person-1.webp
                                            ├─  person-2.webp
                                            └─  person-3.webp


Subject directories do not begin with an underscore and should receive names that indicate the context they represent. For instance, a global folder may be created to house images that are used across the project, while the images in home or about are specific to those views.

In figure 6.1 there are three subject groups, global, home and about. The brand and icons folders represent additional nested subject groups. These “offspring” subjects can be used as necessary for added clarification.


The build system determines the appropriate image processing steps by inferring the implementation type from the directory name. To signify their significance, avoid naming collisions with subject groups, and speed up visual grepping of the file system, implementation directories are prefixed with an underscore.

There are currently four implementation types under which image assets that share a particular type may be grouped. These are _inline and _linked for discrete graphics, as well as _symbols and _views for vector sprites. We'll explore each of them in the next sections.


To designate images for inlining into either markup or styles, place them in a folder named _inline within images. A relative path to src is required when referencing these assets. Additionally, to inline an image in your markup you'll need to add inline as an attribute of the element in question.

Usage within markup

// embed a vector as SVG
img(src="images/section-a/_inline/graphic.svg" inline)

// embed a bitmap as base64
img(src="images/section-b/_inline/picture.png" inline)

Usage within styles

background: url("images/section-c/_inline/drawing.svg")


Earmark discrete graphics that are linked to from within the source by grouping them in a _linked directory (fig. 6.1).


Sometimes you might choose to hand-optimize critical images, and ideally prevent them from being touched by the build system's automated minification process. You can achieve this in Platframe by placing these images in a _verbatim directory. Refer to the "about" folder in figure 6.1 for an example.


Vector sprites are supported through both its view and symbol variants. For example, to add icon.svg to the project's “symbol” sprite, place it in a folder named _symbols, or to add it to the “view” sprite, place it in a _views folder in stead.

Continuing with the example above, the image in brand will be appended to the symbols sprite and the images in icons will be added to the view sprite. The resulting sprite sheets are consumed from the top-level _sprites within the respective build's /assets/images.

Usage within markup

svg: use(xlink:href="/assets/images/_sprites/symbols.svg#icon")

Usage within styles

background: url("/assets/images/_sprites/views.svg#icon")

Platframe does not support traditional raster sprites. If your project require such a sprite sheet format, you may either extend the existing tooling or produce it manually and place it in a _linked folder from where you can source it.


 fonts ─┐
          ├─  font-1 ─┐
          |             ├─  black.woff
          |             ├─  bold.woff
          |             ├─  light.woff
          |             └─  regular.woff
          └─  font-2

Fonts are contained in the fonts folder and managed from the fonts module in style. By default the platform is setup to work with a typography CDN in production. This means that the local type assets contained in fonts are to be included for development purposes only and are not transferred to the production build. This avoids potential connectivity issues that slows down development.

Font stacks are managed through the configuration files in the stacks module within styles. Production builds use Google™ Fonts as the default font delivery service, and can be managed with head.pug

Source® Sans Pro is the default font since version 2.


See the project's GitHub page for more on contributing.