In my previous post, An overview of the component framework architecture, I described, from a technical perspective, how we implemented and built our component framework.
And when we started this project I thought that once we had the technical implementation in place we would soon also have our component library ready, with lots of reusable components.
It turns out, I clearly underestimated the time and complexity of deciding which components to create, what size they should be, how to name them and also the time it takes, even for a small team, to agree on these things.
It’s easy to create a component, it’s hard to create the right ones.
What is a component?
In the beginning, we didn’t always agree on how to break down our web pages into components. Some well known design patterns like pagination, slideshow, hero-images etc. were relatively easy to agree on but others were much harder.
Below, I will share our experience around a few topics related to creating components in a design system.
Granularity and nesting
When creating a component library you need to decide on the level of granularity of the components in it.
Speaking in terms of the Atomic design methodology, our components are similar in size to, what Brad Frost calls, molecules, e.g. Breadcrumb, Hero image or Tabs.
But all components aren’t as easy to distinguish as the common design patterns mentioned above.
Let’s say you have a list of content snippets categorized into groups where each group also can be toggled. Is this one large component, sharing some CSS and HTML with the component Snippet, or is it a composition of smaller (nested) components?
In the beginning we were a bit hesitant about nesting components thinking it would make our component library harder to understand and use. We also liked the idea of self contained, sandboxed, components.
But after continuously having trouble with more complicated cases we came to the conclusion that it probably was a good idea to allow nesting to avoid duplicating code in our CSS framework and also spend less time discussing the size of a component.
Since we on top of the HTML and CSS adds an abstraction layer in the form of our component templates written in XML, we could still tailor a component in a way so that the end users of our component library don’t need to know when, how and if we have used nesting in our CSS-framework.
Let’s look at a simple example where one component uses classes from two CSS-files. One file include styles specific to the snippet component while the other takes care of the layout pattern for the image and text. In this case, the latter file belongs to the object layer of our ITCSS architecture.
The Snippet component has a dependency to two CSS-files, components.snippet.css and objects.media.css.
<article data-name="snippet" class="c-snippet has-sub">
<div class="c-image" data-name="image">
<img class="c-image__image" src="/images/arcade-game.jpg"/>
<a href="#" class="c-snippet__link">The King of Kong</a>
<p>Die-hard gamers compete to break world records on classic arcade games.</p>
Release date: 21 april 2007
The XML only include tags that represent the component’s content model (header, link, image and meta data).
<gbg:link url="#">The King of Kong</gbg:link>
Die-hard gamers compete to break world records on classic arcade games.
<gbg:element name="foot">Release date: 21 april 2007</gbg:element>
The rendered Snippet
We have now also seen that it is, in some cases, also useful to allow nesting of our XML-components, e.g. our Region-component that can contain other components. You can also see how we use the <gbg:list> component to create a structural unordered list that wraps all Snippets in an <li>-element.
Example: Region with nested snippets
The template XML for Region with nested Snippets. The Region has a header and ingress and a footer.
<gbg:element name=”header”>Related movies</gbg:element>
<gbg:element name=”ingress”>Here are other movies you might be interested in</gbg:element>
The Return of the king
Gandalf and Aragorn lead the World of Men against Sauron’s army to draw his gaze from Frodo and Sam as they approach Mount Doom with the One Ring.
The King of Kong: A Fistful of Quarters
<gbg:element name=”text”>Die-hard gamers compete to break world records on classic arcade games.</gbg:element>
<gbg:link url="#">See all movies...</gbg:link>
The rendered Region
One component or many?
You often discover design patterns that look similar but not identical, or two different designs that look visually similar but that actually solves two completely different design problems. Or, you spot something that share the same content-model, but that visually looks very different from each other.
So, do we build one component with possible variations or many?
We soon understood that it would be good to define a set of principles for this but it was hard to know what these principles should be.
After getting some guidance from Harry Roberts, CSS Wizardry during a very productive 3 day workshop in the end of 2015 we tried to formulate a set of principles
- Appearance – If something looks visually very different it’s probably best to create two separate components, even though they might share the same content model.
- Purpose – if two things look visually alike but actually serves different purposes, ask yourself: Are they just coincidentally similar right now? Do you see them develop in different directions over time? If the answer is yes, maybe it better to create two separate components.
- Content model – Do they share the same content model and look similar and have a similar purpose, then maybe you can come up with a more abstract name and merge the two components into one.
We also got a good advise from Harry Roberts during the workshop which was:
Repetition is better than the wrong abstraction.
So now, when in doubt, the first option is to keep components separated and live with some duplication of code.
Naming in general is difficult, and naming components is no exception.
It’s often a good idea to give components abstract names. What you want to avoid is a name that indicates a more specific use case than necessary.
- Start page snippet – A name that indicates a very specific use case, i.e. only to be used on the start page.
- Search result snippet – More abstract but still indicates that it’s only to be used in search results.
- Snippet – A name that is abstract but that still says something about its purpose, namely to present a subset of information for some data object and provide a link to the full information.
The name, of course, is only part of the documentation.
We are still evaluating what kind of documentation is needed for each component. It currently consists of:
- A short summary the describes the purpose of the component
- Examples of how to use the component
Example: Documentation for Snippet
Use Snippet to present a small snippet of information, e.g. an snippet in a search results page. It is always actionable with a link that lead to the full information. Meta data can otionally be added to each snippet. It is possible to nest other components inside a snippet.
direction: Optional layout direction. Possible values: normal (default)|reverse
truncate: Optional max length for text element, e.g. truncate=”140”
Right now, the documentation is almost only used by the small team developing the component framework, but depending on the future situation we will probably update and revise it.
But until then, we prefer to travel as light as possible and create documentation only when we feel we need it. Also, now in the beginning, our iterations have often resulted in quite big changes and it’s often a good guideline only to document things that are stable.
Nevertheless, even during iterations, a short summary of the purpose together with a sentence or two that summarizes the reasoning behind its creation is probably a good idea.
In retrospect, this is something we could have done better. We saw that it was often difficult to remember all the arguments and reasoning between our workshops. Especially since not every one in the team could take part in every conversation/workshop/meeting.
Don’t aim for perfect
A final advise regarding the process of creating components is to not aim for perfect, instead try to, as soon as possible, begin to use the components in real situations and base your refactoring and changes on these actual use cases instead of trying to speculate and guess too much about future use cases and demands.
We spent too much time before we actually started using them and the lengthy theoretical discussions didn’t necessarily lead to a better end result either, and we often saw that they had to be refactored soon enough anyway.
So, try to iterate the Think – Build – Check cycle as fast as you can.
If you want to see all components currently in our library, you’ll find the latest version here:
This was part 3 in the publication: Our Story: Building a component library