"Engage" Design System — Case Study
Introduction
"Engage" is the extensive design system and component library used by the designers and engineers at Emgage to build high-quality, consistent, accessible products at scale. It's the heart of the Emgage App Platform—an all-in-one app creation ecosystem built to empower anyone with insight into business processes to create powerful, integrated enterprise-level workflow and process automation solutions.
As the Principle Product Designer at Emgage, I led the creation and successive evolution of the Engage design system and its surrounding ecosystem, ensuring product consistency and coherence from design to delivery, accelerated timelines, and high-quality deliverables.
Key Metrics
2x Faster
Dev Cycles
100%
Consistent UI
Baked In Accessibility
100%
Themeable
80+
Components
My Role
🎨 Principle Product Designer
🧩 Design System Lead
Key Expertise
Strategic Planning
Systems Thinking
Cross-Functional Collaboration
Accessibility
Tools
Figma, React,
Github, Azure, npm
Retired Tools
Sketch, Zeplin, Storybook
"Engage" Design System
Building the scalable design framework that propelled a large-scale product to success.
Context
Shaping a large-scale product within a ground-level startup has been a thrilling experience, filled with unique challenges and opportunities to learn, lead, and give design a seat at the table. At the start, nothing was standardized, everything was chaotic, and it was up to me to bravely don several hats and plot a path toward success—shaping not only the product experience but also the systems, tools, and processes underpinning it.
As Emgage's first design hire, in collaboration with its lead engineer, I led the creation of Emgage's extensive design system (in Sketch at the time, later migrated to Figma) and component library (React) from the ground up. Equipping our design and engineering teams to effectively design, communicate, and build at scale using the shared language of a well-curated (and tested) library of reusable components and patterns.
Solution
Design component library/UI kit (Figma) and supporting ecosystem.
Engineering component library (React) with supporting DevOps tools and practices.
Component specifications, behaviors, and guidelines.
As a testament to its success, the design system has successfully supported the extensive requirements of the Emgage App Platform and the numerous applications deployed by its clients without issue. Supporting large-scale changes such as a complete re-work of the input validation experience.
How I got there
( a.k.a. the messy stuff )
Building a Design System from scratch
How I took the Engage Design System from 0 to 1 and beyond.
Process Overview
1
Research
Deciding the systems and tools necessary to set this large-scale product on the path to success.
2
Foundations
Defining the visual and structural building blocks of the design system.
3
Components
Creating the component libraries and docs necessary to equip our designers and engineers to design, communicate, and build at scale.
4
Evolution
Continuously refining and evolving the design system to meet current needs, adapt new capabilities, and overcome points of friction.
Research
Establishing the foundation of a large-scale product has to be one of the most consequential challenges a designer or engineer will face. At scale, every aspect is amplified, with the inherent complexities, risks, and unknowns capable of crippling any product without a proper foundation.
I knew appropriate systems and tools must be put in place to manage the complexity associated with a large-scale product; however, I did not have clarity as to which. So, I partnered with our lead engineer, and we started researching. We read articles, studied guidelines created by industry-leading brands such as Facebook, Airbnb, and Google, and searched for others who had faced a similar challenge.
The research paid off as we started defining the systems, tools, and processes upon which the Emgage App Platform would be built. Below are a few key decisions we made related to the design system:
Key Decisions
1
We decided to build our application using React with TypeScript. React due to its performance and component-driven architecture. TypeScript due to the enhanced readability and maintainability of strongly typed code at scale. With Angular in the lead at the time, this may not have been the popular choice but turned out to be the right decision.
2
We chose to develop our own component library. This discussion was twofold. First, this was in the early days of React, and mature open-source design systems did not exist yet. Second, while contributing to an existing "beta" library may have given us an initial jump start, we knew the lack of control over merge approvals could ultimately slow us down.
3
We embraced SCSS with CSS Modules, enabling us to create powerful, sensible, component-scoped styling without headaches or conflicts.
4
We selected React CSS Themr for easy theming when using CSS Modules.
Foundations
Foundations encompass the visual and structural elements necessary to create a professional and functional design system. This includes components and guidance for layout and structure, color, and typography.
Spatial System
Spatial systems, such as spacing scales, grids, and layout rules, provide the consistent rhythm and constraints necessary to create a professional visual interface.
I chose a 4px base unit, constructing an SCSS variant-based spacing system for use within all system components. Though slightly cumbersome—our engineers took some time to adapt to using the spacing variants instead of static px values—the approach provided a simple way to uniformly scale spacing across the app, either by adjusting the base spacing unit or the spacing variant formulas.
This ease of adjustment proved helpful as larger, high-pixel-density screens became more common, and our initial interface spacing norms started to appear dated.
Sample layouts for large, medium, and small devices.
For the layout grid, I chose not to go with the standard 12-column grid of the time, opting instead to create a Column component that better aligned with the way people think—not in subdivisions of 12, but in fractions such as 1/2, 2/3rds, 1/5th, etc.—as they use layout to guide user interaction.
For example, a particular UI may be best served by a layout that on a large device is first split in 1/2, with a left column containing a full-width section followed by a section split into 1/3's, and a right column containing a full-width section followed by a section divided into 1/4's. Next, on a medium device, the initial 50-50 split may be better served by a 60-40 split, with the second section of the right column now displaying 2 columns (with wrapped content) instead of the initial 4. Finally, for a small device, the layout requires the initial two columns to be stacked, with the top column containing all full-width sections and the bottom column containing the full-width section, followed by a wrapped 2-column section. Try accomplishing that with a 12-column grid. You can't, and your user experience suffers because of it.
The column component proved to be the right choice, allowing us to quickly create a fitting layout for each page without compromise. Furthermore, it became the foundation of one of the platform's most appreciated services, the Interface Builder, allowing users to quickly create pages and forms.
Typography
The 4px base unit was also integrated into typography to ensure type line heights and spacing aligned with the rhythm of the rest of the UI.
Typography styles and color hierarchies were added to the design system, and three type-related React components were developed to ensure consistent application across the platform. In addition to consistency, this approach provided a simple method to scale type across the application and to quickly apply fixes.
For example, when we discovered that one of the colors in our type palette did not meet the WCAG 2.1 Level AA contrast guideline desired for accessibility, all it took to resolve the issue across the entire application was a single adjustment within our design system. Because I had selected descriptive yet generic names for the type palette (Darkest, Dark, Mid, Reverse, Danger, etc.), the change was effectively transparent.
Colors
Initially, a limited system-specific color palette was developed, consisting of just a few colors and their tints, with our theming architecture providing the scale needed for future expansion. As the application grew and complex visual elements were added (e.g.: data visualization widgets), the need for a more comprehensive color palette was recognized, and the Material UI palette was adopted. Our theming architecture proved capable and flexible enough to handle this expansion without issue.
Component Library
With the foundation of the design system in place, I was able to move on to creating the components that would serve as the building blocks for the Emgage App Platform application.
Having started work on many of the app interfaces, I already had the beginnings of a design component library and a solid understanding of which code components to prioritize. Desiring not to hinder our engineering team's efficiency, I did what I could to devote my focus to the React component library, composing specs and working hand-in-hand with our front-end engineers to create the necessary components.
Initially, I held a very hands-on role in the development of each React component—determining composition, defining props, developing render markup, generation styling, and ensuring component accessibility. As the team grew, I was able to delegate much of this to a UI engineer I had hired and trained.
I created decision trees for cases where component usage relied on specific patterns or rules. These proved extremely helpful in ensuring a consistent user experience.
Modular
A key to the design system's success is the way it adopts software design patterns and best practices such as modularity, composition, and single responsibility in its construction.
Each component has been created to solve a specific need, with elements and functionality shared between multiple components abstracted into unique sub-components.
Consider the label, for example. Every input has a label. While a label will never exist on its own, it is reused in the composition of numerous components, such as the text field, checkbox, and picker. Abstracting the label into its own component creates consistency and eases maintenance. The same applies to other elements and functionality, such as validation, errors, overlay positioning, and loading indicators.
This modular approach of abstracting what's shared ensures consistency, eases maintenance, and provides a toolkit of building blocks for future components.
Accessible
"Disability is part of being human," says the World Health Organization, with most everyone experiencing temporary or situational disability at some point in their life.
In my opinion, there is no excuse for not baking accessibility into your development process, with several great tools and resources available that simplify both testing and issue remediation.
As design system components were developed, they were tested for accessibility. I initially performed this testing, sharing best practices and fixes with our engineers as we went along. As the team grew, I was able to train our UI engineer to conduct this testing, resolve issues, and work alongside our front-end development team to ensure component accessibility.
Evolve
Design systems are, by nature, living things, continuously changing and evolving to meet current needs and adopt new capabilities.
One of the design system's most significant evolutions came about as the result of an update to Figma's Variants feature. The update made it possible to establish parity between the configuration properties of design components and the interface props of code components in our design system. This meant our engineers could simply click a component in the Figma mockup of the interface they were implementing to review its props configuration. This further accelerated development and had a profound effect on product consistency, aiding in the elimination of discrepancies between our design and code components.
Benefits
A few benefits of a well-implemented design system:
Accelerated Design and Development Cycles
A design system allows teams to quickly select and configure premade components without needing to reinvent the wheel, codifying design decisions and patterns that can quickly be replicated at scale.
Consistency
A design system provides a single source of components, patterns, and styles for use throughout products, delivering a familiar, intuitive experience that enhances overall product usability.
Better Code and Design Quality
Building for reuse (instead of a one-off) intrinsically leads to better quality. The wide use inherent to design system components means they have been well reviewed and tested, with shortcomings surfaced and resolved.
Better Knowledge Sharing
Design systems provide a shared language between designers and engineers, promoting knowledge transfer and eliminating waste created by miscommunication.
Maintainability
The centralized nature of a design system means fixes and improvements are made in just one location and automatically propagated throughout the product.
Built-in Accessibility
A design system with accessibility prioritized from the start sets products on the right path, giving engineers the tools they need for building accessible products.
More Flexibility
Counterintuitively, a design system offers greater flexibility in evaluating designs by reducing the effort required to create and revise mockups.
Challenges
Overall, creating a scalable design foundation from the ground up was a successful and rewarding experience. Nonetheless, it was not entirely without challenges, of which I'd like to share a few.
Avoiding breaking changes
As mentioned earlier, design systems are living entities that by nature continuously change and evolve. This dynamic nature led to one of my biggest challenges—learning how to update a Figma component without introducing breaking changes.
In Figma, updating a component in a Library will propagate that update to any files where the component has been used, presenting you with a prompt the next time you open the file.
Wanting to keep your files up to date with the latest, you typically hit the "Update" button. However, in some instances, especially after the structure of a component's layers has changed, the update would cause any overrides to the component to reset, essentially "breaking" the design.
Learning more about what triggered these breaking changes and implementing some cool tricks I learned from a speaker at one of the Figma Config conferences helped me avoid breaking overrides. However, I must confess, I still cringe a bit when clicking "Update" on files I have not opened for a while.
Version Control in Figma
Version control is essential to software development, allowing a team to develop and test product improvements without affecting the current release. While Figma offers a built-in version history, I've yet to determine an effective method to emulate the branching and merging capabilities of a full-fledged version control system such as Git—a challenge I'd like to spend more time with. Being able to access or apply a specific design system version within Figma would be significant, especially when working on component updates, integrating recently introduced Figma functionality, and keeping Figma art aligned with a release using an older design system version during QA.
Teaching Engineers New Tricks
Many engineers on our team did not have experience working with a design system, considering accessibility, or working on a large-scale application. This meant keeping a lookout for bad practices, and regularly engaging with front-end engineers to provide guidance, advice, and training surrounding user-centric design, accessibility, scalable design principles, design systems, and component-based development. Most engineers welcomed the opportunity to learn and grow, making this process enjoyable and rewarding. However, a few were not as eager, creating a particular challenge.
Using the Engage Design System, I crafted the following experiences for the Emgage App Platform:
Data: Relational data creation, management and governance, item and field templates, faceted search and discovery.
Interfaces: An intuitive, visual builder for creating custom forms and pages.
Workflow: Innovative process automation with powerful conditions, triggers, connections, and automatic interface generation.
Templates: Pre-built, customizable templates to kick-start projects and shorten timelines.
Messaging: Data-driven digital communication across email, SMS, and in-app channels.
Agreements: Frictionless document creation, templating, and e-signatures, all seamlessly integrated with our customer's data and workflows.
User Management: User management, provisioning, groups, and roles.
Authorization: Granular permission and policy creation, entity and field level authorization, intuitive management of direct and inherited policies.