README
Patterns CLI
Make, develop, and publish!
A front-end CLI for managing static design pattern libraries. Created and maintained by @NYCOpportunity. For examples of what the CLI can create and manage, look at our existing pattern libraries; Working NYC Patterns, NYCO Patterns, ACCESS NYC Patterns, and Growing Up NYC Patterns. Essentially, the CLI is a static generator with the following extra features.
π¦ Creates and organizes component libraries using a design system methodology.
π Manages design tokens in JSON format amd works well with Tailwindcss.
βοΈ Agnostic of existing JavaScript Frameworks. Use it to manage a customized component library or extend the default module templates to build out Vanilla ES, React, Vue.js, or Svelte components.
π Scripts for publishing a pattern library to npm for open sourcing and integration into multiple digital products.
π€Έ Ensures accessibility during development by linting rendered HTML with Pa11y.
βοΈ Highly configurable and pluggable for a particular project's needs.
Background
The Patterns CLI is the core developer module of the NYCO User Interface (UI) Patterns Framework. The project started to make building and documenting accessible, CSSβfirst, and frameworkβfree pattern libraries quick, easy, and fun, doing so without task runners and a minimal amount of JavaScript to tie it all together. It does this very well, and over time it has grown into a build system of its own, so acknowledging this as an in-house alternative is essential. Other pattern build systems with more extensive community support also exist and are well worth a look. These include Storybook.js, Fractal, Pattern Lab, and possibly more.
I want to
Quick start a new project. Run the following command.
$ npx @nycopportunity/pttrn scaffold && npm install
When scaffolding is finished run npm start
to start the development server.
Features
β¨ Make module-based patterns
The CLI has several commands, including make
, for quickly generating new pattern modules using file templates.
$ npx pttrn make component accordion
β¨ Created ./src/components/accordion/accordion.slm
β¨ Created ./src/components/accordion/accordion.md
β¨ Created ./src/components/accordion/_accordion.scss
Templates can be extended to create files to support the framework of your choosing.
Design System Methodology
All design pattern source code will be organized into four directories: Elements, Components, Objects, and Utilities (by default). The CLI takes care of the organization for you, creating the necessary files based on configurable templates.
π src
β π elements
β π components
β π accordion
β accordion.slm - Markup
β accordion.js - JavaScript
β _accordion.scss - Styling
β accordion.md - Documentation
β readme.md - Developer Usage
β π objects
β π utilities
π Demonstrate and document markup once
Write dynamic and reusable markup used to create live demonstrations and document markup once using slm-lang (an HTML template language inspired by Pug).
- this.accordion = {}
- this.accordion.id = this.createId()
- this.accordion.active = true
- if (typeof accordion !== 'undefined')
- this.accordion = Object.assign(this.accordion, accordion);
article class='c-accordion'
header class='c-accordion__header'
/! { @data-js "accordion" initalizes the Accordion toggle }
/! { @aria-controls Targets the Accordion body }
/! { @aria-expanded Indicates if the Accordion body is open or not }
button class='c-accordion__toggle w-full text-start print:hidden ${this.accordion.active ? 'active' : ''}' data-js='accordion' aria-controls='aria-c-${this.accordion.id}' aria-expanded='${this.accordion.active.toString()}'
span class='c-accordion__heading mt-0' id='aria-lb-${this.accordion.id}'
span = this.accordion.title
span class='c-accordion__toggle-active'
svg class='icon-wnyc-ui' aria-hidden='true'
use xlink:href='#icon-wnyc-ui-chevron-down'
span class='sr-only' hide this list
span class='c-accordion__toggle-inactive'
svg class='icon-wnyc-ui' aria-hidden='true'
use xlink:href='#icon-wnyc-ui-chevron-up'
span class='sr-only' show this list
/! { @id Target of the Accordion toggle. Must match the "aria-controls" attribute of the toggling button }
/! { @role Indicates an area of significance }
/! { @aria-labelledby Associates the Accordion body with the header text }
/! { @aria-hidden Indicates if the Accordion body is open or not }
div id='aria-c-${this.accordion.id}' role='region' class='c-accordion__body bg-scale-3 print:active hidden:overflow animated ${this.accordion.active ? 'active' : 'hidden'}' aria-labelledby='aria-lb-${this.accordion.id}' aria-hidden='${this.accordion.active ? 'false' : 'true'}'
div class='c-accordion__padding'
= this.accordion.body
Dart Sass
π Style usingDart Sass has many perks such as module-based dependencies. Additionally, CSS post-processing can be customized with PostCSS plugins. LibSass is also supported if desired.
// Dependencies
@use 'config/dimensions';
@use 'config/media';
@use 'config/interaction';
// Declarations
.c-accordion {
margin: 0 0 dimensions.$spacing-base;
}
.c-accordion__header {
padding: dimensions.$spacing-base;
}
.c-accordion__heading {
flex: 1;
font-weight: bold;
margin: 0;
}
.c-accordion__toggle {
text-decoration: underline;
display: inline-flex;
align-items: center;
* {
@include interaction.disable-pointer-events
}
}
.c-accordion__toggle-active,
.c-accordion__toggle-inactive {
align-items: center;
}
.c-accordion__toggle-active {
display: none;
visibility: hidden;
.c-accordion__toggle.active & {
@include interaction.disable-pointer-events;
display: inline-flex;
visibility: visible
}
}
.c-accordion__toggle-inactive {
@include interaction.disable-pointer-events;
display: inline-flex;
visibility: visible;
.c-accordion__toggle.active & {
display: none;
visibility: hidden
}
}
.c-accordion__padding {
padding: dimensions.$spacing-base
}
.c-accordion__padding > *:last-child {
margin-bottom: 0
}
π€Έ Lint for accessibility issues using Pa11y
Pa11y CI will run tests and provide suggestions for accessibility compliance.
β³ Running Pa11y CI on ./dist/web-share.html
π€Έ Pa11y suggestions for ./dist/web-share.html
<pre><code>import WebShare from '@ny...</pre>
axe error Ensure that scrollable region has keyboard access (https://dequeuniversity.com/rules/axe/3.5/scrollable-region-focusable?application=axeAPI) scrollable-region-focusable
β³ Running Pa11y CI on ./dist/utility.html
π€Έ Pa11y suggestions for ./dist/utility.html
<a class="ms-3 btn btn-secondary btn-link" href="#next-steps">Next steps</a>
htmlcs error This link points to a named anchor "next-steps" within the document, but no anchor exists with that name. G124.NoSuchID
β³ Running Pa11y CI on ./dist/text-controller.html
β¨ No Pa11y suggestions for ./dist/text-controller.html
β³ Running Pa11y CI on ./dist/tables.html
π€Έ Pa11y suggestions for ./dist/tables.html
<table class="table-headers-first-column"><thead>...</table>
htmlcs error The relationship between td elements and their associated th elements is not defined. Use either the scope attribute on th elements, or the headers attribute on td elements. H63
<table class="table-headers-sticky"><thead>...</table>
htmlcs error The relationship between td elements and their associated th elements is not defined. Use either the scope attribute on th elements, or the headers attribute on td elements. H63
β³ Running Pa11y CI on ./dist/selects.html
π€Έ Pa11y suggestions for ./dist/selects.html
<select id="ba1a47a76758b" name="" required="true"><option sele...</select>
axe error Form elements must have labels (https://dequeuniversity.com/rules/axe/3.5/label?application=axeAPI) label
<select id="ba1a47a76758b" name="" required="true"><option sele...</select>
htmlcs error This select element does not have a name available to an accessibility API. Valid names are: label element, title undefined, aria-label undefined, aria-labelledby undefined. WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.Select.Name
<select id="ba1a47a76758b" name="" required="true"><option sele...</select>
htmlcs error This form field should be labelled in some way. Use the label element (either with a "for" attribute or wrapped around the form field), or "title", "aria-label" or "aria-labelledby" attributes as appropriate. WCAG2AA.Principle1.Guideline1_3.1_3_1.F68
π Script using ES module syntax.
Library scripts are bundled using rollup.js. Use the library of utility ES modules to help keep scripting DRY.
'use strict';
import Toggle from '@nycopportunity/pttrn-scripts/src/toggle/toggle';
/**
* The Accordion module
*
* @class
*/
class Accordion {
/**
* @constructor
*
* @return {object} The instantiated accordion component
*/
constructor() {
this.toggle = new Toggle({
selector: Accordion.selector
});
return this;
}
}
/** @type {String} The dom selector for the module */
Accordion.selector = '[data-js*="accordion"]';
export default Accordion;
β« Manage design tokens in JavaScript
Design Tokens are compiled to a Sass map and can be imported into a Tailwindcss config for creating a single source between JavaScript, Sass files, and CSS utilities.
module.exports = {
'output': '"./src/config/_tokens.scss"',
'border': {
'width': '3px',
'style': 'solid',
'radius': '16px'
},
'color': {
'white': '#FFF',
'blue': '#284CCA',
'red': '#FC5D52',
'gray': '#E6E8EC'
},
'font-family': {
'system': ['-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Roboto', 'Oxygen-Sans', 'Ubuntu', 'Cantarell', '"Helvetica Neue"', 'sans-serif'],
'monospace': 'monospace'
},
'font': {
'body': 'system',
'pre': 'monospace'
},
'font-weight': {
'body': 'normal',
'pre': 'normal'
},
'font-style': {
'body': 'normal',
'pre': 'normal'
},
'font-size': {
'body': '1em',
'pre': '0.9em'
},
'line-height': {
'body': '1.2',
'pre': '1.5'
},
'grid': '8px', // 8px grid system
'typography': {
'small': '16px',
'mobile': '18px',
'tablet': '20px',
'desktop': '22px'
}
};
ποΈ Optimize and generate SVG sprites
Uses svgo to optimize individual svgs and svgstore-cli to concatenate an SVG sprite for rendering icons and vector graphics.
ποΈ Svgs in ./src/svg/shape-c.svg out ./dist/svg/pttrn-shape-c.svg
ποΈ Svgs in ./src/svg/shape-b.svg out ./dist/svg/pttrn-shape-b.svg
ποΈ Svgs in ./src/svg/shape-a.svg out ./dist/svg/pttrn-shape-a.svg
ποΈ Svgs in ./src/svg/select-chevrons.svg out ./dist/svg/pttrn-select-chevrons.svg
ποΈ Svgs in ./src/svg/option-radio.svg out ./dist/svg/pttrn-option-radio.svg
ποΈ Svgs in ./src/svg/option-checkbox.svg out ./dist/svg/pttrn-option-checkbox.svg
ποΈ Svgs in ./src/svg/logo-standard.svg out ./dist/svg/pttrn-logo-standard.svg
ποΈ Svgs in ./src/svg/logo-stacked.svg out ./dist/svg/pttrn-logo-stacked.svg
ποΈ Svgs in ./src/svg/logo-partnership.svg out ./dist/svg/pttrn-logo-partnership.svg
ποΈ Svgs in ./src/svg/logo-nyc.svg out ./dist/svg/pttrn-logo-nyc.svg
ποΈ Svgs in ./src/svg/logo-google-translate.svg out ./dist/svg/pttrn-logo-google-translate.svg
π¦ Svgs sprite written to ./dist/svg/svgs.svg
β¨ Svgs finished
π Watch and serve assets
Watch for file changes using Chokidar and serve distributed static assets to a local development environment using Express.js.
$ npm start
> @nycopportunity/pttrn-starter@0.1.0 start @nycopportunity/pttrn-starter
> cross-env NODE_ENV=development cross-env PORT=7070 concurrently "pttrn default -w" "pttrn serve -w" -p "none"
π Serve watching ./dist/**/*.html, ./dist/css/*.css, ./dist/js/*.js
π€ Serving ./dist/ to http://localhost:7070
π Slm watching ./config/slm.js, ./src/**/*.slm, ./src/**/*.md
π Svgs watching ./src/svg/**/*.svg
π Rollup watching ./config/rollup.js, ./src/**/*.js
π Styles watching ./src/**/*.scss, ./config/tokens.js, ./config/tailwindcss.js
No config or custom build
Each major feature uses a configuration file for adjusting the settings of every CLI script. Additionally, the package can be extended with other npm packages and custom npm scripts.
β π config - Configuration directory
β π make - Templates for pattern files created by the make command
β style.scss - Sass Stylesheet template
β markup.slm - Markup template
β markdown.md - Usage, design specs, and other documentation
β config.scss - Sass variable and mixin storage
β script.js - JavaScript module template
β readme.md - Technical documentation such as JavaScript usage or installation
β view.slm - View template for displaying the demonstration and documentation
β make.js - Make command settings
β alerts.js - Configure the icons and colors of alerts in the output
β global.js - Global paths and directories
β lint.js - ESLint and Style Lint
β pa11y.js - Settings for Pa11y linting
β postcss.js - PostCSS settings and plugins
β publish.js - Publish settings
β rollup.js - Rollup.js settings
β sass.js - Sass and Sass Module settings
β slm.js - slm-lang templating system settings
β svgs.js - Svg sprite settings for svgo and svgstore
β tailwindcss.js - Tailwindcss config file
β tokens.js - Design tokens and json-to-scss settings
β π dist - Static distribution directory
β π src - Source directory
Contents
- Installation
- Guide: Start from scratch
- Guide:
scaffold
command - Guide:
start
command - Guide:
publish
command - Demo Source
- CLI
- Guide: Adding Tailwindcss
- Guide: Creating a new
make
command template - Optional dependencies
- Supporting Packages
Installation
$1 Install as a normal dependency in a project.
$ npm install @nycopportunity/pttrn
If you need to start a new project you can run npm init -y
before installing.
Start from scratch
... or quickly scaffold a new project
command make
$2 Make a pattern by running npx pttrn make {{ element/component/object/utility }} {{ pattern }}
$ npx pttrn make component accordion
β¨ Created ./src/components/accordion/accordion.slm
β¨ Created ./src/components/accordion/accordion.md
β¨ Created ./src/components/accordion/_accordion.scss
π
Include the accordion stylesheet in your main Sass entry point. To create an independent distribution (optional) add the accordion stylesheet to your Sass configuration.
β Make a config file for accordion? y/n β΅
What just happened?
- The make script has made required files for styling and documenting a component for you based on a few templates included with the Framework;
- reminded you to add the stylesheet module to the global default stylesheet so it's compiled accordingly;
- and prompted to create any of the other optional files specific to the accordion. If you decide not to make any of these files initially, they can be made by rerunning the
npx pttrn make component accordion {{ file }}
command. For the sake of this demonstration answer "yes" (y) to all of the questions.
β¨ ./src/config/_accordion.scss was made.
β Make a view file for accordion? y/n β΅ y
β¨ ./src/views/accordion.slm was made.
β Make a script file for accordion? y/n β΅ y
β¨ ./src/components/accordion/accordion.js was made.
π Import the accordion script into your main JavaScript entry point file and create a public function for it in the default class. To create an independent distribution (optional) add the accordion script to your Rollup configuration.
β Make a readme file for accordion? y/n β΅ y
β¨ ./src/components/accordion/readme.md was made.
The file specs
- .slm - files are used to define markup of the component using slm-lang that has a syntax inspired by Pug. It is also the HTML template language for all views in a Patterns Framework project
- .md - files are markdown files used to store the documentation of the pattern. Here you would describe types, variations, use cases, etc
- .scss - files are used to style the pattern. All Pattern Framework project styling is module-based
- config - A Sass configuration file where variables, mixins, functions, and other dependencies can be stored
- view - A static view template where the pattern may be demonstrated and documentation can be rendered
- script - An ES Module for JavaScript-enhanced patterns
- readme - A markdown file where the pattern documentation is written
Styles
$3 Create the default Sass entry point and add the newly created accordion component stylesheet to it.
$ mkdir -p src/scss && touch src/scss/default.scss
$ echo "@use 'components/accordion/accordion';" >> src/scss/default.scss
Open up ./src/components/accordion/_accordion.scss. It will have the following contents:
/**
* Accordion
*/
// Dependencies
@use 'config/tokens' as *;
// @use 'config/accordion';
// Declarations
.c-accordion { }
Edit the file as you wish (or change the background property to red; background-color:red
). Be sure to add style attributes to the selector otherwise it will not compile. Then, run the following command:
$ npx pttrn styles
β« Tokens in @pttrn/config/tokens.js out ./src/config/_tokens.scss
π€ Lint suggestions for ./src/scss/default.scss
π
Sass in ./src/scss/default.scss out ./dist/css/default.css
π
PostCSS on ./dist/css/default.css
β¨ Styles finished
What just happened?
- The default tokens configuration from the CLI were compiled to Sass.
- StyleLint was run on the default Sass entry point.
- The default Sass entry point was compiled to CSS.
- PostCSS was run on the default CSS stylesheet.
Open up ./dist/css/default.css to see the compiled stylesheet.
Scripts
$4 Create the default JavaScript entry point and add the newly created accordion component stylesheet to it.
$ mkdir -p src/js && touch src/js/default.js
Copy and paste the following into ./src/js/default.js. This creates the main class that will have an API for the accordion module instantiation.
import Accordion from '../components/accordion/accordion';
class Default {
constructor() {
if (process.env.NODE_ENV != 'production')
console.dir('@pttrn Development Mode');
return this;
}
accordion() {
return new Accordion();
}
};
export default Default;
Open up ./src/components/accordion/accordion.js. It will have the following contents:
'use strict';
class Accordion {
/**
* @param {Object} settings This could be some configuration options.
* for the pattern module.
* @param {Object} data This could be a set of data that is needed
* for the pattern module to render.
* @constructor
*/
constructor(settings, data) {
this.data = data;
this.settings = settings;
return this;
}
}
/** @param {String} selector The main selector for the pattern */
Accordion.selector = '[data-js*="accordion"]';
export default Accordion;
The contents of this file are optional, however, using class-based ES modules makes scoping JavaScript-enhanced patterns easier to scope. Edit the file as you wish and run:
$ npx pttrn rollup
π€ ESLint suggestions for ./src/js/default.js
6:7 warn Unexpected console statement. no-console
ποΈ Rollup in src/js/default.js out dist/js/default.js
β¨ Rollup finished
What just happened? The default ES entry point from the CLI was linted and compiled in the iife format for browsers.
Views
$5 Create a layout for you pattern views.
$ mkdir -p src/slm/layouts && touch src/slm/layouts/default.slm
Copy and paste the following slm into the src/slm/layouts/default.slm file. Edit the file as you wish...
doctype html
html lang='en'
head
meta charset='utf-8'
meta http-equiv='X-UA-Compatible' content='IE=edge'
meta name='viewport' content='width=device-width, initial-scale=1'
title My Patterns
link rel='stylesheet' href='css/default.css'
body
header
h1 My Patterns
main
= content('main')
footer
- let date = new Date();
- let opts = {year: 'numeric', month: 'long', day: 'numeric'};
p = `Last updated ${date.toLocaleDateString('en-US', opts)}`
script src='js/default.js'
javascript:
let MyPatterns = new Default();
= content('scripts')
/ The reload script. This should not be compile during production builds
/ @source https://www.npmjs.com/package/reload
- if this.process.env.NODE_ENV !== 'production'
script src='/reload/reload.js'
You'll need to instantiate the Accordion module in the default entry point class. Open ./src/views/accordion.slm and add to the scripts block...
= content('scripts')
... the following script tag...
= content('scripts')
javascript:
MyPatterns.accordion();
... then run the command:
$ npx pttrn slm
β¨ Slm in ./src/views/accordion.slm out ./dist/accordion.html
β³ Running Pa11y CI on ./dist/accordion.html
β¨ No Pa11y suggestions for ./dist/accordion.html
β¨ Slm finished
What just happened?
- The Accordion view slm file was compiled to HTML.
- The command triggered the Pa11y CI to lint the HTML output for accessibility issues. Linting for accessibility issues is a helpful perk of the CLI. No issues here!
Open up ./dist/accordion.html to see the compiled accordion view.
Serve
$6 Start the development server to see view your work.
$ npx pttrn serve
π€ Serving ./dist/ to http://localhost:7000
Open up http://localhost:7000/accordion to see the Accordion Component page. Want to make a change? For development purposes we'll want to run the server as well as watch scripts and reload when changes are made. The CLI uses Concurrently to run multiple commands so the binary is available to execute in the same way. Combine the default
command with the serve
command. Adding the -w
or --watch
flag enable change detection on both commands:
$ npx concurrently 'pttrn -w' 'pttrn serve -w'
[1] π Serve watching ./dist/**/*.html, ./dist/**/*.css, ./dist/**/*.js
[1] π€ Serving ./dist/ to http://localhost:7000
[0] π Slm watching @pttrn/config/slm.js, ./src/**/*.slm, ./src/**/*.md
[0] π Svgs watching ./src/svg/**/*.svg
[0] π Rollup watching @pttrn/config/rollup.js, ./src/**/*.js
[0] π Styles watching ./src/**/*.scss, @pttrn/config/tokens.js, @pttrn/config/tailwindcss.js
What just happened? The default watching processes have been announced. Once you save a change to the file Chokidar will detect it and run default style tasks and reload the development server. Make a change to any file such as the ./src/components/accordion/accordion.scss.
[0] π Detected change on ./src/components/accordion/_accordion.scss
[0] β« Tokens in @pttrn/config/tokens.js out src/config/_tokens.scss
[0] π€ Lint suggestions for ./src/scss/default.scss
[0] π
Sass in ./src/scss/default.scss out dist/css/default.css
[0] π
PostCSS on dist/css/default.css
[0] π Serve reloading
The change was detected and the style scripts were run.
command scaffold
The scaffold
command will create a minimal base project with the following:
- A package.json file with the recommended NPM Scripts and this package as a development dependency.
- CSS only Details Component
- Configuration files for Tokens, Rollup.js, Sass, and Tailwindcss
- Simple Sass library
- Single page static demo site
If you are running the command in an empty directory or without a package.json file, run npx @nycopportunity/pttrn scaffold
, npm install
. Once everything is scaffolded you can run npm start
to start the development server.
If @nycopportunity/pttrn
is already installed as a dependency of your project's package.json file you can run the following command.
$ npx pttrn scaffold
./package.json already exists.
β¨ ./src was made.
β¨ ./src/views was made.
β¨ ./src/views/index.slm was made.
β¨ ./src/utilities was made.
β¨ ./src/utilities/typography was made.
β¨ ./src/utilities/typography/_typography.scss was made.
β¨ ./src/utilities/tailwindcss was made.
β¨ ./src/utilities/tailwindcss/_tailwindcss.scss was made.
β¨ ./src/utilities/padding was made.
β¨ ./src/utilities/padding/_padding.scss was made.
β¨ ./src/utilities/color was made.
β¨ ./src/utilities/color/_color.scss was made.
β¨ ./src/svg was made.
β¨ ./src/svg/a-perfect-heart.svg was made.
β¨ ./src/svg/a-perfect-heart-red.svg was made.
β¨ ./src/slm was made.
β¨ ./src/slm/layouts was made.
β¨ ./src/slm/layouts/default.slm was made.
β¨ ./src/scss was made.
β¨ ./src/scss/default.scss was made.
β¨ ./src/scss/_imports.scss was made.
β¨ ./src/objects was made.
β¨ ./src/js was made.
β¨ ./src/js/default.js was made.
β¨ ./src/elements was made.
β¨ ./src/elements/base was made.
β¨ ./src/elements/base/_base.scss was made.
β¨ ./src/elements/base/_base-first-last.scss was made.
β¨ ./src/config was made.
β¨ ./src/config/_type.scss was made.
β¨ ./src/config/_tokens.scss was made.
β¨ ./src/config/_grid.scss was made.
β¨ ./src/config/_get.scss was made.
β¨ ./src/config/_border.scss was made.
β¨ ./src/components was made.
β¨ ./src/components/details was made.
β¨ ./src/components/details/details.slm was made.
β¨ ./src/components/details/details.md was made.
β¨ ./src/components/details/_details.scss was made.
β¨ ./dist was made.
β¨ ./config was made.
β¨ ./config/tokens.js was made.
β¨ ./config/tailwindcss.js was made.
β¨ ./config/sass.js was made.
β¨ ./config/rollup.js was made.
Then run the following to start the development server and start making.
$ npx concurrently 'pttrn -w' 'pttrn serve -w' -p 'none'
π Serve watching ./dist/**/*.html, ./dist/**/*.css, ./dist/**/*.js
π€ Serving ./dist/ to http://localhost:7000
π Slm watching ./config/slm.js, ./src/**/*.slm, ./src/**/*.md
π Svgs watching ./src/svg/**/*.svg
π Rollup watching ./config/rollup.js, ./src/**/*.js
π Styles watching ./src/**/*.scss, ./config/tokens.js, ./config/tailwindcss.js
command start
Add this start
script in your package.json file to create a shorthand for the development server command.
"scripts": {
"start": "cross-env NODE_ENV=development cross-env PORT=7000 concurrently \"pttrn -w\" \"pttrn serve -w\" -p \"none\""
}
This will hook into npm script's default start command for your project. Once this script is in place starting the server and watching files becomes can be done with the following command:
$ npm start
> patterns-demo@1.0.0 start patterns-demo
> cross-env NODE_ENV=development cross-env PORT=7000 concurrently "pttrn default -w" "pttrn serve -w" -p "none"
π Serve watching ./dist/**/*.html, ./dist/**/*.css, ./dist/**/*.js
π€ Serving ./dist/ to http://localhost:7070
π Slm watching @pttrn/config/slm.js, ./src/**/*.slm, ./src/**/*.md
π Svgs watching ./src/svg/**/*.svg
π Rollup watching @pttrn/config/rollup.js, ./src/**/*.js
π Styles watching ./src/**/*.scss, @pttrn/config/tokens.js, @pttrn/config/tailwind.js
command publish
Before publishing, you'll need to have Git initialized for your project. The following lines will do just that and add prevent /node_modules from being committed to your project. You can skip these steps if you already have this setup.
$ git init
$ touch .gitignore
$ echo '/node_modules' >> .gitignore
You will also need a remote git repository with the repository settings, specifically the URL, are configured in your package.json file. Below is an example:
"repository": {
"type": "git",
"url": "https://github.com/CityOfNewYork/patterns-demo.git"
}
Adding the version
, prepublishOnly
, and publish
scripts in your package.json file will create shorthands for quickly versioning and publishing your library on npmjs.org.
"scripts": {
"version": "pttrn && git add .",
"prepublishOnly": "git push && git push --tags",
"publish": "cross-env NODE_ENV=production pttrn publish"
}
You will want to start from a reasonable version number in your package.json file if starting a new project. Open it up and set the version
value to 0.0.0
.
"version": "0.0.0"
Then you can make the first commit by staging the working directory and committing.
$ git add .
$ git commit -m 'init'
When versioning your library you can pass major
, minor
, patch
, or prerelease
as the argument following semantic versioning.
$ npm version minor
v0.1.0
> patterns-demo@0.1.0 version patterns-demo
> pttrn default && git add .
β¨ Slm in ./src/views/accordion.slm out ./dist/accordion.html
π€Έ Running Pa11y CI on ./dist/accordion.html
ποΈ Rollup in src/js/default.js out dist/js/default.js
β¨ Rollup finished
β« Tokens in @pttrn/config/tokens.js out src/config/_tokens.scss
π€ Lint suggestions for ./src/scss/default.scss
π
Sass in ./src/scss/default.scss out dist/css/default.css
π€Έ No Pa11y suggestions for ./dist/accordion.html
β¨ Slm finished
π
PostCSS on dist/css/default.css
β¨ Styles finished
... then publish to npm for integration in other projects...
$ npm publish
> patterns-demo@0.1.0 prepublishOnly .
> git push && git push --tags
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 379 bytes | 379.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To https://github.com/CityOfNewYork/patterns-demo
40e700a9..330cbad2 master -> master
Everything up-to-date
npm notice
npm notice π¦ patterns-demo@0.1.0
npm notice === Tarball Contents ===
npm notice 71B dist/css/default.css
npm notice 1.1kB dist/accordion.html
npm notice 590B src/components/accordion/accordion.js
npm notice 2.5kB dist/js/default.js
npm notice 127B src/js/default.js
npm notice 1.6kB package.json
npm notice 87B src/components/accordion/accordion.md
npm notice 99B readme.md
npm notice 585B src/components/accordion/readme.md
npm notice 186B src/components/accordion/_accordion.scss
npm notice 198B src/config/_accordion.scss
npm notice 771B src/config/_tokens.scss
npm notice 40B src/scss/default.scss
npm notice 69B src/components/accordion/accordion.slm
npm notice 421B src/views/accordion.slm
npm notice 843B src/slm/layouts/default.slm
npm notice 232B dist/svg/svgs.svg
npm notice === Tarball Details ===
npm notice name: patterns-demo
npm notice version: 0.1.0
npm notice package size: 4.6 kB
npm notice unpacked size: 9.5 kB
npm notice shasum: 47e2e2063c731c9c2b24841cce4288d457cf75c6
npm notice integrity: sha512-gXB4U+AJhlGDo[...]O/CzKHv1LUz4w==
npm notice total files: 17
npm notice
> patterns-demo@0.1.0 publish .
> cross-env NODE_ENV=production pttrn publish
π€ Publishing to origin; https://github.com/CityOfNewYork/patterns-demo.git
β¨ Published to GitHub Pages
+ patterns-demo@0.1.0
Demo Source
The source code of the demo for the previous guides can be found in the Patterns Demo repository.
CLI
Executing the binary
The key to using the CLI is executing the .pttrn binary created in the node_modules/.bin directory when you install this module in your project. There are a few ways to execute the local binary;
$A Use npx before every pttrn
command. For most cases, this is the most convenient option as demonstrated in the getting started guide above.
$ npx pttrn {{ command }}
$B Create an alias to the binary in your shell environment configuration file (such as .profile or .bash_profile)
alias pttrn="./node_modules/.bin/pttrn"
Then running commands can be done like so:
$ pttrn {{ command }}
$C Or, add an npm script to your package.json file. npm will always execute local binaries referenced in the script block.
"scripts": {
"pttrn": "pttrn"
}
Then running commands can be done like so:
$ npm run pttrn {{ command }}
Commands
The basic pattern for commands is as follows:
$ {{ ENVIRONMENT_VARIABLE }}={{ value }} npx pttrn {{ command }} {{ flag }}
Command Configuration
Every command is configured with one or a series of JavaScript files inside the ./config directory. The CLI will check to see if there is a custom configuration file in the local ./config directory of your project first. If it doesn't exist it will use the default configuration file in the CLI package. Project configurations can be used to pass in additional and custom options to each package that the CLI uses. Below the packages used and default configurations are described in more detail.
Command Flags
Optional flags can be passed to the commands for signaling watching and log settings. The log settings are universal so they aren't represented in the command tables below.
Commands
Custom commands can be defined in the local project ./bin are described below.
Default
Command | Flags | Configuration | NODE_ENV
-----------------|-------|---------------|-
default
or
| -w
| n/a | production
or development
Uses Concurrently to synchronously run this series of commands; styles
, rollup
, slm
, and svgs
, respectfully. Each command is described in more detail below. The series of commands can be modified by editing the array of commands defined in a custom ./config/default.js file. Addionally, the options passed to Concurrently can also be
Back to commands ^ | table of contents ^
Styles
Command | Flags | Configuration | NODE_ENV
---------|-------|---------------|-
styles
| -w
| n/a | production
or development
Asynchronously runs this series of commands; tokens
, sass
, then postcss
respectfully and described below.
Back to commands ^ | table of contents ^
Tokens
Command | Flags | Configuration
---------|-------|-
tokens
| n/a | tokens.js
Uses JSON-TO-SCSS to convert design tokens defined in ./config/tokens.js into ./src/config/_tokens.scss. Tokens can be custom values for your Sass library as well as be values mapped directly to tokens in the Tailwindcss configuration (if used by your project). CSS variables can also be used in the token configuration. Note, other Tailwindcss configuration options, such as variants and modules should be configured in the tailwindcss
configuration.
Settings for JSON-TO-SCSS are set at the root level of the export and include setting the file output and specific transformation options. Refer to the source for available options.
A custom tokens
configuration is highly recommended (if not required) for any patterns library that uses the CLI to manage unique design tokens.
Back to commands ^ | table of contents ^
Sass
Command | Flags | Configuration | NODE_ENV
--------|-------|---------------------------|-
sass
| -nl
| sass.js | production
or development
Uses Dart Sass to compile Sass modules defined in the sass
configuration into CSS. It will use Node Sass in place of Dart Sass if it is required in a project's package.json file. By default, it will compile the default Sass entry point ./src/scss/default.js. If NODE_ENV
is set to development
only the modules with the attribute devModule: true
will be compiled.
A custom sass
configuration could be used to add additional Sass modules to compile.
Back to commands ^ | table of contents ^
PostCSS
Command | Flags | Configuration
----------|-------|-
postcss
| n/a | postcss.js tailwindcss.js
Runs PostCSS on CSS modules defined in the sass
configuration. PostCSS plugins are defined in the configuration. By default, PostCSS is configured to use the plugins cssnano and, if installed in your project, Tailwindcss. The command will use the ./config/tailwindcss.js file where a custom Tailwindcss configuration would live. Learn more about adding tailwindcss in the guide below.
A custom postcss
configuration could be used to configure PostCSS and add additional plugins needed for a particular project.
A custom tailwindcss
configuration can easily import values from the configuration of the tokens
to generate Tailwindcss utilities.
Back to commands ^ | table of contents ^
Rollup
Command | Flags | Configuration | NODE_ENV
---------|------------|-------------------------------|-
rollup
| -w
-nl
| rollup.js | production
or development
Runs Rollup.js on an array of ES modules defined in the rollup
configuration and bundles them into a self-executing function (iife). By default, it will bundle the default JavaScript entry point ./src/js/default.js. Rollup.js plugins included with the default rollup
configuration include the following:
- Replace for replacing
process.env.NODE_ENV
in scripts with theNODE_ENV
environment variable passed through the command. - Node Resolve for resolving module imports from the ./node_modules directory.
NODE_ENV
The value development
will affect the command directly by compiling only the modules with the attribute devModule: true
(the default entry point module is a development module).
Other ES modules in your library can use the process.env.NODE_ENV
for things such as logging to the console during development. Say the following line appears in an ES module:
if (process.env.NODE_ENV != 'production')
console.dir('A development only log');
If NODE_ENV
is set any value other than production
the statement above will appear in the output like the following:
{
console.dir('A development only log');
}
If NODE_ENV
is set to production
then the statement will not appear at all.
Internet Explorer 11 Support
IE 11 is no longer supported. If you absolutely must support it you will need to install and configure Rollup Plugin BublΓ© or Rollup Plugin Babel and configure them in your project.
A custom rollup
configuration could be used to add additional output modules to support additional JavaScript environments, such as NodeJS, as well as utilize additional Rollup plugins needed for a particular project.
Back to commands ^ | table of contents ^
Lint
Command | Flags | Configuration
--------|-------|-
lint
| n/a | lint.js
Uses ESLint and stylelint to lint JavaScript and Sass files in the ./src/ directory. Linting suggestions are logged to the terminal. The default lint
configuration uses Google's JavaScript style guide and stylelint's standard config with a few additional rules.
A custom lint
configuration could be used to change or extend the linting standards of a project.
Back to commands ^ | table of contents ^
Slm
Command | Flags | Configuration | NODE_ENV
--------|------------|-----------------------------------|-
slm
| -w
-np
| slm.js | production
or development
Uses Slm to compile Slm pages from the ./src/views/ directory to static HTML pages in the ./dist directory. Slm files serve as the template language for site documentation and HTML spec for patterns. The output is run through JS Beautifier for human-readable markup. The Slm parser is extended with a method that includes Markdown files compiled by Marked. The default slm
configuration passes configuration options to these packages as well as global variables described below.
Views
The default entry-point for Slm views exist in the ./src/views/ directory. Files in this directory will be treated as pages and compiled to the ./dist/ directory. Sub-directories are supported for nested pages. Slm extras, such as partials and layouts, can be placed in the ./src/slm/ directory.
β π src/ - Source directory
β π slm/ - Slm extras
β π partials/
β π layouts/
β π views/ - Slm views
β π newsletter - Sub-directory
β index.slm
β index.slm - Homepage
β accordion.slm - Accordion demo page
β buttons.slm - Buttons demo page
β ...
β ...
Running npx pttrn slm
would compile the files in the source above to the static distribution described below.
β π dist/
β π newsletter
β index.html
β index.html
β accordion.html
β buttons.html
β ...
Include
The include method, this.include()
, accepts a single path argument of a file to be included. It will return the compiled HTML output of the file. Prefixing the method with the single equals sign =
will escape the returned HTML enabling it to be rendered within a pre
tag as a code demonstration on the page.
= this.include('components/accordion/accordion.slm');
The double equals sign ==
will prevent HTML escaping and render the HTML as a valid element to be rendered by the browser.
== this.include('components/accordion/accordion.slm');
Passing an .md file path without escaping will render the markdown file as HTML.
== this.include('components/accordion/accordion.md');
The slm
command only supports Slm and Markdown files so other file types included by this method will be rendered "as is."
Markdown files can also include Slm and other Markdown files using the following tag:
include{{ path/to/file.slm }}
Variables
The default slm
configuration passes global variables to use in Slm templates. These include the package.json, ./config/global.js, and ./config/tokens.js files. Additionally, the NODE_ENV
is also passed to templates.
= this.package
= this.global
= this.tokens
= this.process.env.NODE_ENV
Variables are also available to Markdown files using the following tag:
{{ this.package }}
{{ this.global }}
{{ this.tokens }}
{{ this.process.env.NODE_ENV }}
A custom slm
configuration could be used to be used to pass additional data and methods to the view templates as well as further configure JS Beautify and Marked.
Back to commands ^ | table of contents ^
Pa11y
Command | Flags | Configuration
--------|-------|-
pa11y
| n/a | pa11y.js
Uses Pa11y to test the static output of HTML files in the ./dist directory for accessibility issues. Issues are logged to the terminal. The default pa11y
configuration uses the WCAG AA accessibility standard, aXe-core, and HTML CodeSniffer as test runners, and adds the selector [data-pa11y="disable"]
to that can be used to hide elements that shouldn't be tested in the static output.
A custom pa11y
configuration could be used to enable or disable many of the available configuration options for Pa11y.
Back to commands ^ | table of contents ^
Svgs
Command | Flags | Configuration
--------|-------|-
svgs
| -w
| svgs.js
Uses svgo to optimize SVGs in the ./src/svg/ directory and saves them in the ./dist/svg directory. Then, it uses svgstore to create an SVG sprite in the ./dist/svg/svgs.svg file of all the optimized SVGs. The svgs
configuration passes svg file name prefix and svg sprite name settings to each package.
A custom svgs
configuration could be used to add multiple source directories and sprites, modify their svg file prefixes, svg sprite names, and configuration options for svgo and svgstore. Additionally, allows a restrict parameter to limit the svgs that will be compiled from the source directory and included in the sprite.
Back to commands ^ | table of contents ^
Scaffold
Command | Flags | Configuration
-----------|-------|-
scaffold
| n/a | global.js scaffold/*
As described in the Scaffold guide above this command will initialize a minimal base project with the following:
- CSS only Details Component
- Configuration files for Tokens, Rollup.js, Sass, and Tailwindcss
- Simple Sass library
- Single page static demo site
The scaffold
command relies on the global.js
configuration that describes the default filesystem and entry points for a project. It also relies on file templates in the ./config/scaffold/ directory to source the contents of files described in the system.
A custom scaffold
configuration could be used to change the output of the starter filesystem and the contents of files for a project.
Back to commands ^ | table of contents ^
Make
Command | Arguments | Flags | Configuration
--------|----------------------------|-------|-
make
| type
* name
* template
| n/a | make.js make/*
Creates pattern directories and files using paths and variables defined in the make
configuration with file contents defined in the ./config/make directory as described in the make
command guide. It will not permit overwriting pattern files if they already exist.
*denotes required arguments.
Arguments
type
required - Determines where in the filesystem patterns will be stored. One of;element
,component
,object
, orutility
pattern
required - Name of the patterntemplate
optional - If included the third argument can be used to create a single template from the ./config/make directory. By default, all files will be made.
A custom make
configuration could be used to add custom files with predefined templates in the ./config/make directory. The CLI currently supports CSS and ES module-based libraries out-of-the-box, however, with custom make templates, it could be extended to make Vue.js, React, Svelte, or other component type files.
Refer to the guide on creating a new make
command template for details.
Back to commands ^ | table of contents ^
Serve
Command | Flags | Configuration | PORT
--------|-------|---------------|-
serve
| -w
| n/a | Any port number (ex; 8080
)
Uses Express to serve static files in the ./dist/ directory. By default, it runs on port 7000
. The serve
command doesn't have a configuration file, however, the port number can be configured through the environment variable PORT
.
Back to commands ^ | table of contents ^
Publish
Command | Flags | Configuration | NODE_ENV
*
----------|-------|---------------|-
publish
| n/a | publish.js | production
or development
Uses gh-pages to stand up the static output in the ./dist directory to the GitHub Pages branch of your project's remote repository. The default publish
configuration requires the NODE_ENV=production
environment variable to push to the package repository URL defined in the package.json file.
A custom publish
configuration could be used to push to different remote GitHub Pages repositories.
Back to commands ^ | table of contents ^
Flags
Flag | Non abbreviated Flag | Description
-----------|--------------------------------|-
-w
| --watch
| Use Chokidar to watch for changes on concerned source files and run their scripts when changes are detected.
-nd
| --nondescript
| Silence detailed logging (such as file writing writing) for commands. All other logs (such as script start and success) will display. This can be used on all commands.
-s
| --silent
| Disable all logging output. Note, some output will always log such as linting and errors. This can be used on all commands.
-nl
| --no-lint
| Disable ESLint and stylelint. This only works the rollup
and sass
command respectively. Running npx pttrn lint -nl
will not effect.
-np
| --no-pa11y
| Disable Pa11y linting. This only works for the slm
command. Running npx pttrn pa11y -np
command will not effect.
Alerts
Node Emoji and Chalk are used to illustrate the logging alert output. The emoji symbols and colors can be modified or removed with a custom ./config/alerts.js configuration file.
Custom Commands
As the commands above will look for a custom configuration file for each command in the ./config directory of your project the CLI will also resolve custom command scripts in the ./bin directory of your project. A sample script is included in this repo and can be used to start the creation of a custom command. The bare minimum a command script should include is a run()
method.
const cnsl = require('@nycopportunity/pttrn/bin/util/console');
const alerts = require('@nycopportunity/pttrn/config/alerts');
module.exports = {
run: () => {
cnsl.describe(`${alerts.success} My custom command`);
}
};
For example, a custom script named bin/custom.js that exports the run()
method above can be executed with the CLI by running
$ npx pttrn custom
β¨ My custom command
Plugins
Custom commands can use other packages that are not integrated in this project and reuse the CLI scripts, utilities, or default configuration in different ways. Custom commands can also be packaged, published, and shared between projects as plugins. A few published custom command plugins are described below.
NPM Scripts
The recommended npm scripts below create shortcuts for using the CLI and hook into other npm methods to make starting, versioning, and publishing more convenient. They can be modified to suit the needs of a particular project. Add them to your project's package.json file.
"scripts": {
"start": "cross-env NODE_ENV=development concurrently \"pttrn -w\" \"pttrn serve -w\" -p \"none\"",
"version": "npm run default && git add .",
"prepublishOnly": "git push && git push --tags",
"publish": "cross-env NODE_ENV=production pttrn publish",
"default": "cross-env NODE_ENV=production pttrn"
}
Then each script can be run using the following outline:
$ npm run {{ script }} {{ arg }}
Except for start
, version
, and publish
which hook into default npm commands.
$ npm {{ start / version / publish }} {{ arg }}
Script | Description
-----------------|-
start
| Hooks into the npm-start script to concurrently run the default
and serve
commands in watching mode for development.
version
| Hooks into the npm-version script to commit a production-ready distribution and semantic version tag for publishing. It accepts an argument describing the version number to increment such as patch
, minor
, major
, or prerelease
.
prepublishOnly
| Hooks into the npm prepublishOnly
script to push the latest distribution and semantic version tag to the remote repository for publishing.
publish
| Hooks into the npm-publish script to push the contents of the ./dist folder in the remote repositories GitHub Pages branch.
default
| Runs the default
command in production mode to build a production-ready distribution.
Below is an explainer of each script's contents.
Back to NPM scripts | table of contents ^
Start
cross-env
- A package for the support of setting environment variables across terminal platforms.NODE_ENV=development
- Sets the node environment variable todevelopment
.PORT=7000
- Sets the development server port environment variable to7000
.concurrently
- A node.js package for running multiple commands in the same session.pttrn -w
- Runs the default pttrn command in watch mode.pttrn serve -w
- Runs the serve pttrn command for starting the development server.-p "none"
- This is a flag for Concurrently that removes the process prefix ([0]
) from the log.
Back to NPM scripts | table of contents ^
Version
pttrn
- This is the same asnpx pttrn default
. It will run the CLI executable in the local ./node_modules directory.git add .
- This stages the working directory for a commit. Since it runs after thedefault
command anything compiled will be committed to the release.
Back to NPM scripts | table of contents ^
prepublishOnly
git push
- This will push the committed release files to the origin repository.git push --tags
- This will push the committed release tag to the origin repository.
Back to NPM scripts | table of contents ^
Publish
cross-env NODE_ENV=production
- This sets theNODE_ENV
variable toproduction
for the next command.pttrn publish
- Takes the contents of the ./dist directory and commits it to thegh-pages
branch to create a GitHub Pages site using the gh-pages package.
Back to NPM scripts | table of contents ^
Adding Tailwindcss
From Tailwindcss;
Rapidly build modern websites without ever leaving your HTML.
A utility-first CSS framework packed with classes like flex, pt-4, text-center, and rotate-90 that can be composed to build any design, directly in your markup.
CSS utilities make it easier for developers who do not actively maintain your pattern library to create designs with components that aren't available in your stylesheet. They can also be used to modify existing components for different contexts. Tailwindcss ships as a dependency with the CLI but it needs to be configured and included in your project.
Step 1: Create your configuration file
The scaffold
command creates a ./config/tailwindcss.js. If not using the scaffold
command, create your configuration file:
touch config/taiwindcss.js
In the configuration file, you can import your ./config/tokens.js configuration and include design tokens from your project in the Tailwindcss configuration. You may also use the default configuration if desired.
/**
* Dependencies
*/
const tokens = require('tokens');
const tailwindcss = require('tailwindcss/defaultConfig');
/**
* Config
*/
module.exports = {
...
Further reference
Step 2: Include Tailwindcss in your CSS
All that's needed to include Tailwindcss in your stylesheet is to add the @
directives somewhere in your stylesheet.
@tailwind components;
@tailwind utilities;
You may notice this does not include the @tailwind base
tag which adds some base styling for Tailwindcss utilities. They aren't required and they can interfere with the styling of other patterns in your stylesheet. Use them at your discretion.
These directives can be added anywhere but we recommend keeping them in the ./src/utilites directory. The scaffold
command creates a module directory for these directives automatically: ./src/utilities/tailwindcss. If not using the scaffold
command the directory and stylesheet can be added with the following make
command:
npx pttrn make utility tailwindcss style
Below are sample contents of the stylesheet from the scaffold
command. Copy and paste them into the stylesheet if using the make
command above.
/**
* Tailwindcss
*/
// This injects all of Tailwind's utility classes, generated based on your
// config file. View docs for usage; https://tailwindcss.com/docs/
// @tailwind base; // Uncomment this to use Tailwindcss base styles. These may interfere with existing styles.
@tailwind components;
@tailwind utilities;
Now, in the stylesheet entry point, include the _tailwindcss.scss stylesheet.
@forward 'utilities/tailwindcss/tailwindcss';
PostCSS will inject Tailwindcss styles into your stylesheet.
Further reference
Step 3: Optional. Configure PostCSS
The CLI already configures PostCSS to inject Tailwindcss styles where the directives are included in the stylesheet. You may, however, want to further configure PostCSS, you can create a custom ./config/postcss.js file to modify the PostCSS plugins used in your project.
Additionally, you may want to create a Tailwindcss only distribution for inclusion in other projects. This can be done by adding new distribution modules to the ./config/sass.js configuration. The example below creates a CDN and browser friendly CSS file and a project friendly Sass file:
module.exports = [
{
file: `${process.env.PWD}/src/scss/default.scss`,
outDir: `${process.env.PWD}/dist/styles/`,
outFile: 'default.css',
sourceMapEmbed: sass.sourceMapEmbed,
includePaths: sass.includePaths,
devModule: true // This needs to be set if we want the module to be compiled during development
},
{
file: `${process.env.PWD}/src/utilities/tailwindcss/_tailwindcss.scss`,
outDir: `${process.env.PWD}/dist/styles/`,
outFile: 'tailwindcss.css', // CDN and browser friendly CSS file
sourceMapEmbed: sass.sourceMapEmbed,
includePaths: sass.includePaths,
},
{
file: `${process.env.PWD}/src/utilities/tailwindcss/_tailwindcss.scss`,
outDir: `${process.env.PWD}/dist/styles/`,
outFile: '_tailwindcss.scss', // Project friendly Sass file
sourceMapEmbed: sass.sourceMapEmbed,
includePaths: sass.includePaths,
}
];
Step 4: Optional (but recommended). Purge CSS
Purge CSS will read static files and remove CSS from a stylesheet that isn't being used by those files. This is recommended for project