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.

Sharing

Components are pluggable units of functionality, making them shareable between projects that use Platframe's infrastructure.

You may also contribute a component you've built to Platframe. Once approved, it will become part of the default collection distributed with the framework. We'll cover the necessary steps next.

Note on theming

If you're considering contributing your component back to Platframe, it is a good idea to keep the appearance of your component relatively consistent with the current major iteration of Platframe's theme and general style. Doing so helps to ease integration of the component into existing Platframe-based projects. As the framework does not have an official style guide yet, you'll have to take a look at the existing collection of components for current theme pointers.

Testing

Test the component in a fresh copy of Platframe to ensure that it integrates seamlessly with the framework.

Documentation

Use the current component documentation as a reference for writing your own. Extend the appropriate component category in the documentation repository, using the existing template as a foundation.

Submission

Once tested and documented, you'll need to submit 2 pull requests. For the documentation, submit a PR the the documentation repository. For the component, choose the appropriate category in components and submit a PR to the distribution repository.