Get started

Install

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

2.

  npx degit platframe/new my-app

  or download the latest release

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

Develop

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

Test

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

Deploy

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

Architecture

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
Platframe's Frontend Architecturedir.js.pug.svg.stylassets*/*scriptsimagesfontsstylesviewssectionsub-section__views__modulespluginselementscategorycomponentimagesjslibsnode_moduleslib-aintsubject-afont-afont-b_symbols_views_inlinesrcdevprdsectionsub-sectionmodulesext_linksubject-btemplatesstylesheadbodymixinsindexrootindex__indexrootindex__indexindex____componentslogic_index_rootimagesfontsindexroota****

root

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.

src

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.

dev

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

prd

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

Templates

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.

Global

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, metadata, various project assests 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.

Root

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.

Head

head

    meta(charset="utf-8")

    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="//cdn.example.com/lib.css")
        link(rel="stylesheet" href="/assets/style/root.css")

    block scripts
        - if (ENV === 'development')
            script(src="/assets/logic/js/libs/lib-a/x.x.x.js")
        - else
            script(defer src="//cdn.example.com/lib/x.x.x.js")
        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>.

Body

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 superceding 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

Local

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.

Views

extends ../_/body

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

block main

    main(role="main")

        section.canvas

            .content

                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 inhereted 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.

Nesting

 views─┐
         ├─  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.

Partials

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.6 illustrates the former method, while figure 3.7 shows the latter. It is recommended to only use the prefix method (figure 3.7) when dealing with content partials.

Global

 templates ─┐
              ├─  root.pug
              ├─  views
              └─  _ ─┐
                       ├─  mixins.pug
                       ├─  head.pug
                       └─  body.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 _: mixins.pug, head.pug and body.pug.

Local

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.

Sectional

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.

Content
 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.

Styles

 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.

Global

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.

Root
@require '_/modules'
@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.

Partials

_ 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 plugins which in turn makes all component styles available for use. Up next is elements from where we manage the styles of features that are common to all views.

Modules

 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.

Color

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.

Palette

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.

Schemes

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
.class
    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.

header
    color: scheme.header.default
    svg
        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

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

Conventions

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%
Mixins

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.

Color

schemer(scheme)

Provides the ability to supercede 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.

body(class="fancy")
.fancy
    schemer(gray)

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
.special-links
    anchors(gray)

// override link values in scheme scope
.dark-footer-links
    anchors(dark, footer)
Conversion

rem(32px)

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

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

— Dimensions

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

Media query helpers are provided to allow granular control for a mobile-first 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
+min(1)
// pass an arbitrary literal
+max(500px)
// target range between 2nd & 3rd breakpoints
+between(second, third)

— Resolution

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

Target the platform's pixel density (fig. 4.12).

// dots per pixel
+min-res(2x)
// dots per inch
+max-res(400dpi)
Grid

grid(width)

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
.example-1
   grid(12)

// 3 columns & active gutter
.example-2
   grid('third', true)

// 2 columns, active gutter, override gutter
.example-3
   grid(6, true, 2%)

Plugins

 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

 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.

Local

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

Views

 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.

.home
    // 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

 logic ─┐
          ├─  js ─┐
          |         ├─  root.js
          |         ├─  libs ─┐
          |         |           ├─  int ─┐
          |         |           |          ├─  animation.js
          |         |           |          ├─  math.js
          |         |           |          ┆
          |         |           |
          |         |           └─  ext ─┐
          |         |                      └─  lib-a ─┐
          |         |                                   ├─  x.x.x.js
          |         |                                   └─  x.x.x.min.js
          |         └─  modules ─┐
          |                        ├─  module.js
          |                        ┆
          ├─  lang-x
          ┆

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).

JavaScript

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

Root

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

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

// external package from node_modules
import 'fancylib';

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

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

Libs

The libs folder is a direct child of the parent language directory and contain the respective native (int) and 3rd party libraries (ext) that your app consumes.

Internal

The native library is made up by a collection of utility modules contained in int (fig. 5.1). They are separated by type and consist of low-level, "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 interdependant.

External

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

Modules

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.

Images

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
           |             └─  _link ─┐
           |                            └─  favicon.png
           |
           ├─  home ─┐
           |           ├─  icons ─┐
           |           |             _views ─┐
           |           |                       ├─  icon-1.svg
           |           |                       └─  icon-2.svg
           |           └─  _inline ─┐
           |                          └─  background.jpg
           └─  about ─┐
                        ┆

Subjects

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.

Implementations

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 _link for discrete graphics, as well as _symbols and _views for vector sprites. We'll explore each of them in the next sections.

Inline

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 _link directory (fig. 6.1).

Sprites

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")
Rasters

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 _link folder from where you can source it.

Fonts

 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 transfered 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.

Contribute

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