A project at Concentric Sky applies the expertise of user experience design, graphic design, web development, software engineering, quality assurance testing, and systems engineering. Our teams have diverse backgrounds and a breadth of expertise. However, these various disciplines mean that we have different approaches to understanding and solving problems.
Modular Design is how Concentric Sky empowers each discipline to use the same tools for designing and building interfaces and work towards project goals in tandem. This results in a more agile approach with less of the drudge of waterfall – all while maintaining an enjoyable user experience and delightful presentation. It sets the team’s sights on understanding the problem and testing solutions early and often. This is accomplished by eliminating unnecessary documentation and pedantic review cycles.
To understand how this is done we must first review the role of design and the reality of software development. Here we’ll find some ingrained assumptions and demonstrate how Modular Design challenges these while offering a clearer path forward.
Atomic Design, BEM, and a variety of in-house solutions are a sign that traditional approaches to software design have shifted towards design systems rather than design mockups. There are apparent benefits:
Consistency: the user gets a cohesive experience, especially across an ecosystem of products. Interfaces become more intuitive while enforcing brand standards.
Manageability: a design system is a bottom-up approach that allows us to think of our interfaces as a collection of parts. Changing those parts will predictably impact the interface when it comes to rebrands, white labeling, and enhancements.
However, executing a successful design system is difficult and requires us to challenge a lot of ingrained assumptions.
At Concentric Sky, Lean UX has accentuated our need for design systems. To be successful, we need to test our assumptions early and often, and respond to new user data quickly. From a technical perspective, styleguides such as SMACSS (similar to BEM) and concepts from Web Components have informed how we construct our interfaces. Our goal is to unify these approaches and meet the requirements of designers and developers alike.
An iterative design process, which allows us to test and tweak often, is unique to software. Software tends to be large and complex, yet its strength is in its flexibility. On the other hand, disciplines like architecture and print design remain rigid and require certainty before execution. This understandably leads to longer design cycles.
Design systems fail when the designer and the developer are not unified in their approach. Not using the same design system is akin to having two team members who are fluent in different languages: great for them and anyone who speaks the same language, but requires heavy translation when speaking to each other. We wanted to remove the need for translation and empower designers and developers to use a singular design system that persists from prototyping through launch – and beyond.
A successful design system is:
- Iterative: global changes should be trivial as requirements change and our assumptions are tested.
- Extensible: we need building blocks to start from (instead of a blank page) to solve new problems. These building blocks should be flexible and able to adapt to nuance.
- Ubiquitous: “Call to Action Card” means the exact same thing to designers and developers.
From Print to Software
Mockups are necessary to define the final product, however we shouldn’t confuse the means with the end. A mockup traditionally serves these roles:
- For designers: the canvas where their ideas come to life.
- For stakeholders: ensure that qualitative and quantitative goals will be met.
- For technicians: detailed specification on how to reproduce the design in its final medium.
A print designer’s mockup represents physical plates and there is virtually a one-to-one relationship between the mockup and the result (what you see is what you get). Software, on the other hand, has a more subtle relationship between the mockup and its final medium. Though it’s changing, most design software still outputs specification for technicians based on the concept of the printed page.
In software a mockup represents a view, and views are constructed from smaller pieces we call modules. For a developer there’s no equivalent to a plate. Modules are defined and then assembled together to create a view.
Modules are necessary because most developers follow the DRY principle (do not repeat yourself). Instead of specifying how an element should appear multiple times within a product (as would be the case with each plate in print) they will define it once, and then point to that definition whenever displaying that element. The DRY principle allows for rapid iteration because we primarily deal with declaring and updating definitions rather than views. Modular Design embraces the DRY principle and empowers designers to specify how their design will be produced in the final medium: code.
Enter Modular Design
Modular Design allows designers and developers to have objective discussions about the impact of design decisions. Modular Design is a language for defining and discussing the modules we use to create our views. Modules aim to solve common user experience problems in a generalized way. Together, with content, modules create a narrative that addresses context-specific problems (the view).
Nesting and Recursivity
Modules can be nested and recursive which allows us to define every aspect of the interface as a module. As requirements change, and assumptions are challenged, modules adapt holistically but independently from each other.
The goal is to properly encapsulate every element of the interface. Encapsulation occurs based on the problems the module solves, rather than appearance or branding concerns alone. New modules are designed when gaps in our problem solving are revealed through testing or changing requirements.
We’ll draw lines around an interface to determine where meaningful encapsulation of modules exist. Once a module is encapsulated it cannot affect modules outside of it, or other modules within it.
Counterintuitively, agreeing to more constraints makes our interfaces easier to manage and extend. We can make design decisions knowing what the impacts will be in code. This saves time and money by eliminating the need to always rethink portions of a product, and enhancements are easily translated through the whole interface.
It’s best to organize and name modules based on the problem they solve. “Header” is a better name for a module than “Portfolio Title” as Header implies it can be used in more than one context. Along with the name it should be clear what the properties of the module are so that our design system is always up-to-date and evolving with the product.
A Module’s Properties
A module is made up of three properties: rules, states, and submodules. Rules describe the module – its appearance. States and submodules override the default rules.
Rules – The “Button”
Rules describe what makes the module unique and repeatable within the interface.
It’s okay to break the rules
When the only difference between two modules are a few rule changes then they’re likely the same module. Those changes should be documented as a submodule or state.
States – The ”Button is Disabled” state
States indicate a module’s behavior (what it can do). A module changes its state after being loaded into the view. Since modules are mobile first, any responsive changes are considered a state. States can also chain together – such as a hover state on a disabled state.
States always indicate modules
In order for an element in our interface to receive a state it must be a module (and thus be encapsulated and documented). Identifying states is the most useful tool for identifying modules.
Submodules – The ”Button Secondary” submodule
Submodules change the rules of a module, along with its states. This allows us to extend our interface without the need for heavy lifting. Submodules tweak and remix modules in a deliberate and organized way. When we encounter nuance such as the need for more or less emphasis, new messaging, or adapting to content changes submodules are the answer. They allow us to keep our modules focused on the problems they solve while making them optimized for new contexts.
Submodules are like copies or re-skins of a module which selectively overrides certain rules. Submodules can also be chained together – such as Button Secondary along with “Button Uppercase”.
Modular Design works only when an entire team buys into it. This means something different for each discipline but the above properties are universally accepted and useful. Our team’s shared understanding is what allows for a cohesive design system for the whole process – from prototype to launch.
We’ll continue to explore various aspects of applying modular design in future articles such as how to organize design files into modules, develop modular code, and facilitate a process of rapid iteration.
Thanks to Adam Barton, Nic Marson, and Jesse Holk who helped with the content and illustrations for this article.