Your stylesheets need methodology!

Pierre Allard
Akeneo Labs
Published in
7 min readAug 28, 2017

Akeneo, as a lot of web start-ups, has first recruited full stack developers, to be able to deliver quickly as much business value as possible. A full stack developer is somebody able to work on every layer, from server to browser. Behind this, each full stack developer is more attracted by a specific part of a web application (e.g. front-end, back-end, system, database) The majority of the features or proof of concepts are assigned to developers regarding their development preferences. The issue is that the smallest parts of the application are often left aside.

A little bit of context

In this article, we will tell a story about one of the darkest and often the most forgotten part of web applications: the stylesheets. Let’s begin by a little history of Akeneo PIM style development. Akeneo PIM is based on Oro Platform. Oro was based on a custom Bootstrap2, with an overlay of Oro styles (contained in 1 single file). We added another layer of Akeneo style for the first version of Akeneo PIM. Fun fact: at a certain time, the Bootstrap LESS files were compiled into a bootstrap.css file, this file was renamed into bootstrap.less to be able to use variables, but the original Bootstrap LESS files were kept. We added some custom code directly in these files (original ones or compiled one!). So the entire web application style was declared in Bootstrap original, Bootstrap custom, Akeneo PIM and Oro files.

In Akeneo PIM 1.6, when developers wanted to add a new style rule, it was completely impossible to know if we had to add a new rule (is it component-specific?), update an existing rule (what are the impacts on the rest of the application?), or override an existing rule. Generally, this last solution was chosen to avoid breaking existing rules. It leads to rules declarations such as #element span.myClass .bootstrapClass.another_class, or usage of !important to make sure the previous rule will be overridden.
This brings us to the state of the broken window theory: when a developer updates a dirty file, adding a little dirt on top doesn’t look like a big deal.

When we started working on the 1.7, we knew that we would have to do a huge update in the following years in the front part, and we had time to think and prepare the construction site. The decision was taken to entirely refactor the stylesheets of our application, in order to be:

  • more maintainable (easy to find, understandable, and allows us to update rules easily)
  • more reusable (to avoid code duplication)
  • smaller (the actual CSS generated file contained a lot of useless and overriden rules)
  • better organized (to avoid a huge LESS file with random rules inside)
  • consistent (with the same naming convention everywhere)

OK, but what’s the solution?

We carried out a lot of studies on the existing methodologies for style. Here is a non-exhaustive list: AMCSS, Atomic design, BEM, ITCSS, montageJS, OOCSS, SMACSS, SuitCSS… I’ll let you open all these links to see the diversity of the solutions. During the research phase, we found an interesting methodology: Atomic OOBEMITSCSS. Behind this funny name there is a combination of several methodologies (here, Atomic Design, Oriented Object, BEM, Inverted Triangle and SCSS). This interesting case study led us to understand that we didn’t need to follow a unique and specific methodology, but we could pick ideas from several ones and adapt it to our needs. We finally chose to start working with the BEM methodology because it fits our primary needs, and we could adapt it thanks to other solutions to make it match all of our requirements.

BEM for Block, Element, Modifier

The main idea of this methodology is to organize the stylesheets by Blocks (called in other methodologies modules, or components). A block contains several Elements. Each block or element can have Modifiers (sort of variations). From this simple idea comes a lot of good properties matching our primary needs.

First, each block is described in a single file containing its own rules, its elements and modifiers. It implies that you can easily find what you want to update just by reading a single file, and you know where to add your new rules. Then, the names of the blocks, elements and modifiers have to be thought through. Indeed, to be reusable, we have to name our blocks with the thing it describes (e.g. Action Button) instead of the context where it is used (e.g. Toolbar Page Selector).

Second, BEM defines a naming convention. A block is written .aBlock, an element is written.aBlock__anElement, and the modifiers are written .aBlock--aModifier or .ABlock__anElement--aModifier. It looks very verbose at first sight, but sometimes verbosity does not prohibit legibility. We found a lot of variations for this naming convention (from Google, montageJS, AlsaCreations, SystematicCSS, SuitCSS…). We finally chose our proper naming convention: .AknBlock, .AknBlock-anElement ,.AknBlock--aModifierand .AknBlock-anElement--aModifier. If you look closely, you can see the Akn prefix (for AKeNeo) for each block name: it will help us to refactor, then to identify what is our custom code and what belongs to external libraries.

Third, BEM prevents the usage of #identifiers, tag selectors (e.g. span or div ), !important annotations, and usage of other blocks inside a block. This makes a developer’s work more complicated, but we’re convinced it forces us to better think and organize our ideas before writing code. It brings a lot of good properties and good practices, and prevents the bad deprecated concepts we had 10 years ago with the non mature webdesign methodologies.

Let’s start!

The first idea was to start from a blank page; and it was quickly aborted. We decided to work in cycles of development: work block per block, and ensure after each cycle we have no style regression or no functional regression.
One of the biggest parts of the refactoring was, as explained above, to figure out what will be a module, and what will its name be.

We began with the most obvious and repetitive ones: Button, ButtonList, Grid, Flash, Breadcrumb… Describing a new BEM module can be done step by step:

  • Most importantly, find a relevant and reusable name.
  • Implement the default style of your module. For example, when you design a button, the default style will be a simple button without icon, and with a gray background.
  • Implement the element and modifiers of your module. In the case of a button design, an element will be the icon or dropdown, and modifiers will be the button types (apply, important, disabled…). At this point, if you have too many elements (by our little experience, 10 can be good candidate), you may want to split into several modules?
  • Remove all the old related code. That’s a crucial step to show the progress of the refactoring, but it has to be done step by step because some old rules were used in too many places.
  • Do manual checks to ensure there are no design regressions and run automatic tests (with continuous integration) to spot functional regressions.

To have an overview of what still has to be done, we declared some specific rules, declared in a global file, to display with a red border every non-BEM component.

Finally, the last month of work was to fix all the little glitches. We organized an internal contest for all employee to find glitches on the application… More than 100 glitches were found and patched within that month!

Akeneo PIM 1.6 vs Akeneo PIM 1.7. Not so many visual differences, no?

Key results

Using that for BEM Block creation, the 1.7 was released with 52 blocks. All the previous style code was removed.

In 1.6, the compiled CSS was 16588 lines long (3508 rules for 394kb!). In this file, rules were distributed as follows: ~4200 from external libraries, ~5100 from Bootstrap (as a reminder, compiled twice!), ~7200 from Akeneo PIM.

In 1.7, the compiled CSS was 7987 lines long (1777 rules for 185kb): still ~4200 from external libraries, ~300 from Bootstrap (we wanted to remove it completely but we kept some useful modules like modal and tabs), and 3400 lines from BEM blocks. We defined 52 blocks and 520 rules.

All the used libraries (15 actually) were moved in specific folders, and we overrode them only when it was necessary, in non-BEM files. Every block is contained in specific folders (page blocks or components blocks).

Another funny statistic is the lack of coherence we had in the application without realizing it. In 1.6, we found 26 different font sizes and -brace yourself- 97 grays. Now, we have only 7 font sizes and 3 grays. In the 1.5, we changed the main purple color of the application; we had to change it manually in~200 places. Now, a color change implies 1 line update. Indeed, every block now uses a strict list of variables contained in the same file.

Conclusion

Besides the file size decreasing, this refactoring of our stylesheet has led to a lot of cool improvements for the Akeneo developers, the designers and the external contributors.

The application of BEM principles in such a big application was challenging, but now every element of this web application is right where it should be, they are all consistent. The designers know the blocks we use and we speak the same language. Every developer has access to a complete versioned style guide to find right blocks to use and see every modifiers and thus avoid duplication.

So, what’s next? The front-end refactoring is a never-ending process, and we’re currently thinking about a new way to better link BEM blocks with JavaScript modules. Let’s dream about a front-end application only composed of reusable elements, defined by their functional behavior and their style… Stay tuned!

--

--