Computacenter

This repository contains the source files and documentation for the Computacenter CSS framework. The source files are compiled down to a CSS bundle and a JavaScript bundle which may then be imported and consumed on a production website.

Contents

  1. Methodology
  2. Structure
    1. Files
      1. Documentation
      2. Front-end framework
    2. Folders
  3. Technologies
    1. Node.js
    2. Grunt
    3. Twig.js
    4. Sass
  4. Usage
    1. On a production website
      1. Imports
    2. Development
      1. Prerequisites
      2. Getting started
  5. Contributing
    1. Creating Elements and Widgets
    2. Extending and modifying components
    3. Making changes to global visual styles
      1. Design tokens
      2. Sass variables
      3. Sass maps
    4. Using styles, components and patterns

Methodology

This framework uses the major concepts and practices of Atomic Design and Design Systems, such as:

  1. Tokenization/abstraction of styling into global variables
  2. Repetition and reuse of designs through modular, self-contained components (in this instance, Elements and Widgets)
  3. Consistent, logical grouping of design assets
  4. Rapid interface composition through mixing-and-matching components into templates and patterns



Back to top ⇧


Structure

Files

All files in this repository are associated with one of the following products:

Documentation

  • All *.twig files relate exclusively to the documentation. These have been used to quickly generate static HTML mark-up for demo pages, documentation pages and element/widget examples. These files are not the components themselves - they are for exploring and representing the HTML structure and possible variations in the HTML structure.

Front-end framework

  • All *.scss files relate to the front-end framework. These files are compiled down to a single bundle.css file and provide functional styling through class names and other such attributes.
  • All *.js files relate to the front-end framework. They are compiled to a single bundle.js file which provides the functionality for specific component interactions - such as opening a modal window, or animating a number counter.
  • Static assets such as fonts and icons, which will need to be hosted alongside the css and js bundles.

Folders

The src folder contains all development files. After following the steps in getting started we will find the compiled and ready-to-use files in the dist folder.

╻ ┣━━┳╸dist ┃ ┡━━┯╸build ┃ │ ├───╴bundle.css ┃ │ ╰───╴bundle.js ┃ ╰───╴┅ ┃ ┡━━┳╸src │ ┣━━┳╸assets │ ┃ ┣━━━╸data │ ┃ ┣━━━╸fonts │ ┃ ┣━━━╸images │ ┃ ┣━━━╸js │ ┃ ┗━━┳╸scss │ ┃ ┣━━┯╸global │ ┃ ┃ ├───╴core.scss ╶╴ Base styles │ ┃ ┃ ├───╴library.scss ╶╴ Documentation styles │ ┃ ┃ ╰───╴utilities.scss ╶╴ Utility classes │ ┃ ┃ │ ┃ ┣━━┯╸helpers │ ┃ ┃ ├───╴functions.scss │ ┃ ┃ ├───╴mixins.scss │ ┃ ┃ ╰───╴predefined-layouts.scss ╶╴ Core layout @mixins │ ┃ ┃ │ ┃ ┣━━┯╸imports │ ┃ ┃ ╰───╴imports.scss ╶╴ Font imports (@font-face) │ ┃ ┃ │ ┃ ┣━━┯╸objects │ ┃ ┃ ├───╴form.scss ╶╴ Default <form> styles │ ┃ ┃ ╰───╴┅ │ ┃ ┃ │ ┃ ┣━━┯╸tokens │ ┃ ┃ ╰───╴tokens.scss ╶╴ Global variables, settings and aliases │ ┃ ┃ │ ┃ ┗━━━╸reset ╶╴ Overrides default browser styles │ ┃ │ ┣━━┳╸components │ ┃ ┣━━┳╸elements │ ┃ ┃ ┡━━┯╸button │ ┃ ┃ │ ├───╴button.scss ╶╴ Element styles │ ┃ ┃ │ ╰───╴button.twig ╶╴ Element Twig template │ ┃ ┃ ╰───╴┅ │ ┃ ┃ │ ┃ ┗━━┳╸widgets │ ┃ ┡━━┯╸header │ ┃ │ ├───╴header.scss ╶╴ Widget styles │ ┃ │ ├───╴header.twig ╶╴ Widget Twig template │ ┃ │ ╰───╴header.js ╶╴ Some Widgets have associated JavaScript │ ┃ ╰───╴┅ │ ┃ │ ┣━━┳╸pages │ ┃ ┡━━┯╸.modules ╶╴ Reusable content blocks │ ┃ │ ├───╴related-services.twig │ ┃ │ ╰───╴┅ │ ┃ │ │ ┃ ├───╴home.twig ╶╴ Becomes 'dist/home/index.html' │ ┃ ├───╴careers.twig ╶╴ Becomes 'dist/careers/index.html' │ ┃ ├───╴news.twig ╶╴ Becomes 'dist/news/index.html' │ ┃ ╰───╴┅ │ ┃ │ ┡━━┯╸templates │ │ ├───╴html.twig ╶╴ Outermost template (<html> and <body> tags) │ │ ├───╴page.twig ╶╴ Page template │ │ ╰───╴┅ │ │ │ ├───╴main.scss ╶╴ Entry point for compiling all Sass files │ ├───╴main.js ╶╴ Entry point for compiling all JavaScript files │ ├───╴util.js ╶╴ JavaScript helpers │ ╰───╴util.twig ╶╴ Twig helpers │ ├───╴package.json ╰───╴┅



Back to top ⇧


Technologies

Node.js

Node.js is an open-source JavaScript runtime environment that executes JavaScript code outside a web browser.

Node.js’ package ecosystem, npm, is the largest ecosystem of open source libraries in the world. These packages are the backbone of the repository.

nodejs.org

Grunt

Grunt is a tool for running sets of tasks in Node.js. It is the Node.js stack equivalent of ANT for Java.

We use Grunt to handle:

  • Spinning up a web server
  • Reloading the browser automatically whenever a file is saved
  • Using preprocessors such as Sass to compile our CSS
  • Optimizing assets like CSS, JavaScript, and images

The file Gruntfile.js handles the configuration and the grunt folder contains the individual task configuration files. Tasks are run via npm scripts, which we can find in the file package.json.

gruntjs.com

Twig.js

Twig.js is a pure JavaScript implementation of the Twig PHP templating language.

Under the hood, this repository uses Twig.js to render static HTML files as part of the Grunt build process. The files in this repository use many complex Twig features — if unfamiliar, please refer to the official documentation.

Logic delimiters

{% ... %}

Iteration and looping with for:

<ul> {% for user in users %} <li>{{ user.username|e }}</li> {% endfor %} </ul>

Conditionals like if, elseif and else:

{% set size = "small" %} <div> {% if size == "big" %} <h1>Big title</h1> {% elseif size == "small" %} <h6>Small title</h6> {% else %} <h3>Default title</h3> {% endif %} </div>

Variables with set:

{% set foo = "foo" %} {% set foo = [1, 2] %} {% set foo = { foo: "bar" } %}

Control structures with block and extends:

{% extends "base.html" %} {% block content %} <h1>Index</h1> <p class="important"> Welcome to the homepage. </p> {% endblock %}

Expression delimiters

{{ ... }}

Renders the result of an expression:

There are {{ 60 * 60 * 24 }} seconds in a day. {# There are 86400 seconds in a day. #}

This can also be used with variables:

{% set greeting = "Hello " %} {% set name = "Fabien" %} {{ greeting ~ name|lower }} {# Hello fabien #}

Or with custom functions:

{% set Button = "src/components/elements/button/button.twig" %} {{ i(Button, { text: "Read More" }) }} {# inserts "button.twig" with the variable "text" defined as "Read More" #}

Comment delimiters

{# ... #}

Any text inside these delimiters will be ignored by the compiler:

{# note: disabled template because we no longer use this {% for user in users %} ... {% endfor %} #}

github.com/twigjs

Sass

Sass is a CSS pre-processor that lets us use variables, nested selectors, mathematical operations, mixins, functions and imports while writing CSS.

A CSS preprocessor is a scripting language that extends CSS by allowing developers to write code in one language and then compile it into CSS. Sass is perhaps the most popular preprocessor around right now, but other well-known examples include Less and Stylus.

The files in this repository use many complex Sass features — if unfamiliar, please refer to the official documentation.

Syntax

Sass looks like this:

$button-height: 44px; $button-color: #FFFFFF; $button-background: #D56483; @mixin button($color: $button-color, $background: $button-background) { @include font(button); @include flex(inline, center); height: $button-height; color: $color; background-color: $background; position: relative; cursor: pointer; outline: 3px solid transparent; &:hover { color: $background; background-color: transparent; outline-color: $background; } } button { @include button(#000000, #91D68E); }

sass-lang.com



Back to top ⇧


Usage

On a production website

In order to correctly render the whole framework, we will need to include the following dist files:

  • CSS: Compiled, prefixed and minified to bundle.css
  • JavaScript: Transpiled and minified to bundle.js
  • Fonts: fonts/**/*.otf|woff

We may also wish to include the following optional dependencies:

  • Ionicons: A flexible, robust and open source icon library which is consumed via a CDN (content delivery network) as a <script> imported ES6 module. If desired, this library can be replaced by inline SVG brand icons. Learn more
  • jQuery: A library for HTML document traversal and manipulation, event handling and animation. Any component functionality that requires jQuery is bundled in bundle.js, but we do recommend having the library available. Learn more
  • Flag Icons: A collection of all country flags in SVG with CSS for easy integration. Learn more

Imports

CSS should be imported in the document <head>

<head> <link rel="stylesheet" href="/path/to/bundle.css"> </head>

JavaScript should be imported anywhere after the CSS <link>

<head> <script defer src="/path/to/bundle.js"></script> </head>

The font files are imported for us within the CSS but the fonts folder in dist will need to be copied into the root directory of our website.

Development

Prerequisites

If not already installed, we will need Node.js and Node.js Package Manager (npm). This process is operating-system specific - please follow the official guide.

We will also need a modern browser like Microsoft Edge, Google Chrome, Mozilla Firefox, or Safari.

Getting started

From a command-line interface (Terminal, PowerShell, etc.) navigate to the base of this repository:

cd path/to/this-repository

Install the core dependencies using npm:

npm install # or yarn install - if using yarn

Start the development environment and generate the dist folder:

grunt

Inside dist we can now find our static HTML and finished assets.

Lastly, Grunt will launch the default browser and open the site (if not, open http://localhost:5000) - it will rebuild and reload in response to any src/**/* file changes while grunt is running.



Back to top ⇧


Contributing

Creating Elements and Widgets

Step 1

Decide if the component is an Element or Widget and navigate to the associated folder - src/components/elements for Elements, src/components/widgets for Widgets.

Step 2

Create a new folder here with the name of our component (we'll use my-component as an example). Keep in mind, that this name is also used for the CSS class selector.

Step 3

Inside this folder create two new files, one for Sass and one for Twig. Name both files after the component (in this case, my-component.scss and my-component.twig).

Step 4

Now we'll write the Twig template. This is at our discretion but existing components use the following structure (in order):

Documentation title and short description (markdown)

Comments of any kind are normally ignored, however for component files the first comment in the file is treated as the text to insert at the top of the component's documentation page. It processed as Markdown.

{# # My Component A short description of the component written in **Markdown**. This text appears at the top of component's documentation page. [Link to something](https://my-component.com/link-to-something). #}
Import utility functions like i( ... ) and v( ... )

Util is a variable, its value is the absolute path "/src/util.twig".

r is a filter function that takes the value preceding it as an argument.

Twig only accepts relative paths so the expression Util | r converts Util to a relative path from my-component.twig to /src/util.twig.

The result is "../util.twig".

{% from Util | r import i %} {# import the i() function from "../util.twig" #} {% from Util | r import v %} {# import the v() function from "../util.twig" #} {% from Util | r import i, v %} {# import the i() AND v() functions from "../util.twig" #}
Handle any variables that the component expects to receive

Here, we often use logical operators like ?, ??, ?:, : and ~. Check out the official Twig documentation on the syntax.

{% set color = color ? color : "blue" %} {# if color isn"t defined, it"s set to blue #} {% set id = id ? "id='" ~ id ~ "'" : "" %} {# if id is defined, it's formatted like: id="example-id" #}
An if statement to switch between the component itself and the component documentation
{% if not docs %} {# Component's normal HTML goes here (see below) #} {% else %} {# Variations for the component's documentation go here (see below) #} {% endif %}
The component's normal HTML

cx( ... ) is a custom function that joins classes together. In this example, it would render: class="my-component blue"

<blockquote {{ cx("my-component", color) }}> {% if image %} <img class="photo" src="{{ image }}"> {% endif %} <span>One of our highest priorities is to make sure that, in the workplace environment, our people are supported, protected, developed and suitably recognised for the contribution they make.</span> {% if citation %} <cite class="citation"> {{ citation }} </cite> {% endif %} </blockquote>
Variations for the component's documentation

v([ ... ]) is a custom function that renders a variation in the component's documentation. The first argument is the title of the variation, any subsequent arguments are rendered in the HTML preview box below the title.

{# Example with no defined variables #} {{ v([ "Default example with no arguments", i(MyComponent), ]) }} {# Example with the citation variable defined #} {{ v([ "Example with a citation", i(MyComponent, { citation: "Mike Norris, CEO" }), ]) }} {# Example with the image variable defined #} {{ v([ "Example with an image", i(MyComponent, { image: "/images/my-image.jpg" }), ]) }} {# Multiple examples in a single function #} {{ v([ "Multiple examples in one preview box", i(MyComponent, { citation: "One" }), i(MyComponent, { citation: "Two" }), i(MyComponent, { citation: "Three" }), ]) }}
Our finished Twig file should look something like this
{# # My Component This is an example component for the documentation. #} {% from Util | r import i, v %} {% set color = color ? color : "blue" %} {% set image = image ? image : "/images/my-image.jpg" %} {% set text = text ? text : "Lorem ipsum dolor sit amet" %} {% set citation = citation ? citation : "Mike Norris, CEO" %} {% if not docs %} <blockquote {{ cx("my-component", color) }}> <img class="photo" src="{{ image }}"> <span> {{ text }} </span> <cite class="citation"> {{ citation }} </cite> </blockquote> {% else %} {{ v(["Default example", i(MyComponent)]) }} {% endif %}

Step 5

Once we've finished writing the Twig file, we can move on to the Sass styles. This is going to be a much more straightforward process as it's very similar to plain CSS syntax:

.my-component { display: block; padding: 1rem; margin: 0; // FUNCTION: // Amongst traditional use-cases, we often use functions // to make accessing tokens easier. // "color: colors(light);" = "color: #e1e0e0;" color: colors(light); background-color: colors(darkblue); // VARIABLE: // Sass variables are always prefixed with "$". // "max-width: $measure;" = "max-width: 880px;" width: 100%; max-width: $measure; // NESTED SELECTOR: // The "&" represents the parent selector. // ".my-component .photo {...}" & .photo { float: right; height: 100%; width: max(128px, 33%); margin: 0.5rem; // MEDIA QUERY MIXIN: // Mixin syntax is: "@include mixin-name(...) {...}" // "@media screen and (max-width: 960px) {...}" @include media('tablet') { margin: -1rem -1rem -1rem 0px; } } & .citation { @include font(caption); font-style: italic; } }

Step 6

If Grunt is still running, terminate the process and run it again. When it has finished compiling, we'll be able to find our new component in the documentation!


Extending and modifying components

Rather than creating entirely new components, they may need to be extended or modified from time to time. For example to:

  • Improve them based on user research
  • Meet a specific user need on the website

Before modifying anything, we should first evaluate whether the changes:

  • Effect the long-term maintenance of the framework
  • Interfere with the safe installation of new updates to the codebase
  • Reduce the risk of technical debt

When extending or modifying existing components, there is always an amount of risk. A common issue is modified code breaking or being overwritten when updating to the latest version of the framework.

Reduce this potential risk to the codebase by:

  • Using utility classes to add contextual styling exceptions
  • Not overwriting core framework code (files located at src/assets/scss/**/*)
  • Ensuring that component names are unique
  • Creating isolated override classes for specific contextual changes
  • Using BEM for small modifications to components
  • Forking components when making large modifications
  • Use style prefixes to support modern browsers above the 2% statistical signficance threshold

We have used these techniques to make sure that code in the framework is compatible, legible and scoped at a component-level where possible.


Making changes to global visual styles

Certain styles within an efficient and consistent CSS framework ought to deviate from strict component based scoping - it is important to use and leverage the global reach of CSS rules. CSS itself exists to enable the styling of HTML globally, and by category, rather than element-by-element.

Progressive enhancement through global/inheritable styles is the most efficient way to create any kind of visual design on the web. When global styling techniques are used appropriately, the rudimental styles that constitute branding/aesthetic can be abstracted away from layout/composition and the two may be treated as separate concerns.

Design tokens

These rudimental styles can be configured in the framework repository (src/assets/scss/tokens). They are organised by category within Sass maps (e.g $colors, $transitions and $size). These can be accessed through their corresponding function (e.g colors(darkblue), transitions(small), size(s1)).

Sass variables

Sass variables are a method of aliasing common values. If a value is assigned to a name that begins with $, we can then refer to that name instead of the value itself.

Sass maps

Iterable token maps can save hundreds of lines of code in instances where, for example, a component must be available in every brand color. With a map we are able to iterate over each (key, value) pair and generate the required classes / rulesets.


Using styles components and patterns

The examples in the documentation of this framework will always have an accompanying HTML snippet providing both structural insight into the component as well as a live reference point for developers.

When something is published in the Computacenter framework as a global style, Element or Widget we provide written documentation for clarity in general usage - this documentation will often include a collection of variants or patterns. Patterns are best practice design solutions for specific user-focused tasks or presentational layout requirements.



Back to top ⇧