As the number of web applications for a company grows, the effort required to maintain a consistent typography and palette across the applications increases. A good solution to this issue is to create a shared design package that takes care of the most basic design configuration. By isolating this from GUI components, the library becomes unopinionated with regards to front-end frameworks, as it is not coupled to for example React or Angular. In this article, we’ll make a simple NPM package containing a palette and a typography configuration which is available to consumers as a SASS mixin. If you want to know more about sharing reusable code, you can check out Magnus Stuhr’s article on Bulding reusable Angular NPM packages.

Tips:

It can be a good idea to look at the example repository while reading, as it makes it easier to follow along.

Setting up the structure

Since the shared design package has a narrow purpose, the number of files in the repository is pretty small. In essence, the only thing required is a ./src folder to contain all of the styles, and a gulp-script to merge them together when we want to publish the NPM package. We’ll come back to why we merge the files, and an alternative approach to this later in this article.

Defining the colors

For our example, we’ll create four colors in the palette along with two shades of grey. Start by creating palette.scss in the ./src folder to contain all of our colors and shades:

// Colors
$calm-blue: #2196F3;
$teal: #009688;
$amber: #FFC107;
$red: #D32F2F;

// Greys
$dark-grey: #282828;
$light-grey: #dedede;

These are all defined as SASS variables, so that they can be referenced from the project that depends on our shared design package.

Defining the typography

The previously described palette consists of SASS variables that can be used however and whenever the consumer wants. Typography on the other hand, doesn’t work like that.

Since our typography configuration will be global, it’s a good practice to implement it as a SASS mixin, so that the consumer of our package can decide whether or when he/she wants to use it. It’s also a great guard against polluting the global stylesheet, as each import of our theme file will add the typography configuration to the application. For the sake of the length of this article, I’ll only show a part of the typography configuration. To see the full configuration, see the example-repository.

@import './palette.scss';

@mixin demo-typography() {
  * {
    font-family: 'Montserrat', sans-serif;
    font-style: normal;
    font-weight: normal;
  }

  h1, h2, h3, h4, h5, p, b, strong, i, em, mark, small, del, ins, sub, sup {
    color: $dark-grey;
  }

  h1 {
    font-weight: bold;
    font-size: 3rem;
    line-height: 4rem;
  }

  p, b, strong, i, em, mark, del, ins {
    font-size: 1.2rem;
    line-height: 2rem;
  }

  .small-typography {
    h1 {
      font-size: 3.1rem;
      line-height: 2.6rem;
    }

    h2 {
      font-size: 2rem;
      line-height: 2.7rem;
    }

    p, b, strong, i, em, mark, del, ins {
      font-size: 1rem;
      line-height: 2rem;
    }
  }
}

As you may notice, we haven’t used media queries to define breakpoints for our mobile typography. This is naturally something that you can do in your own project. In our example, we have implemented the mobile typography as a CSS class, so that it can easily be toggled as a root-level CSS class using @angular/flex-layout. This is a good approach to prevent having the same media query breakpoints duplicated across your application:

<div ngClass.xs=”small-typography”> // Root element of our application

The ngClass.xs-attribute is a flex-layout wrapper around the Angular ngClass attribute, and will add the ‘small-typography’ CSS-class to the div-element if the width of the screen is within the extra-small (xs) range.

Merging it together

By now, our project contains two files in the ./src folder, namely ./src/palette.scss and .src/typography.scss. A nice touch for making our library a little more user friendly for the consumers, is to make it easy to use the different parts of the package, without having to reference the different files directly:

@import '~@my-package/shared-design-demo/typography';
@import '~@my-package/shared-design-demo/palette';

As our design library grows, this approach does not scale well since each new file in the package will require a new import line for the consumer. It’s also not a good idea to expose our internal file structure through our package. This prevents us from renaming or refactoring our styles, since this will introduce a breaking change in our consumers applications. We’ll therefore create one file that functions as a public access point, containing all of the styles from our “private” files.

There are two approaches to this. We can either create an “index” SASS file, which simply imports all of the files in the ./src folder. This is the simplest approach, since it allows us to publish our package directly, without any further build steps. The downside is that we need to make sure the index file is maintained if any files are added or renamed in the ./src-folder. The alternative approach is to create a simple build script using Gulp, to merge all of the contents in the ./src-folder before publishing our package. We’ll go with the latter, just to mix some Gulp into this article to make it a bit more interesting. The script is placed in gulpfile.js, and requires us to add gulp and a couple of gulp addons to our project:

npm install gulp gulp-concat gulp-scss-combine -D

The script is as follows:

'use strict';

var gulp = require('gulp');
var sass = require('gulp-scss-combine');
var concat = require('gulp-concat');

gulp.task('default', mergeSass);

function mergeSass(done) {
  return gulp.src(['src/palette.scss', 'src/*.scss']) // Reads all files in src
    .pipe(concat('theme.scss')) // Merges all of the files into theme.scss
    .pipe(sass()) // Serves to remove @import statements from the output
    .pipe(gulp.dest('./')); // Writes the file to root
}

All it does is to read the SASS files from our ./src folder, starting with our palette. This is just to make sure the palette variables is placed at the top of our file, since they are used by for example the typography. The script does also make sure to remove @import statements, such as the @import of the palette in the top of the typography. This is necessary since the output folder of our library will not contain a palette.scss file, and thus the library would throw an error.

Preparing to publish

Almost all of the pieces required for our shared design package are now in place. All that’s left is to make a NPM script to use our newly created Gulp script, and make sure that our theme.scss file is included in our NPM package. In order to deal with this, our package.json looks like this:

{
  "name": "@my-package/shared-design-demo",
  ...
  "scripts": {
    "merge": "gulp"
  },
  "files": [
    "theme.scss"
  ],
  "devDependencies": {
    "gulp": "^3.9.1",
    "gulp-concat": "^2.6.1",
    "gulp-scss-combine": "^1.0.0"
  }
}

Running our script through ‘npm run merge’ should now generate a file called theme.scss in our root folder. All that’s left now is to run ‘npm publish –access=public’ to publish our package to NPM!

Final thoughts

By using a shared NPM package to define common design elements across our web applications, we save a lot of time when changing pieces of the design that should be the same throughout our applications. It is worth mentioning that our typography configuration gives an opinionated approach to every aspect of the looks of our typography. If a more configurable approach is preferred, the typography mixin can be altered to accept input parameters. These can include for example font family or other aspects of the typography that you wish to make configurable.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *