Slide 1

Slide 1 text

Frontend Architecture for Scalable Design Systems Tweet me questions! @salem_ghoweri Slides: bit.ly/boltdesignsystem

Slide 2

Slide 2 text

Building a progressively decoupled, 
 external design system…. • More maintainable documentation • Individually installable components • Drupal and Pattern Lab friendly + automated Twig integration • Works with other systems (Vue, React, static, etc)
 
 … in 30 minutes or less.

Slide 3

Slide 3 text

Salem Ghoweri • Twitter @salem_ghoweri • Lead Frontend Architect at Pegasystems • Building design systems, pattern libraries for over 4+ years • Pattern Lab PHP and Node Lead

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

3 years ago, our Drupal front-end code was a bit of a mess...

Slide 7

Slide 7 text

…so we used Pattern Lab 
 to build our first Design System!

Slide 8

Slide 8 text

It had a great codebase… Tools Generic Elements Settings Objects Components Themes Utils

Slide 9

Slide 9 text

• Code for Twig-based components built & lived in Pattern Lab • Drupal integration w/ Twig namespaces + Components module • Component docs = Pattern Lab demos + mostly static markdown • Design system lived as a standalone Git repo outside of Drupal (pulled in via Composer) And Followed Best Practices for Drupal + Pattern Lab Integration

Slide 10

Slide 10 text

So lots of our
 Drupal sites used it!

Slide 11

Slide 11 text

Unfortunately, our first design system started having some problems… …major scalability problems

Slide 12

Slide 12 text

…major scalability problems How do I use this component? 
 What are the different config options? What changed with this most recent release? How do we pull in just this one component’s updates? Slightly reorganizing Pattern Lab’s docs completely broke all these Twig templates in Drupal… Component
 Fragmentation Problems Shipping via Composer Fragile
 Integrations Why are these component docs out of date?! We had to recreate this component’s Twig template… forgot to tell you… and now your CSS updates broke Drupal. WHY IS DRUPAL HAVING TO INSTALL PATTERN LAB?! We couldn’t configure this in Drupal so we had to hack something together with some inline styles… Our 3rd party service injects hard coded design system components onto the page… We didn’t know that Twig template required that option… Oh we copied over / forked those custom Twig extensions months ago… We need to build and ship a new component ASAP that looks like the design system… Poor Documentation

Slide 13

Slide 13 text

The fragmentation got so bad… Half the pages on this Drupal site need to use the old version of the design system (from a year ago)…
 
 …and the other half need to use the latest version… …our first design system was basically dead. ⚰

Slide 14

Slide 14 text

Rebuilding a Design System for Scale

Slide 15

Slide 15 text

1. Maintainability: how do you systematically maintain, support, and improve the design system over time? ^ bug fixes, docs, testing coverage, refactors, enhancements, etc
 
 2. Fragmentation: how do you systematically prevent the code that’s shipped from falling apart over time? ^ easy to upgrade + hard to break + related code stays in sync A Scalable Design System Need To Solve Two Big Problems

Slide 16

Slide 16 text

1. More maintainable docs & demos that stay up to date. 2. More flexible, more encapsulated, and more cross- platform-friendly components. 3. Make the design system’s code installable à la carte. 4. Make integrations as automated as possible. Bolt Design System Goals:

Slide 17

Slide 17 text

Solution #1: 
 API-powered Docs

Slide 18

Slide 18 text

Step 1: Structure your code to be export-friendly // packages/core/styles/…/_settings-colors.scss @import ' ../ ../02-tools/tools-data/tools-data.scss'; $bolt-brand-colors: ( indigo: ( xdark: hsl(233, 71%, 8%), dark: hsl(233, 47%, 16%), base: hsl(233, 47%, 23%), light: hsl(233, 33%, 49%), xlight: hsl(233, 73%, 81%), ), ); @include bolt-export-data('colors/brand.bolt.json', $bolt-brand-colors); { "indigo": { "xdark": "rgb(6, 9, 35)", "dark": "rgb(22, 26, 60)", "base": "rgb(31, 38, 86)", "light": "rgb(84, 93, 166)", "xlight": "rgb(171, 179, 242)" } } Step 2: Export data about your code

Slide 19

Slide 19 text

// BoltCore.php — teach Twig about data in the design system function initRuntime(\Twig_Environment $env) { try { $fullManifestPath = TwigTools\Utils ::resolveTwigPath($env, '@bolt-data/full-manifest.bolt.json'); $dataDir = dirname($fullManifestPath); $this ->data = self ::buildBoltData($dataDir); } catch (\Exception $e) {} } /** * @param $dataDir {string} - Path to data directory * @return {array} - Json files parsed as a single array */ function buildBoltData($dataDir) { ...} // Expose data globally via Twig’s getGlobals API public function getGlobals() { return [ 'bolt' => [ 'data' => $this ->data, ], 'enable_json_schema_validation' => true, ]; } Step 3: Teach your design system to other systems (like Twig)

Slide 20

Slide 20 text

{% for colorName, palette in bolt.data.colors.brand %} {% include "_color-swatch.twig" with { color_swatch: { colorName: colorName, palette: palette } } %} {% endfor %} Step 4: Use It!

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Solution #2 
 Component Schemas $schema: 'http: //json-schema.org/draft-04/schema#' title: 'Bolt Button' description: 'Buttons are the core of our action components.' type: object required: - text properties: attributes: type: object description: A Drupal-style attributes object for adding extra HTML attributes. text: title: 'Button Text' description: 'The text displayed inside a button' type: string icon: type: object description: Nested icon component. Accepts an extra 'position' prop for placement. ref: '@bolt-components-icon/icon.schema.yml' properties:

Slide 23

Slide 23 text

• Describes your existing data format(s). • Provides clear human—and machine—readable documentation. • Validates data which is super helpful for: ◦ Automated testing ◦ Enforcing rules about the data passed into components ◦ Managing API changes over time What’s a schema? https://json-schema.org/

Slide 24

Slide 24 text

Step 1: Write schemas # button.schema.yml $schema: 'http: //json-schema.org/draft-04/schema#' title: 'Bolt Button' description: 'Buttons are the core of our action components.' type: object required: - text properties: attributes: type: object description: Drupal attributes object text: // package.json "schema": "button.schema.yml"

Slide 25

Slide 25 text

// bolt-button/package.json "schema": "button.schema.yml" // www/build/data/full-manifest.bolt.json { "name": "@bolt/components-button", “basicName": "bolt-components-button", "dir": "/Users/ghows/sites/bolt-release/packages/components/bolt-button", "assets": { "style": "/Users/ghows/sites/bolt-release/packages/components/bolt-button/index.scss", "main": "/Users/ghows/sites/bolt-release/packages/components/bolt-button/index.js" }, "deps": ["@bolt/core"], "twigNamespace": “@bolt-components-button", "schema": { "$schema": "http: //json-schema.org/draft-04/schema#", "title": "Bolt Button", “description": "Buttons are the core of our action components.", "type": "object", "properties": { "attributes": { Step 2: Aggregate the data // bolt-link/package.json "schema": “link.schema.yml"

Slide 26

Slide 26 text

Slide 27

Slide 27 text

Step 3: Teach other systems about these schemas (or JavaScript)! import schema from ‘ ../link.schema.yml'; @define class BoltLink extends BoltAction { static is = 'bolt-link'; static props = { display: props.string, valign: props.string, url: props.string, target: props.string, isHeadline: props.boolean, }; constructor(self) { self = super(self); self.schema = schema; return self; } render() { // Validate the original prop data passed along // Returns back the validated data w/ defaults adde const { display, valign, url, target, isHeadline } this.validateProps( this.props, ); } • Globally provide schema data • Import schema files directly • Validate data being passed in • Automatically add default data

Slide 28

Slide 28 text

{% set schema = bolt.data.components['@bolt-components-button'].schema %} {% for size in schema.properties.size.enum %}

Button Size Variation: {{ size }}

{% include "@bolt-components-button/button.twig" with { "size": size, "text": "Example " ~ size ~" Button" } %} {% endfor %} Pattern Lab demos Step 4: Use it to power everything!

Slide 29

Slide 29 text

Component Documentation

Slide 30

Slide 30 text

import { render } from '@bolt/twig-renderer'; const { readYamlFileSync } = require('@bolt/build-tools/utils/yaml'); const { join } = require('path'); const schema = readYamlFileSync(join( __dirname, ' ../button.schema.yml')); const { tag, size } = schema.properties; // test server-side rendering via Twig + rendering service size.enum.forEach(async sizeOption => { test(`button size: ${sizeOption}`, async () => { const renderedTwigTemplate = await render('@bolt-components-button/button.twig', { text: `${sizeOption} Button`, size: sizeOption, }); expect(renderedTwigTemplate.ok).toBe(true); expect(renderedTwigTemplate.html).toMatchSnapshot(); }); }); Schema-powered Jest tests

Slide 31

Slide 31 text

Schema-powered component validation (CLI) $schema: 'http: //json-schema.org/draft-04/ schema#' title: 'Bolt Button' required: ✖ Failed to recompile Pattern Lab! Error with code 1 after running: php: “/Users/ghows/sites/bolt/packages/components/bolt-button/src/button.twig” had schema validation errors: `text` The property text is required {% include "@bolt-components-button/button.twig" with { iconOnly: true, icon: { name: "close" }

Slide 32

Slide 32 text

Schema-powered component validation (in-browser)

Slide 33

Slide 33 text

Schema-powered Component Explorer

Slide 34

Slide 34 text

$schema: 'http: //json-schema.org/draft-04/schema#' title: 'Bolt Button' description: 'Buttons are the core of our action components.' type: object required: - text properties: attributes: type: object description: A Drupal-style attributes object for adding extra HTML attributes. text: title: 'Button Text' description: 'The text displayed inside a button' One Schema To Rule Them All! Component 
 Documentation Configuration 
 Defaults ✅ CLI + In-Browser 
 Validation Component Tests Component 
 Explorer Label Label Pattern Lab 
 Demos

Slide 35

Slide 35 text

Solution #3 
 Twig + Web Components View The Components ✨

Slide 36

Slide 36 text

4 core technologies to build reusable, native-like components: • Custom Elements: defines a custom HTML tag your component uses • Shadow DOM: encapsulated markup and styling inside a component • HTML Templates: reusable HTML templates • ES6 Modules: import & use components in other components Tons of great options available for using web components: • Libraries & Tools: Lit-HTML, SkateJS, Lit-Element, StencilJS • Frameworks: Polymer 3, Ionic Framework, Angular, Preact, React, Vue.js, Svelte… Web Components: Build Once, Ship Everywhere

Slide 37

Slide 37 text

Native browser support? 
 Quite good + polyfills for older browsers. https://www.webcomponents.org/

Slide 38

Slide 38 text

{% include "@bolt-components-button/button.twig" with { text: "View The Components", url: "/pattern-lab/index.html", style: "primary" } only %} via Twig View The Components via HTML import '@bolt/components-button'; render(){ return html` View The Components `; } via JS True
 Component Interoperability

Slide 39

Slide 39 text

Twig Pre-rendered Web Components {% include "@bolt-components-button/button.twig" with { text: "View The Components", url: "/pattern-lab/index.html", width: "full", style: "primary" } only %} button.twig View The Components Rendered HTML (No JavaScript) View The Components Rendered HTML (With JavaScript)

Slide 40

Slide 40 text

No Shadow DOM? No Problem! View The Components ▸#shadow-root (open) View The Components

Slide 41

Slide 41 text

https://github.com/bolt-design-system/bolt/tree/ master/example-integrations/vue Want to go headless?

Slide 42

Slide 42 text

Solution #4 
 Progressively Decoupling + 
 Publishing to NPM npm install @bolt/components-button

Slide 43

Slide 43 text

Monorepos

Slide 44

Slide 44 text

• A repository that contains multiple packages or projects. These projects can be related (but don’t have to be). • Many of the most popular JavaScript projects have moved to a monorepo: Babel, Gatsby, Webpack, Vue CLI, Storybook, Jest... • Some of the most popular PHP frameworks like Symfony and Laravel are also monorepos! What is a Monorepo? https://gomonorepo.org/

Slide 45

Slide 45 text

• More maintainable codebase • Simplified, more easily shared, de-duplicated dependencies • Easier to coordinate updates across multiple components • Unified tools / build process for linting code, compiling, testing, deploying, and publishing • Single place to report issues / announce releases • Tests across modules are run together → finds bugs that touch multiple modules easier • One single source of truth for making any changes to the code, demos, or documentation Why Go With A Monorepo?

Slide 46

Slide 46 text

Design system contributors: 
 more unified + maintainable code Design system consumers: 
 only pull in the parts needed

Slide 47

Slide 47 text

+ How to Monorepo?

Slide 48

Slide 48 text

# publish packages w/ changes since last publishing $ lerna publish { "lerna": "3.13.1", "npmClient": "yarn", "useWorkspaces": true, "version": “2.3.0", } lerna.json "dependencies": { "lerna": "^3.13.1", }, "workspaces": { "packages": [ "packages /*", "packages/config-presets /*", "docs-site" ] } package.json How to Monorepo?

Slide 49

Slide 49 text

“Downgrading” Pattern Lab 1. Reorganized codebase so components don’t live in Pattern Lab, 
 they are simply demoed in Pattern Lab 2. Avoid using Pattern Labisms in the code being shipped 3. This allows Pattern Lab, our Twig-based static site generator (used for the docs site), Drupal, etc to all integrate the exact same way: Twig.

Slide 50

Slide 50 text

Reorganizing Your 
 Design System Codebase └── packages ├── components
 │ ├── bolt-button │ │ ├── __tests __ │ │ ├── src │ │ │ ├── button.scss │ │ │ ├── button.twig │ │ │ └── button.js │ │ ├── index.js │ │ ├── index.scss │ │ ├── button.schema.yml │ │ └── package.json │ └── bolt-icon/ ├── core ├── core-php ├── config ├── global └── build-tools ├── website │ └── src │ ├── pattern-lab │ │ └── _patterns │ │ ├── 01-visual-styles │ │ ├── 02-components │ │ └── ├── button │ │ └── icon │ └── docs/ │ ├── getting-started.md │ └── coding-standards.md ├── example-integrations/ │ ├── drupal-lab/ │ └── vue/ ├── internal-scripts/ Code that’s published
 (components, build tools, config, shared styles, cross browser polyfills, Twig extensions, etc) “Everything Else” (support code)
 (docs, website code, pattern lab demos, example integrations, deploy scripts, etc)

Slide 51

Slide 51 text

But wait, what about PHP dependencies?! • When publishing, git subtree split to sync PHP deps to READ-ONLY git repos • Web hook updates Packagist

Slide 52

Slide 52 text

Solution #5 
 Integrations composer require 
 bolt-design-system/bolt_connect

Slide 53

Slide 53 text

Design System A La Carte 1. NPM install only the components you need + build tools for compiling • These Webpack-based build tools also generate the data that automatically wires up Twig namespaces & extensions to Drupal 2. Configure the build tools based on how you want things setup in Drupal • Also solves the problem of “installed components” vs “enabled components” 3. Composer require a small Drupal module that teaches Drupal about your components + Twig extensions (bolt_connect)

Slide 54

Slide 54 text

{ "name": "pega www_theme", "version": "1.0.0", "scripts": { "build": "bolt build --prod", "start": "bolt start", "watch": "bolt watch" }, "dependencies": { "@bolt/build-tools": "2.3.0", "@bolt/components-action-blocks": "2.3.0", "@bolt/components-background": "2.3.0", "@bolt/components-background-shapes": "2.3.0", "@bolt/components-band": "2.3.0", "@bolt/components-block-list": "2.3.0", "@bolt/components-blockquote": "2.3.0", "@bolt/components-breadcrumb": "2.3.0", • NPM installed components live in the node_modules folder • Even works w/ component dependencies!

Slide 55

Slide 55 text

module.exports = { env: 'drupal', buildDir: './dist/', wwwDir: ' ../ ../ ../', verbosity: 1, components: { global: [ '@bolt/global', '@bolt/components-action-blocks', '@bolt/components-background', '@bolt/components-background-shapes', '@bolt/components-band', '@bolt/components-blockquote', .boltrc.js {% set hero %} {% grid "o-bolt-grid --flex o-bolt-grid --matrix o-bolt-grid --middle" %} {% cell "u-bolt-width-12/12 u-bolt-width-5/12@medium" %} {{ featured_image }} {% endcell %} {% cell "u-bolt-width-12/12 u-bolt-width-7/12@medium" %} {% include "@bolt-components-headline/headline.twig" with { text: article_title, size: "xxxlarge", tag: "h1", } only %} {% include "@bolt-components-list/list.twig" with { display: "inline@small", tag: "div", node --blog.html.twig { "name": "pega www_theme", "version": "1.0.0", "scripts": { "build": "bolt build --prod", "start": "bolt start", "watch": "bolt watch" }, "dependencies": { "@bolt/build-tools": "2.3.0", "@bolt/components-action-blocks": "2.3.0", "@bolt/components-background": "2.3.0", "@bolt/components-background-shapes": "2.3.0", package.json

Slide 56

Slide 56 text

https://github.com/bolt-design-system/bolt/tree/master/example-integrations/drupal-lab

Slide 57

Slide 57 text

Recap

Slide 58

Slide 58 text

1. Schemas can provide a powerful way to keep your design system up to date, validated, and well documented. 2. Twig-integrated Web Components allow for more resilient, more flexible, more future-proof, and more cross-platform interoperability FTW. 3. Progressively decoupling your design system from Drupal — and Pattern Lab can help better organize, maintain & scale your system 4. Publishing / installing your design system a la carte via NPM allows integrators to pick and choose just the parts they need. 5. Automating Twig namespaces and Twig extensions can further reduce frictions with Drupal and with Pattern Lab + address fragility issues with manual approach Key Takeaways

Slide 59

Slide 59 text

https://github.com/bolt-design-system/bolt

Slide 60

Slide 60 text

Design System + Pattern Lab BoF April 10th (Today), 2:30 - 3pm
 BoF Room 1 | Exhibit Hall | Level 4 Thanks! Web Components BoF April 11th (Tomorrow), 9 to 9:30am
 BoF Room 1 | Exhibit Hall | Level 4 boltdesignsystem.com