Lesson 4: Variations
In the context of CSS and front-end development, variations (sometimes referred to as mutations), are the ways in which elements change slightly depending on their context. They’re how we adapt UI to behave in different ways.
In this lesson we’re going to cover:
- What constitutes a variation.
- Extracting variations from existing styles.
- Strategies for managing variations.
What constitutes a variation? #
A variation is comprised of three main ingredients:
- A base set of styles that define how an element looks by default.
- Variation styles which override a small subset of the base styles.
- A “hook” that causes variation styles to kick in.
Ancestor-controlled variations #
Now we know how to identify variations, let’s start looking at a few strategies for sensibly managing them.
Say we want to display two buttons on a page; we want them to look the same, except the second one should be red. If we decided to take an “ancestor-controlled” approach, we might end up with something like this:
We have a ruleset that defines the default styles of a .button
, and also a ruleset which changes the background colour of .button
elements when they’re nested inside a .container
. The ancestor element acts as the hook that controls the variation in elements nested inside it.
This method is commonly employed to manage variations at a page level. For example, adding a .home-page
class to the <body>
element, and using it as the ancestor like in the above example could be used to vary elements specifically on the home page.
Advantages #
Lets look at some of the strengths of this strategy:
- It’s quite intuitive and simple to understand.
- It’s usually easy to introduce into a small codebase.
- Often you won’t have to edit your HTML to introduce ancestor-controlled variations. You just need to pick an appropriate ancestor to control the variation.
Disadvantages #
Although quite simple, this solution has a few drawbacks:
- It couples our CSS to the structure of our HTML. In other words, if we change how our HTML is laid out there’s a chance that the variation styles might break. Using the above
.container .button
example, if we pulled the.button
element out of the.container
ancestor element, it suddenly doesn’t work. As the author of the styles, we may be aware of the impact that that would have, but a teammate who comes to change the HTML might not do. - Every time we add an ancestor class to a selector, it increases it’s specificity. Like we established in Lesson 1, minimising specificity and using load order to control overrides is more likely to result in a healthy CSS codebase in the long-term.
- It makes our UI components less modular and well-encapsulated. If we’re looking to create reusable, self-contained UI components, then it’s not a great idea to hand over control to ancestor elements. Ideally, a component should control the styles that affects it, not the context it’s placed in.
- Styles become less portable. We’re not going to be able place elements under a different ancestor and preserve the variations styles all that easily doing things this way.
- We may end up styling things we didn’t mean to. For example, the above
.container .button
selector will style any and all.button
elements placed inside.container
. If.container
has many descendants, then there’s a greater chance of another.button
element being affected. That might be a good thing and something we’re deliberately trying to do, or it might be something we end up doing accidentally which breaks our design.
Class-controlled variations #
Class-controlled variations are where we add “modifier” or “mutator” classes to our elements to trigger the visual variations.
Advantages #
- You have to deliberately add the class to trigger the variation. This means it’s very difficult to accidentally style elements with the variation styles, since you need to take the step of adding it to the markup.
- TODO: add more advantages
Disadvantages #
- TODO: add more disadvantages
Extracting variations #
This solution does the job, however it isn’t ideal as most of the styles are duplicated across the two classes. On closer inspection, the only thing that varies between them is their background-color
rules.
This is where variation styles can help:
- First, we need to decide what our element should look like by default. Let’s say, in this scenario, our button should be white.
- Double check the naming of your elements still makes sense. Let’s rename
.white-button
to a more generic.button
class, as it’s no longer the case the button will always be white. - Then we extract the subset of rules that vary into their own ruleset. We’ll grab the
background-color: red;
and put it in a new.red-variation
class. - We then hook the new variation onto the elements that we’d like to vary. We change the second button’s
.red-button
class over to the default.button
class and also add the.red-variation
class to hook up the variation. - Next, we clean up any redundant styles that aren’t needed anymore. The
.red-button
class can now be deleted. Generally, it’s important to be careful here; make sure you’ve moved over all impacted elements over to your variation class before doing this.
Voila! We’ve taken two rulesets, which were almost identical, extracted what varied between them, and DRY-ed up our styles considerably. What’s more, we’ve made the button styles more generic, whilst maintaining the flexibility to customise the background colour should we wish.
Lessons
- Lesson 1: Specificity
- Lesson 2: Scope
- Lesson 3: Naming
- Lesson 4: Variations ← You're here
- Lesson: BEM Coming Soon
- Lesson: SCSS Coming Soon
-
Lesson:
position
Deep Dive Coming Soon - Lesson: Reusability Coming Soon
- Lesson: Units Coming Soon
- Lesson: Media Queries Coming Soon
-
Lesson:
display
Deep Dive Coming Soon - Lesson: Flexbox Coming Soon
- Lesson: Grid Coming Soon
- Lesson: Inheritance Coming Soon
- Lesson: Layout vs Aesthetics Coming Soon
- Lesson: Fixing Bad CSS Coming Soon
- Lesson: Composability Coming Soon
- Lesson: File Organisation Coming Soon
- Lesson: Accessibility Coming Soon