CSS: Escape ‘house-of-cards’ with Atomic Templates
Beautiful software often is the product of a symphony of reusable cohesive components. There’s one notorious area of a website where typically that sadly falls a long way behind. We can’t really blame the team, the tooling and best practices around this area has been a little slow in catching up, along with difficulty to test it and the dreaded ‘house-of-cards’ danger it’s commonly the bane of some developers existence. If you haven’t guessed it yet, we’re talking about CSS!!!
Your codebase may suffer from this ‘house-of-cards’ symptom. If so you’d likely have a CSS dumping ground, dump.scss say, where you put all your styles that you don’t know what to do with. At over a thousand lines or more with a mixture of global styles, you’d dare not touch it for fear of messing up a style anywhere on your site. So how could you come to stop adding to this ever-growing beast? Surely there must be a better way?
We can look to the BEM (Block, Element, Modifier) naming convention. It’s a good step, giving us name-spacing to which we can now refactor with greater peace of mind. Though we still want reuse and a way to make this dump.scss redundant. How could we come to write CSS that was refactorable without crazy amounts of manual regression testing? Is there a way we could chip away at this problem incrementally? Is there anyway to make our CSS more ‘plug-able’?
First of we can start with a style-guide. It is a great tool, though initially it might feel like more of an afterthought in the development process, ‘Don’t forget to add it to the style-guide… oh yeah right thanks, almost forgot!’. Ideally we want to get to a state where we started with the style-guide. We could even use it as a design tool, almost like Lego blocks…
To work towards our aim we can employ the Atomic pattern, proposed by Brad Frost. If you haven’t checked it out yet, it might just be the templating system you’ve been needing all this time. Reuse. Self contained. Plugable. As an example take a button that we use on the Thoughtworks.com website:
Here’s the Slim template markup:
a.button href="#{link}" #{text}
With this little atom we can now reuse it to our heart’s content with:
== render_atom 'button', { link: 'some/url', text: 'Read more' }
Boom! Our button.slim and _button.scss along with the sample-data (for the style-guide) all reside alongside one another. It’s now easy to understand what styles are applied, we’re secure and safe in our refactoring of the button if we need and it’s easier and less error-prone for front-end developers reusing in this way. Now we’re talking!
We can now have reusable components, our Lego blocks. If we need to create a new search molecule, it’s just a matter of plugging our button atom in alongside our input text atom and viola! This way dump.scss can eventually become redundant, at which point we’ll pop the champagne. Though over a thousand lines of semi-global styles may take a little time but that’s ok, this way we can add the new elements to the style-guide as we go and bit-by-bit refactor the old ones there too, working away at it incrementally.
Such a pattern can also simplify our functional testing page models. It can give us the same reuse for our page selectors and doing away with large page models that were all doing the same thing but for a different class name here or a slightly different html structure there. Another example from our website is our ‘Author’ molecule, which has a respective functional test model here:
class Author > TestNode
def name
find('.author__title').text
end
def job_role
find('.author__role').text
end
end
When asserting on content within this molecule, in our page-model that uses it we can have a method such as:
def author
all_as('.author', Author)
end
Then to assert on this content in the functional test for the page, we can say something like:
expect(page.authors).to have(1).items
expect(page.authors.name).to eq ‘Sarah’
expect(page.authors.job_role).to eq ‘Senior Consultant’
Looking further ahead you could attempt automated regression testing for CSS for our individual atoms, molecules and organisms with a library like BackstopJS, achieving a refactoring comfort level in CSS the likes we have never seen!
Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Thoughtworks.