We used Atomic Design in production. I highly suggest checking out Brad Frost Atomic design explanation before reading this, especially if you don’t know what Atomic Design is. It’s worth mentioning that even though our implementation was mostly done in react, styled-components and typescript, Atomic can be achieved in any technology.
To explain why we decided on Atomic I would like to introduce our problem and use case.
We had to choose a project structure but most of the blueprints are prepared for whole apps, not just a component library.
Our business requirement wa to divide it into base components like buttons, inputs, containers, and more complex components according to their features. That is not a bad idea but it can be difficult to come up with naming if the library has 100+ components. Browsing such libraries is not the easiest task either so we had to keep that in mind.
Apart from that, organising in by feature would be great but in general we wanted our components to be unaware of any business logic or requirement. Focusing on what the component has to do at one time would mean sacrifices for any future uses in a different scenario.
So we started thinking... Maybe it’s the perfect case to use Atomic Design? 🤔
While working for a while with atomic, I started to notice some trade-offs. As developers, we can only be aware of it, making sure we can use it to our advantage.
We describe the complexity of components using atomic level which tells us how complicated components are used inside.For example, when you see a form component with simple inputs you can be 100% sure that it is molecule level component in the form directory.
You can tell how big components are, without looking into the code. If a component is some kind of button, this will be an atom component.
You may have to split components or group them because you can only use the max of 2 levels below. This forces you to look at another similar part in the design and build a component that you can reuse later.
Because you split your components, you will find yourself using them a lot. Additionally, instead of implementing another similar version, you will add functionality to an existing one.
As I described above, atomic encourage the creation of small components, so testing should be easy if we don’t have to deal with overloaded organisms. Try to keep your test as isolated as your component and test external functions in isolation too.When you want to test styling check visual testing - use snapshots.
We can go bigger and bigger but we can’t go smaller if something is an atom. It has happened to us a few times, that some atoms components started to have a lot of logic (most props and styles related). Going up in hierarchy is not easier either. You will find yourself in a lot of semantic conflicts like molecule importing a molecule. Most of these issues can be resolved using variants but this feels like dodging a problem and not resolving it.
Newcomers will have to understand how to create components that happened to us. Just be patient and review their mistakes (keep an eye on atomic level semantics).
This sometimes feels like doing unnecessary work, because you are just wrapping a few components into a more complex one, but this as described above, keeps your project easier to navigate and development speed is increased each split.
If you have to support N variants (e.g. input and buttons) this can be very hard and will lead to overloaded atoms, and molecules.
We ran into an issue where atoms started to have a lot of props but most of them were related to styling. Implementing another atom (ex. button, list) with the same code doesn’t feel like a great solution. We decide on restyling the components, fromDescriptionList atom we created InlineHorizontalList which was the same component but styles were handled differently.That solved our problem with overloaded styling and we still keep the atomic level.
Moving styles props to variants so basically we get a re-styled version of the component. We can omit some props doing it this way (we omitted most of the props that variant was changing).
More complex components might need a many props to be used by an end-user. In that case moving style and config props to variants, and letting them use pre-configured variants solved the problem. The other thing we can do is handle obvious scenarios like using local state management (React’s useContext) or moving code to external functions (ex. React Hooks) where code is external from the component.
Implementing layouts, without components inside, seems odd at the beginning, because we are used to going straight to page level. Implementing the layout can be done using a few patterns like named childsand props.
The hardest part is actually developing them, because we can’t have real components inside. That’s why we decided to create a small helper for that, which was only used to show layout in isolation. It was just a div element that was always filling the parent, each element had different border color so telling when the slot ends were easy.
We could’ve sacrificed a bit of atomic semantics and adjust atomic to our needs. Starting to separate code to variants much earlier would also be helpful. From the component library side, moving styles to variants is easy, but components are shared between different projects it’s better to be safe than sorry, as requesting changes in external, dependant projects might me tricky.
Atomic is great, but it is far from being a perfect solution. You need specific use case for it. (ex. component library, multiple frontends with sharable components). Sometimes, splitting big components into smaller ones fitting atomic semantics is hard and messing it up hurts while developing. Looking at it from another perspective, this is a great validation tool that you probably did a bad job splitting a design into components.
In my opinion, the biggest advantage is that atomic development feels rapid fast later in the development cycle, although the start can be a bit rough. Creating components without context encourages high reusability (this speeds up later development by a lot). That was very noticeable for me after working with it for a while.
I highly suggest using atomic as a baseline guideline for creating your own design system that will fit your needs.
A rich text element can be used with static or dynamic content. For static content, just drop it into any page and begin editing. For dynamic content, add a rich text field to any collection and then connect a rich text element to that field in the settings panel. Voila!
Headings, paragraphs, blockquotes, figures, images, and figure captions can all be styled after a class is added to the rich text element using the "When inside of" nested selector system.