percipio.london: Integrating atomic design in your CraftCMS workflow
In the natural world, atomic elements combine together to form molecules. These molecules can combine further to form relatively complex organisms. We use this same principle in our development workflow – read on to find out more.
Taking cues from chemistry
In the natural world, atomic elements combine together to form molecules. These molecules can combine further to form relatively complex organisms. To expound a bit further:
Atoms are the basic building blocks of all matter. Each chemical element his distinct properties and they can’t be broken down further without losing their meaning.
Molecules are groups of two or more atoms held together with chemical bonds. These combinations of atoms take on their own unique properties and become more tangible and operational than atoms.
Organisms are assemblies of molecules functioning together as a unit. These relatively complex structures can range from single-celled organisms all the way up to incredibly sophisticated organisms like human beings.
Now, we might be simplifying the incredibly rich composition of the universe ever-so-slightly, however the basic premise remains
atoms combine together to form molecules – which further combine to form organisms.
This atomic theory means that all matter in the known universe can be broken down into a finite set of atomic elements.
OK, High school chemistry lesson over… what has this got to do with web development? Watch the quick video below first.
All websites, apps, intranets, hoobadyboops and whatevers are all composed of the same HTML elements.
So, if Dmitri Mendeleev can organise the universe into the periodic table, then we should be able to do it with our web application development. This gives our entire team a common language when discussing projects and allows us to think of our user interfaces as both a cohesive whole and a collection of parts at the same time.
The atoms of our interfaces are the foundational building blocks that comprise all of our interfaces.
These atoms include the basic elements like form labels, inputs, buttons, images and others. They can’t be broken down any further without ceasing to be functional.
Each atom has its own unique properties, such as the ratio of a hero image or the font size of a primary heading.
These properties influence how each atom should be applied to the broader user interface system.
Molecules are groups of atoms bonded together that take on distinct new properties.
They are relatively simple groups of UI elements functioning together as a unit. For example an image, heading and description can join together to create a card molecule.
Making simple UI molecules makes testing easier, encourages reusability and promotes consistency throughout the project.
Organisms are more complex UI components, they are created by groups of molecules and/or atoms and/or other organisms which form distinct sections of an interface.
While some organisms like a header might consist of different types of molecules like a primary navigation, search form and logos, others might consist of the same molecule repeated over and over again.
For instance if you visit an eCommerce site or a news site you will most likely see listings of articles that are contained in repeatable cards or panels.
Building up from molecules to more elaborate organisms provides us with an important sense of context – they demonstrate those smaller, simpler components in action and serve as distinct patterns that can be used again and again.
Templates are page level objects that place components into a layout and articulate the underlying content structure of the design. They have the important characteristic in that they focus on the page’s underlying content structure rather than the page’s final content.
Atomic design provides us a structure to navigate between parts and the whole of our UIs, which is why it’s crucial to reiterate that atomic design is not a linear process.
It would be foolish to design buttons and other elements in isolation, then cross our fingers and hope everything comes together to form a cohesive whole. This means, don’t interpret the stages of atomic design as “Step 1: atoms; Step 2; molecules, Step 3: organisms; Step 4: templates”.
Instead, think of the stages of atomic design as a mental model that allows us to concurrently create final UIs and their underlying design systems.
Template structure setup within Craft
So, how do we translate all of this into the Craft CMS template structure?
In our templates folder we create three main groups for our atomic design structure. As you may expect, they’re named according to our atomic design the principle _atoms , _molecules and _organisms.
You’ll notice every folder is prepended with an underscore _. This ensures that these directories cannot be loaded directly via a URL
Inside of these main folders we have pluralised, sub directories (buttons, images, links…). Inside these directories you’ll find template files in the singular form of its parent directory name.
For example, inside
buttons we have
Doing so means that when we pass properties though an object we don’t need to think about variable order in the function as showcased in this simple atom render function
This means that by retaining a clean and purposeful naming convention within our directory structure we can build larger, more complex web applications at scale.
Inside of our
_atoms directory we house all our atoms in our projects. As a reminder, these smaller templates are the foundational building blocks that we will need throughout our project – items that cannot be broken down any further.
Text atoms are probably the simplest atoms you will find in projects, all they do is contain text content in different formats and styles – think headings, paragraphs etc.
As every atom that we create will share common properties, we create a twig file that contains our common properties and name this
Now that we have our base file in place, we can extend the specific atoms from these properties, avoiding repeatability and copy/pasting. As you can see in our
We slimmed down the comments in this one, as even tho they’re available through the parent template, we are not using color and alignment here.
And the beauty of this… we don’t need to! As every property has a default fallback, using the empty-coalesce operator
??? which is not built-in into Craft sadly. But as usual, Andrew Welch have us covered with his empty-coalesce plugin
Applying this same logic to a more complex example we can take a look at a button atom. As with text, we need a
button--props file to house all of our button properties.
You will notice a lot of additional properties here, since a button does a lot more than just showing some text. Buttons contain an action or url, they might have an icon and they’ll most likely require some event tracking functions too.
Atoms shouldn’t be too complicated, so we try to avoid into overloading with logic statements.
Here we have our object that groups all the tailwind utility classes in a readable format. Since we also have the possibility to use icons in buttons we can even include another atom into this atom
A button with an icon is still too simple to label it as a molecule. Doing so would break consistency as buttons would live under molecules and under atoms – sometimes a little sane sacrifice needs to be made.
But why not include the icon inside of the button atom? The reason here is simple, an icon can also be part of other molecules and/or atoms – if we add it to our button as another atom, we can also include that same atom wherever else it may be needed.
As we now know, molecules are composed out of different atoms.
Let’s run through the primary card molecule
card--primary-large that we use here on our Percipio Site, as this is a perfect example of how simple atoms combined can create something a lot more visual.
As you can see, we simply import our macro as a render function and through the simple structure of this card we include the needed atoms where they belong.
But what if we need a change our image atom or card-title atom? Perhaps by changing the ratio or the font-size… no problem! We can edit specific atom that controls the ratio or font-size and all the card titles and image ratios for each and every card has been updated, including other places we need the exact same atoms.
By changing one line of code in one file, we ensure that everywhere we use this element in the project will be updated. This eliminates headaches tracking down multiple templates and the fear of deploying and noticing that you forgot to update an element on a long forgotten template!
As you can see with our button molecule we are starting to add a lot of functionality to a simple link.
As a rule, element queries or data fetches are not done at molecule level – this always happens on the top layer, namely our organisms. This is important, because it separates the atoms and molecules from functional organisms.
Eventually we end up at our more complex organisms, these can combine the molecules and atoms or even other organisms. These bigger organisms will also pass down the variables and the data that we fetch from our Craft CMS entries.
Most of the time, our grids contain cards for an overview page or categorised page where we show a subset of our entries.
This is our grid organism where we expect an array of cards to be given, this will be passed in through another organism which we will go through soon enough. As you can see this organism contains a little loop logic to make sure we can alternate between large and small cards in the grid.
We also make use of the
ignore missing keyword in every include we do which ensures we don’t receive an internal server error in a production environment if there is an include to a non-existing file.
On the includes we also make use of the
with parameter, which can be used to pass variables into the child template. There we expect a content variable into our card molecule that then will read all available options.
In this passed on variable we have all the Craft methods available if we would need some, without putting extra stress on fetching information again and avoiding to add more queries.
Putting it all together _templates
Templates bring together our atoms, molecules and organisms, they’re the final step in building out and displaying operational web application pages.
In our template structure you’ll find a page.twig file as main file that contains our our builder – a simple loop over our block types and based on the type and/or content that the block contains, we load the correct molecules or organisms that are needed to be parsed.
To keep everything simple in the CP each and every section, is it a channel, structure or single, all will point to the exact same entry point namely
This is our base file that will take every entry request, check if an entry actually exists and then based on the entry type ( handle ) will serve up the view we request.
This makes everything easy in the backend, no more doubting or thinking to which template we should actually point.
Since we work with a structure where a single file change impacts every template an atom, molecule or organisms is used, we reduce a lot of maintenance overheads as we don’t need to check in the templates where every button lives.
A simple class change will affect the entire site so the design system stays consistent throughout. Of course the code comments are also a big help if you need to come back to a project after a while
As complex as this might seem, atomic design actually makes it easier for new developer onboarding. Since they can work on smaller items that match their knowledge and experience. If they still need training for more complicated components they can start working on simple atoms and finally work their way up throughout the rest.
Want to know more?
We regularly talk about atomic design at conferences, meet-ups and direct with development agencies.