Frontend Architecture for Scalable Design Systems

salem_ghoweri
6 min readApr 14, 2019

--

This past week at DrupalCon Seattle, I presented my 30 minute talk on “Frontend Architecture for Scalable Design Systems”!

In this jam-packed session, I outlined 5 of the front-end architectural approaches we’ve been using on the Bolt Design System to address the maintainability and fragmentation problems that ultimately killed off our first design system attempt.

Definitely go check out the session and the slides for all the nuts and bolts but here’s the quick TLDR version 😉!

1. APIs

Automatically generated, always up to date color palette swatches demoed in Pattern Lab. Even the text color’s contrast is automated!

Sufficiently documenting your design system is hard, but doable.

Indefinitely maintaining and improving that documentation for developers, designers, and content authors to always stay up to date and relevant? Borderline insane.

One big way we’ve been tackling this whole “maintainable docs” problem is via APIs.

Basically, we make sure certain types of things in the system system’s codebase automatically stay up to date— especially things related to specific colors, sizes, breakpoints, config options, etc.

4 Step Process

  1. Make your code is “export friendly” (ex. via Sass Maps)
  2. Automatically export data about your code (ex. auto-convert Sass Maps to JSON via custom Sass function)
  3. Automatically-wire up this data to other systems (ex. globally make sure Twig templates have access to all global design system-specific data)
  4. Use it! For example, we use this always-up-to-date data to loop through our color palette options and generate our swatch demos in Pattern Lab.

This approach has helped us better manage the dozens of little moving parts that make up the system’s codebase AND has allowed us to build off of this approach to produce some really neat stuff.

Automated Color Contrast Check

2. Schemas

https://twitter.com/BasaltInc/status/1116070893602480128

Schemas are the “human and machine-readable” way to define the rules about how things like Components can be configured, customized, and used.

For example, here’s a portion of the schema file we have for our button component:

Notice how the icon prop points to the icon.schema.yml file? API-powered docs and references!

I’ll skip over the implementation details but suffice to say, just one schema file can power TONS of different things!

For example, we currently use schemas to:

  • Generate many of the component demos in Pattern Lab
  • Enforce component validation via our CLI and browser console messages
  • Setup default / available component config options
  • Auto-generate docs in Pattern Lab to display available component options
  • Setup Jest tests used for for visual and functional regression testing
  • Power our in-browser component preview (my personal favorite)
Twig-powered, in-browser component explorer

3. Twig + Web Components

Oh boy. This one probably deserves it’s own dedicated Medium post given the (controversial?) buzz on Twitter around this!

Until I can get around to writing a deeper dive on this, here’s just a couple key takeaways from this section:

  1. Web Components are 3 (or 4 if you count ES Modules) official browser standards / APIs to allow devs to build super powerful solutions. Think of them like the “native” version of components seen in JS frameworks like React, Vue, Angular, etc.
  2. The latest versions of Chrome, Safari and Firefox all fully support Web Components. Edge support is half-way there (working on it) and both Edge and IE 11 can use Web Components via polyfills like webcomponents.js.
  3. Tons of lightweight framework-agnostic libraries are out there like SkateJS (no JavaScript framework required) however nearly all of the big frameworks out there have perfect support if you want to use Web Components with a framework like, Preact / React, Angular, or Vue.
  4. We’ve been moving all of our components over to Web Components for more universal cross-framework / cross-platform support so our design system can solve even more problems in many different areas and use cases.
  5. Polyfilling Shadow DOM in IE 11 is a performance nightmare SO instead we treat Shadow DOM as a progressive enhancement based on browser support, component support, and component-specific use case support — all without giving up super powerful features like <slots>s!
  6. We pre-render many of our web components via PHP-rendered Twig templates (a little more on this below) 😱

Twig Pre-rendered Web Components

TLDR: our Drupal-friendly Twig templates setup the minimal amount of markup / HTML logic needed for our Web Components before our JavaScript loads up and takes over.

Twig Pre-rendered Web Components

Yes, that means our button.twig template has some duplicate template logic with our button component’s lit-html template HOWEVER both of these solve different problems in different environments (client-side vs server-side).

For us, the benefits for us far outweigh the minimal overhead:

  • Progressive Enhancement: most of our components visually and functionally work if JavaScript is manually turned off, is slow to load, or stops working if an error occurs. Ex. our button component still looks good and is a semantic clickable <button> or <a href>, etc.
  • Customization: this approach provides us with a way to customize how a web component’s markup gets setup / used (ex. adding the ability to add data attributes to some HTML element that client-side JavaScript retains when hydrating)
  • Backend Integration: this Twig layer also provides us with a rich, extendable templating language that backend devs can use to prep data passed along to these templates
  • Perceived Performance: no flash of unstyled web component HTML FTW
  • Component Defaults: server-side schema validation and config defaults
  • Server-side performance: I’ve been working on a pure Javascript-powered server-side rendering solution using JSDOM however things are still a bit too slow (like, 0.4 to 1.5 seconds to render every single component, slow) to ship as-is. Caching might help but until we have a more performant solution, PHP-based Twig templates are still more performant!

There’s a ton more to chat about on this evolving area but needless to say, Twig still is a super valuable tool for us and has significantly helped with design system adoption, better end-use experiences, and more compliant component usage.

4. Publishing to NPM

TLDR: we switched to using a monorepo + Lerna to publish nearly everything in the design system as standalone package on NPM.

This also means that Pattern Lab has been “downgraded” from being where components live (single source of truth) to instead be “just another consumer” of the design system’s bits and pieces — no different than Drupal or any other system wired up to use the design system.

Code that’s published vs “supporting code”

It’s almost as if our progressively decoupled design system has been progressively decoupling itself…? 🤯

5. Automating Twig Integrations

Finally, last but not least, we decided to ditch the “best practice” approach that nearly every single Drupal-integrated design system has been using — using the Drupal Components module and writing out each Twig namespace path manually — and instead switched to something that’s a bit more automated and less fragile (see the reoccurring trend here?)

In the Drupal theme:

  1. Components are NPM installed just like any other front-end dependency,
  2. They are added to a simple .boltrc config to add them to the Webpack build process (without needing to know Webpack)
  3. Via the tiny bolt_connect Drupal Module we built, we automatically teach Drupal where to find these components (added via Twig namespaces internally) + add any shared Twig extensions.

It’s the same overall idea as the Drupal Components module BUT unlike before, renaming a component folder, etc won’t cause the Drupal integration to break because we’re teaching telling the Twig environment inside of Drupal where to find a component’s Twig template, etc automatically.

NPM install components, add to the config, do a front-end build and switch on the bolt_connect module!

Phew — it’s been a long but super exciting week. Can’t wait to share more about the work we’ve been doing to make design systems a heckuva lot more scalable and maintainable!

-Salem

Be sure to check out our docs site or take a peek at our code on Github!

Resources:

--

--

salem_ghoweri

Lead Frontend Architect at Pega; creator and lead maintainer of our web component and Pattern Lab-powered design system, Bolt. https://boltdesignsystem.com