Deployment
A component is a collection of related modules that provides specific
functionality. Located in components
, they are grouped by category
and indexed numerically. Once a new component package is placed in its
respective parent category, all that remains is plugging it into your project.
Components are deployed by implementing its constituent modules. We'll start by covering markup as it's a core module and essential to all components.
Markup
include ../../components/category/1/_
The Pug portion of the component is provisioned for project-wide use by
including it in mixins.pug
, the global partials file of
templates
(Fig. 8.1).
// keep the defaults
+header-1()
// or customize with modifiers
+header-1("/#home", "logo", "Heading", [
{
href: "/about/",
name: "About"
}
])
Once included, call the component at an appropriate location in the target document. If the mixin accepts any modifiers, you can pass arguments to customize the output.
Styles
Style files within components
will be picked up by the Stylus
interpreter and available for use throughout the project's core styles.
// keep the defaults
header-1()
// overriding the default color scheme
header-1(scheme)
// call as block mixin to override more specifically
+header-1(arg1, arg2: this)
h1
color: gray.header
border-color: #333
background: _20
Components receive their styling by attaching the similarly named mixin to them. Each component is labelled with a unique class that is used for tying it to the styles. To provide room for adaptation, you can instantiate the mixin in a few ways. Figure 8.2 illustrates the different methods of applying the styles to its component.
Images
Component images should not require any additional configuration in order to work. Authors include the original images so it can be altered to suit the host application if necessary. It is best to keep the images directory structure unchanged after modifying any graphics as the build system depends on its conventions for determining sprite options.
Scripts
Platframe components make their JavaScript available for consumption by way of the standardized ES2015 module definition.
// get the component's logic
import header from 'headers/1/_';
// typical initiation scenario
document.addEventListener('DOMContentLoaded', () => {
header();
});
To activate a component's logic, import its script module into
root.js
, located in src/logic/js
. Be sure to initiate any
DOM-dependant packages appropriately (fig. 8.3).
Creation
A Platframe component is the result of packaging the dependencies of existing, specific functionality into discreet payloads that are easy to consume by other developers.
As a structural scaffolding system first, Platframe is opinionated on architectural conventions. However, it is less prescriptive when it comes to how you write your code. Not only does this allow developers to exercise their preference in language methodology, but it also allows components that are specifically built for Platframe to be used in other (non-native) projects, with little to no adaptation needed. Of course, the only requirement is that Pug, Stylus and in some cases ES6 must be part of the target app's frontend stack.
The gist
Depending on the type and complexity of a component it generally consist of the following parts:
- Markup* (Pug)
- Style* (Stylus)
- Graphics (vector/raster)
- Logic (JavaScript)
The most basic components should contain at least the first two layers: markup and style.
Components should be complete in the functionality or purpose they aim to achieve, and fall within one of the 6 component types (more types may be added). Whether you're building a header, footer or a form, ensure that the component provides all the necessary functionality, styling and assets for production usage. Only stylistic changes should be necessary as the component is likely to be thematically adapted to be consistent with the host project's appearance.
Naming
Component names should conform to the type-{num}
format. The number part
gets incremented with each new component added to the category in question. For
instance, if the last form component was number 4, the subsequent one will
be ID'd as form-5
. This ensures quick and unique identification.
Markup
The markup portion of a component can be static (imported "as is") or it can be encapsulated within a mixin. Most component's markup can be sensibly abstracted into a mixin and it's also the ideal way for wrapping the structural (HTML) part as it facilitates reuse and hide unnecessary detail.
Mixin
While there's no restriction on the amount of arguments your component's Pug
mixin may take, there is one that is compulsory and that - unless you're making
use of the rest parameter syntax - should always be specified last. It is
the hook
parameter that allow users to add an additional style hook in
the form of a CSS class, should they require additional specificity.
Attributes
The only required attribute on components is the default CSS class that is a verbatim reference to its name. This attribute should be applied to the component's root element and be included by way of a ternary expression that allows the optional style hook to be chained (figure 8.4).
mixin component-x(param1, param2, hook)
div(class=hook ? "component-x " + hook : "component-x")
// body...
You may choose to avoid using a mixin in the case where, due to the scope, size or complexity of the component it is not feasible or may defeat the purpose of the abstraction. For example, the markup of a complex footer component is generally not a good candidate for mixin encapsulation (figure 8.5).
footer(class="footer-x")
// body...
Styles
Components should be styled in a responsive manner as all platforms should be treated as 1st class citizens in relation to the user's experience and the app's appearance.
Mixin
Encapsulate styles within a
block mixin.
A mixin is necessary to allow the component to accept modifiers while a
block
adds a transparent interface for end users with which to override
the styling of a particular instance of a component without altering the
original which may be called in more than one place. To allow the component to
either inherit the global scheme or receive an arbitrary one, it should have at
least a scheme
parameter that defaults to the project's primary scheme.
Selectors
Attach the styling to a class-selector with the same name as the component it belongs to. This ensures that it gets associated with its sibling markup. Avoid hard coding color values into your component as it lessens interoperability. In stead, assign color by referencing the scoped identities from the framework's scheme hash format. This allows your component to receive the appropriate paint from the context of the active scheme.
component-x(scheme = __default)
.component-x
// declaration...
// border: 1px solid scheme.border
// background: scheme.background
{ block }
Images
If your component will make use of image assets, it should be bundled with the
distributable package, specifically in its images
directory. You'll
need to decide on the most appropriate form of implementation, a decision that
will largely depend on the nature of your componemt, i.e. whether it will be
used across the application or be confined to one particular location and so
forth. Images can either become part of a project's global sprite sheet
(for SVG's), be embedded as inline images, or get linked. Refer to
the section on images for the difference between
these techniques.
SVG assets that should be included in a project's SVG sprite sheet(s), must be
contained in either a view
or symbols
folder that is
housed within the sprite
folder in images
(refer to
figure 8.3). This allows the build system to determine which SVG's are destined
for which type of sprite sheet and produce it accordingly.
Embedded images should be placed in a directory named inline
. To
prepare assets for embedding, the build system will produce minified versions of
SVG's while rasters will be encoded as base64 during a development build.
Scripts
A component with JavaScript functionality should wrap its logic in the
standardized ES6 module syntax. Where possible, use default
as the
export method - it allows for easier consumption with a more transparent
interface (Fig 8.4 & 8.5).
// category/1/_.js
export default function() {
// do many things
}
// use preferred indent. in root.js
import allthethings from 'category/1/_';
Functionality should be written exclusively for the component by only addressing its needs and avoiding any excess weight. Avoid the inclusion of external dependencies if possible (eg. 3rd-party libs). If deemed unavoidable, extract the necessary functionality for inclusion, taking care to do so in a way that respects the author's license.
Structure
headers ─┐
├─ 1 ─┐
| ├─ images ─┐
| | └──── _symbols
| ├─ _.js |
| ├─ _.pug ├─ header-1-logo.svg
| └─ index.styl └─ header-1-mobile.svg
|
├─ 2
├─ 3
┆
Component directory structure follows a predictable pattern. As an example, we'll be looking at how the 1st header component is structured (figure 8.3).
To simplify indexing, we only distinguish between major component types. Currently, these are:
- Banners
- Footers
- Forms
- Headers
- Navigation
Within the above categories, individual components are numerically separated, incrementing only the component number with each new addition.
JavaScript and Pug files should be named with a single underscore (fig. 8.3
). Name your main stylesheet index.styl
to allow Stylus to
find and process it. Image files should be arranged as explained above in the
section on images.