Get started
Install
1. You'll need Node.js and NPM or Yarn
3. cd my-app && git init && yarn
Develop
Spin up a development environment with yarn develop
By default, the auto-sync development server runs at localhost:3000
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 build
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).
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, 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
.
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 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
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 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
.
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.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.
Global
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
.
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
.
Mixins
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.
Data
analytics(id)
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')
+analytics('G-7RQFC9MDHK')
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 '_/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.
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 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
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.
index.styl
→ pulls in the dependantspalette.styl
→ tie color values to special scheme variablesdictionary.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 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.
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
+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
+min(1)
// pass an arbitrary literal
+max(500px)
// 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
+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%)
Utilities
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
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 ─┐
├─ 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).
JavaScript
Your project's JavaScript is managed from the logic
directory. Its immediate children are root.js
, libs
and modules
.
Root
// 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', () => {
header1();
});
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 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
external
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.
Included modules
Platframe ships with some built-in modules.
Form
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: 'https://some-forms-api.com', // 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.
Scroll
The scroll module currently only enables smooth scrolling for in-page anchors.
import { smooth } from 'modules/scroll';
document.addEventListener('DOMContentLoaded', () => {
smooth();
});
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
| └─ _linked ─┐
| └─ favicon.png
|
├─ home ─┐
| ├─ icons
| | └─ _views ─┐
| | ├─ icon-1.svg
| | └─ icon-2.svg
| ├─ _inline ─┐
| | └─ background.jpg
| |
| └─ _linked ─┐
| └─ image.webp
└─ about ─┐
people
└─ _linked
└─ _verbatim ─┐
├─ person-1.webp
├─ person-2.webp
└─ person-3.webp
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 _linked
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")
Linked
Earmark discrete graphics that are linked to from within the source by
grouping them in a _linked
directory (fig. 6.1).
Verbatim
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.
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 _linked
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 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.
Contribute
See the project's GitHub page for more on contributing.