Get started

Install

1.  Ensure that you have Node.js and NPM or Yarn on your machine.

2.

ⓐ  Download the latest release or

ⓑ  git clone https://github.com/platframe/platframe.git project

3.  In the root of your project, run  yarn  or  npm install

Develop

Spin up a dev environment with  grunt develop

By default, the auto-sync development server runs at  localhost:3003

Test

Run your test suit with  grunt test

Build, test and locally serve a production build for inspection:  grunt preflight

Deploy

For an optimized, production-targeted build:  grunt build

Deployment logic can be run with:  grunt deploy

Architecture

The platform aims to scale seamlessly with consistent patterns for each part of the frontend stack. The following is a diagram of the basic architecture (fig. 2.1).

architecture 2.1
diagram of Platframe's architecture

root

The root directory contains your project. It is the parent of the src, dev and prod 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.

prod

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
html(lang="en")

    include _/mixins

    block context

    include _/head

    block body

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

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

Next, with block context we reserve a "context" block that will allow us to inject variables that can be consumed by all the descendants of the root HTML tag. For instance, this is useful for changing the page title in the HEAD and set ID and CLASS attributes on the BODY element of each page.

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

    block scripts
        - if (env === 'dev')
            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.

Style

🖿 style ─┐
          ├─ 🖿 _
          ├─ 🖿 views
          └─ 📄 root.styl

The style 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 style 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 ─┐
            ├─ 📄 index.styl
            ├─ 📄 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
            |             |
            |             ├─ 📄 index.styl
            |             ├─ 📄 color.styl
            |             ├─ 📄 conversion.styl
            |             ├─ 📄 effects.styl
            |             ├─ 📄 interaction.styl
            |             ├─ 📄 layout.styl
            |             └─ 📄 text.styl
            |
            └─ 🖿 text ─┐
                        ├─ 📄 index.styl
                        ├─ 📄 stacks.styl
                        ├─ 🖿 domains ─┐  🠆 defaults for markup domains
                        |              |
                        |              ├─ 📄 body.styl
                        |              ├─ 📄 headings.styl
                        |              ├─ 📄 paragraphs.styl
                        |              ├─ 📄 anchors.styl
                        |              ├─ 📄 lists.styl
                        |              ├─ 📄 forms.styl
                        |              ├─ 📄 monospaced.styl
                        |              └─ 📄 modifiers.styl
                        |
                        └─ 🖿 fonts ─┐
                                     ├─ 📄 font-1.styl  🠆 face rules
                                     ├─ 📄 font-2.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
Layout

🄼  +min(value) | +max(value) | +between(value, value)

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 interstice between 2nd & 3rd breakpoints
+between(second, third)
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 { function } 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 own logic as well as NPM dependencies, permitting that they use the standard (ES6) module syntax. While Platframe does not support idiosyncratic methods like CommonJS out of the box, it can be enabled.

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 forks into sprite, inline and linked. This allows for the grouping of image assets based on their respective scope and function. While optional, it helps to keep assets organized with subject directories that are children of the aforementioned trio.

🖿 images ─┐
           ├─ 🖿 sprite ─┐
           |             ├─ 🖿 icons ─┐
           |             |            🖿 views ─┐
           |             |            |         ├─ 🖺 icon-1.svg
           |             |            |         ├─ 🖺 icon-2.svg
           |             |            |         └─ 🖺 icon-3.svg
           |             |            |
           |             |            ├─ 🖻 icon-1.png
           |             |            └─ 🖻 icon-2.png
           |             |
           |             └─ 🖿 brand ─┐
           |                          🖿 symbols ─┐
           |                                      ├─ 🖺 logo-1.svg
           |                                      └─ 🖺 logo-2.svg
           ├─ 🖿 inline ─┐
           |             ├─ 🖿 home ─┐
           |             |           ├─ 🖿 hello ─ 🖺 hello.svg
           |             |           |
           |             |           ├─ 🖿 story ─ 🖻 story.png
           |             |           |
           |             |           └─ 🖿 thend ─ 🖼 thend.jpg
           |             |
           |             └─ 🖿 about ─┐
           |                          ├─ 🖺 graphic.svg
           |                          └─ 🖼 founder.jpg
           |
           └─ 🖿 linked ─ 🖻 favicon.png

Subject groups

A subject directory serves to demarcate our image assets. If the asset is used across the project, it gets separated based on its function, otherwise according to the page it's used on in the case of inline images. This helps to make our file system more intuitive and speeds up both development and maintenance as less time is spent navigating and managing resources.

In figure 6.1 sprite and inline each contain two subject directories: icons and brand in the former and home and about in the latter.

Sprite

Sprites are home to images that are not confined to a particular section or document and are used throughout the project.

Vector

Global SVG's are compiled into sprite sheets. Depening on the intented use of the images, you may choose a particular flavour of sprite to be used exclusively or in tandem with another.

Continuing with the example above, the images in icons will become part of the view sprite and those contained in brand will populate the symbols sprite.

Raster

Rasters (png, jpg, webp, gif) can be placed in the same subject directory as SVG's, however not within a view or symbol folder as these are reserved for vectors. Note that the build system does not support sprite creation for rasters. If your project requires a raster-based sprite sheet, you can either extend the existing tooling or produce it manually and place it in the linked folder - see below.

Inline

Images with a narrow, document-level scope are often inlined. They should be placed in the inline directory (see figure 6.1). Inline graphics will not end up in a production build as they should be embedded in the development stage.

Linked

Some situations may call for a graphic to be made available as a discrete asset that can be cached and referenced by means of a URL. To earmark graphics for linking, group them in the linked directory (fig. 6.1).

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

If your project makes use of custom fonts, modify head.pug to configure your provider. Platframe uses Google™ Fonts as the default font service in production.

Contribute

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