How do we handle CSS at trivago, what methodologies do we apply, how do we structure our CSS, what do we do for Quality Assurance and automated testing, how do we release our CSS?
A lot of questions which came up during my Project Ironman tour in Europe a few weeks ago. With a series of articles I would like to give some more insights how we approach CSS at trivago.
The first part will cover how we structure our CSS and focusses on how we apply ITCSS to our CSS base.
My article series won’t get into any detail of CSS architecture or methodologies and requires the understanding of some principles. A good resource to catch-up with ITCSS is: http://www.creativebloq.com/web-design/manage-large-css-projects-itcss-101517528
ITCSS at trivago
ITCSS helps us ordering and scaling up our CSS in a maintainable way and to avoid running into those famous specificity issues.
Before going more into detail let’s have first a look at our structure, which is also reflected in our filesystem folder structure (without the numbers).
You can see we didn’t change anything to the proposed methodology.
Let’s have a look what the different layers contain in our case.
The settings layer aka Design System variables
The settings-layer contains all our global Design System variables, so it defines colors, spacings, font-sizes, breakpoints settings and many more. We built all this to be theme able, so all of the variables are declared in the settings layer with `!default`. This provides the opportunity to change some variables inside an application which uses the CSS base without changing the settings files itself, which does make updates easier as your changes won’t be lost if you update the CSS base to a newer version. A good use case for this are media queries. The CSS base might come with some default breakpoint variables but as we all know that is a best practice that breakpoints follow content and not the opposite way, different applications will need different breakpoints.
An example of our spacing definitions:
Mixins and functions in Tools
The tools layer defines all the mixins and functions we utilise in our CSS base. As we recently switched to autoprefixer we have put all mixins which handled vendor prefixes to deprecation, but we still use mixins to handle our RTL layout for platforms like trivago Israel.
The first two layers are the most important ones to rollout our Design System. Every application should at least use these two layers to ensure consistency with our Design System which is mostly achieved through following / applying the global design system values in the settings layer. With Project Ironman we made sure, that every CSS build, e.g. the main builds and the builds which we load on condition make use of those layers. So even the legacy CSS files utilises those variables and mixins.
With ensuring this we were able to get rid of our 50 shades of grey and a lot of other color inconsistencies.
It is worth to mention that the first two layers do not produce any code.
Getting more specific with Generic to Objects
The third layer Generic contains amongst other things normalize.css and the specificity trends upwards with the elements layer which defines some element settings while the fifth layer — the objects layer — starts to become more specific and provides more or less unstyled patterns as reusable objects like wrappers and containers. Also this layer contains all our functional / utility CSS classes like spacing classes. This might differ from a lot of other ITCSS based codebases that move the utility classes to the trumps layer including making them immutable with `!important`. We didn’t put them into this layer yet mainly because of two reasons: Immutability can be in regards to massive A/B testing also something that you don’t want to have and second we didn’t had or very very seldom had issues with immutability so far.
The sixth layer — the components — itself is the first layer which is completely BEMified (Naming conventions will be handled in another article of the series). All components are very encapsulated and of course we try to limit any dependencies between components. So here we have e.g. one component which has all the CSS needed for our search result list item which we call item element.
Overwrites as the end: the trumps
Last but not least the trumps layer, where we add some responsive utilities which we made immutable with `!important`.
Example Base CSS
Let’s have a look how a typical base.css file might look like. The base CSS file is also the global CSS file which is render blocking with page load. So it holds everything which is needed to render the content on page load.
Example Module CSS
Let’s just image we have a component / module which we load on condition and therefore only want to load the component if the user has decided to load it with an interaction, e.g. opening a map or a gallery. So what do we need to include and how did ITCSS help us here? The base styles up to the objects layers are already loaded with the base file and therefore available.
To compile the build for this additional CSS file for this component we need to have the first two layers imported as they define variables and mixins needed and only import the necessary component.
The benefits for us
ITCSS is great. But also it isn’t rocket science. Parts of this structure will be found in every reasonable S(CSS) base like e.g. global variables. We had a similar approach with a separation in foundation, modules and components but the granularity of the ITCSS approach combined with the clear definition as well as all the experiences we made in the past with CSS at large scale has led us to decide to follow the approach without changing naming or reducing the layers by e.g. combining elements & generic. What convinces me is the simplicity of the approach combined with the clear structure focussed on taming the specificity problems which arise by nature on large scale applications.
But it showed up its full power with refactoring our CSS base. Project Ironman was basically a refactoring project where we have included the first five layers. Having components abstracted and not mixed somehow with the base CSS helped us a lot to refactor the base styles and we were be able to tackle the components in a later step.
We owe our thanks to Harry Roberts for continuously inspiring us with his knowledge about CSS architecture.
The next article of this series will be about Naming Conventions, BEM, OOCSS and functional CSS.