The web continues to evolve further and further away from its document-based heritage. Whereas we once thought of fixed-dimension ‘pages’, today we think of ‘systems’ that serve a variety of devices and users. The requirements of a growing ecosystem have necessitated a change in workflow and methodology. One popular approach is Brad Frost’s Atomic Design.
Brad encourages the division of UI components into smaller ‘Lego bricks’ that continue to combine until they resemble the familiar page. You would start by identifying a feature (selecting users), then create a component which satisfies it (such as an interactive people-picker). This would, in turn, eventually reside within a template alongside other components which would make up the finished UI.
As Brad remarks:
A well-crafted design system caters to the content that lives inside it, and well-crafted content is aware of how it’s presented in the context of a UI.
Instead of designing and building individual pages, Atomic Design focuses on the individual components needed in order to meet the demands of our application. The result of this is a pattern library, a set of reusable interface components that document both behaviour and implementation.
Conceptually this sounds great, enabling a cohesive design system and faster iteration. Yet how well does it work in practice?
The larger an application gets, the harder it is to maintain a reliable mental model of the individual components that make it up. Do we already have a drop-down menu? Is this accordion in use? Why do we have 5 different button styles?
Atomic Design allows us to view an inventory of components separately from our application, while also letting us see patterns in the context they’ll be used:
By creating an interface inventory we can quickly identify existing design solutions to new problems while also ensuring they work in-app.
As Brad remarks:
The parts of our designs influence the whole, and the whole influences the parts. The two are intertwined, and atomic design embraces this fact.
By encouraging this interplay between component and application, we end up with a far more consistent and robust UI.
Separation of concerns
One of the great benefits we found when working with Atomic Design was the separation of concerns between the front-end design system and the production application. Since patterns are shared between the static pattern library and the live application (both use Twig), new changes in markup or styles propagate through automatically.
The result is a ‘living style guide‘ that encourages a kind of reciprocity between the front-end UI and the production application. Components can be developed separately from the application, tested, then easily imported into a more realistic context to test their appropriateness in-situ. This is also extremely useful at the start of the project, as it lets front-end development kick off without any reliance on back-end application logic or frameworks.
Once a set of patterns has been established it becomes far faster to build new pages (at least on the frontend). Instead of constantly reinventing solutions, modules can easily be reused across contexts to add new features and functionality.
Seeing an at-a-glance list of components makes it far easier to assess their appropriateness or identify opportunities for extension. This, in turn, makes it simpler to estimate the time it will take to implement new features since they can be easily matched against the existing inventory.
At Browser we’re big fans of Test-Driven-Development, even on the frontend. Using a testing framework (such as Jest) ensures front-end components continue to behave as expected throughout development. Adopting an Atomic workflow makes this even easier since each pattern is designed to work (and therefore be tested) in complete isolation.
Pattern library generators, such as Pattern Lab, often generate a separate HTML file for each component, making it extremely easy to run against a testing framework.
Despite Brad’s suggestion to ‘loop back…to better address the real context’, we’ve found that pattern-based workflows can result in components that work better in isolation than within the finished product. Design in any form is about context, so achieving a balance between context-sensitive and context-agnostic can be challenging.
As soon as patterns are combined into layouts the temptation quickly becomes to use that as the reference point and avoid the individual (perhaps non-representative) pattern views. If you’re not careful it becomes very easy to slip back into a traditional ‘document’ based workflow (especially once the production application reaches maturity), even if the underlying structure is modular.
The re-use of patterns across various contexts is great for maintainability and consistency, yet it often encourages the (re)use of a pattern that isn’t fit for purpose. Having a toolkit of pre-made patterns makes it very tempting to cobble together a solution instead of devising something more appropriate from scratch.
Similarly, by creating patterns to be as contextually flexible as possible (and therefore maximize their re-use) you run the risk of them not being entirely right for anything at all.
This makes pattern reuse a double-edged sword. It’s far faster to iterate, but if you’re not careful the final result can be a compromise. It’s the same issue that plagues front-end frameworks such as Bootstrap.
Responsive websites use ‘media-queries’ to adjust themselves to the size of the user’s browser/device, but these are always in relation to the size of the device. Components have no knowledge of their own width, so all pattern changes must happen in response to the page size, which is particularly problematic if they’re going to be dropped into different containers. Until we get the fabled ‘element-query‘, this will continue to be a pain point.
Responsive images suffer from a similar problem. In order to be effective, they need to be told how large they are in relation to the page size, not their parent. This means any pattern which uses bitmap images, such as photographs, needs to consider how it will handle switching contexts.
Like many methodologies, Atomic Design can feel like a problematic trade-off. For every technological advantage gained (speed, consistency) there’s a workflow or process problem.
Atomic design is not a linear process, but rather a mental model to help us think of our user interfaces as both a cohesive whole and a collection of parts at the same time.
This interplay is central to the process’s success but also presents its biggest challenge. Jumping between these mental models requires not only a technological shift but a fundamental change in the way we approach designing user interfaces.
However, the long-term value in creating a modular, reusable set of components is undoubtedly worth the overhead. Faster prototyping, easier scaling and a consistent design vocabulary combine to result in higher quality applications and a better overall user experience.