Design Systems

The ability to version, manage, and distribute design libraries in Abstract is one of our users’ favorite features. Libraries can be small and useful so you don’t repeat yourself as you gain momentum on a growing project, or they can be the backbone of a design system for an enterprise team’s many products. Managing libraries in Abstract enables teams to scale their design component libraries very efficiently. By considering some of the following best practices, you can be sure to set your team up for success when working with libraries in Abstract.

TL;DR

  1. Leverage clear and predictable naming conventions that communicate intended library use.
  2. For quick visibility and performant files, organize in a way that uses several small files rather than few larger ones. We recommend one component type per file.
  3. Patterns that touch many projects shouldn’t change often and belong in a Design System project. Patterns that are context-specific and might be updated relatively frequently belong in a per-project library alongside the files where their instances are used.
  4. The more you can organize reusable patterns into library files, and thus move edits to the symbol master level, the smaller your merge conflicts will be.
  5. Leverage Sketch plugins to make using and analyzing your libraries easier.

Organize and name your libraries

Projects and sections

We see users have the most success with their libraries when they are built on the foundation of a centralized design system. This design system is most often given its own project consisting entirely of library files, with each component type distributed into its own file. These component files are then linked to any project that needs them. When an organization needs to utilize multiple central libraries or multiple themes on top of a library, it’s a good idea to have a section to contain them.

In this way of organizing, the answer of where a component lives is relatively self-evident. Instead of scrolling through large files or digging through dozens of pages within one file, you get a quickly digestible index of your components in the master files view of the project. This technique also helps scope the change management of a design system in clear ways, limiting the footprint of changes to single files or pages of files, which lessens the likelihood of merge conflicts. More on that later.

Naming for interoperability

As it is in programming, naming things is hard. But investing in clear naming conventions returns a much lighter mental load in your day-to-day design work. Abstract and several other design tools work with the common convention of slash separated naming.

This convention mirrors a directory structure and helps provide context into a component’s category and its behavior in relation to other related components. For example, a button component could be named button-group/large or a color could be named Color/Red/Red-10. The benefits of this approach are maximized when the way you name your components is informed by the ways your engineering colleagues implement them in code. In this example below, a developer can recognize red-10 from their convention and quickly know what they’re working with instead of fussing with less human-readable RGB or hex values.

Naming for intent

In addition to naming a component for what it is and why your team needs it, sometimes you’ll want its name to communicate how it should be used. Or maybe you’ve run into an outlier in your pattern library and you’d like to convey its uniqueness. Leveraging emojis or clear and efficient abbreviations is a effective way to accomplish this. Check out these two examples:

1. Testing or deprecated components

It’s likely that at some point, you’d like to test out some experimental components, or any other type of symbols that haven’t yet been approved. In this situation, it could be a good idea to append an emoji onto the component name to tag it as experimental and “not approved”. Our team uses the crystal ball emoji, giving these components names like Desktop Inputs 🔮. That way, this component can be merged into our Design System project’s master and linked to any other project while still having its acceptance status implied wherever it is seen. The same convention could also be applied to old components that need to be used on older legacy projects but not anywhere else.

2. Templates

Another example where usage instructions can be implied is in the case of templates. Like reusable components, templates help you to not start from scratch on common types of screens you might need to design. Unlike reusable components, however, templates are made with the express purpose of being altered beyond simply overriding. They can be anything from groupings of pre-defined library components to entire layouts. In these cases, it would make sense to communicate that a symbol is intended to be detached within its name. Our team uses the broken heart emoji, as in Menu/2 Select 💔 indicating that after you insert this selection menu, you’re supposed to detach the symbol and customize it to your specific context.

Linked libraries vs. per-project libraries

Generally, it’s a good idea to have foundational designs that are reused in many projects (and thus shouldn’t change often) in a designated design system project. These can be utilized throughout your organization through linked-libraries. For the purpose of promoting stability and healthy change management, updates to these far-reaching pieces will need to happen through a more formal branch, commit, review, and merge process. Context-specific arrangements of these pieces, along with other patterns that don’t get utilized elsewhere are best organized in per-project libraries that can be changed on the fly within branches, without having to leave Sketch.

In the example above, the Navigation.sketch is a per-project library file that contains context-specific arrangements of symbol instances from Iconography.sketch and Spacing.sketch.

Merging per-project libraries

One potentially unexpected benefit of using libraries more is much cleaner merging process. In general, the more your designs are organized into components that are distributed through library files, the less merge conflicts you will encounter. This is because if you make decisions and edits on the component level (in their library files), the changes that might be eligible for a merge conflict are limited to that smaller number of symbol masters rather than the many possible compositions of their instances.

Example: Collaboration on the same artboard with no conflicts

Suppose you have a project for a website and you and a collaborator are looking to make changes to the same artboard in a file called Homepage.sketch. Let’s say you need to update the the navigation while your collaborator edits the footer. Instead of making changes directly to the mockup in Homepage.sketch, in which case one of you will need to accept or reject changes that would overwrite your version of the artboard before you merge, doing this work in separated library files will avoid these merge conflicts entirely.

If you scope your updates to the symbol masters in a Navigation.sketch library file while your collaborator does the same in a Footers.sketch library file, both of you will be able to merge to master (or a shared parent branch) with no conflicts from the other. Fully updating the targeted mockup then only requires simply opening Homepage.sketch, accepting library updates, and merging.

Plugins we love

  • Sleuth, which integrates with our SDK, is a great way to get a sense of how well your team is adopting your design system.
  • Sketch Runner is the tools of choice for many teams, including ours, for quickly finding and placing symbols.
  • Symbol Swapper is a quick and efficient way to update symbols in bulk.
  • If the developers you collaborate with use Storybook for UI development, they might find it useful to use our Storybook add-on to display component mockups from Abstract directly next to their live components.