Contextual CSS and the problems therein

In the past I’ve worked on some projects in which I’ve adopted 3rd party SCSS and had to expand it. In cases where an effort has been made to apply styles contextually, where class styles are dependant on parent classes, I’ve found styling can be quite painful.

Contextual styles seem fantastic at a glance. They allow you to keep clean, easy to read HTML because elements can look like this:

<article class="blog post">
	<h1 class="title">
		// Title
	</h1>

	<p class="caption">
		// Blog excerpt
	</p>

	<a class="primary button" href="/blog/post">
		// Read More
	</a>
</article>

Which in turn, makes use of SCSS like this:

.blog.post {
	.title {
		// styles
	}

	.caption {
		//styles
	}

	.button {
		// styles
	}

	.primary {
		&.button {
			// styles
		}
	}
}

Seems simple enough. The HTML is nice to look at and to read, especially when compared to using naming methodologies such as BEM, but as your components begin to grow in complexity, issues start to arise.

One major drawback of this approach that I often see ignored, is the difficulty that arises with keeping track of where you are within the nested selectors. Take this for example:

.blog {
	// styles

	@include media-query(medium) {
		// styles
	}

	@include media-query(large) {
		// styles
	}

	&.has-related-content {
		// styles

		@include media-query(large) {
			// styles
		}
	}

	.title {
		// styles
	}

	.description {
		// styles

		@include media-query(medium) {
			// styles
		}
		
		&.table {
			// styles
		}
	}

	.read-more {
		// styles

		&.right {
			// styles
		}
	}
}

Contextual styling, if we are not careful, can result in style nesting so tall it challenges some of the world’s skyscrapers.

1:1 Scale comparison of skyscraper and nested SCSS selectors

When you’re scanning through a bunch of SCSS that you need to edit, it can be difficult to keep track of which rule your target is nested inside. Style declarations like the one above are tiny compared to some of those I’ve come across, and it’s problematic when hunting the classes you need. In the example above, .right could be used in several places across the project. It’s certainly nice to be able to read sentences in your HTML elements, such as <article class="blog post related-to something-else">, but would it be more appropriate to reconsider how you’re adding classes to your elements? Shameless plug: read Un-inverting the triangle: A proposal for HTML class declarations for a proposal related to this.

This problem is compounded if you aren’t looking at a well structured project. I’ve worked on a project which came with one SCSS stylesheet, and making sure that I was looking at the .title within the correct parent was an absolute nightmare. My monitor only has so much vertical space!

Another issue that I’ve found is searching for the correct instance of whichever generic element name you need. For example, .title will likely live in at least 10 places in your project. If you keep searching your project directory for instances of that class name, only to find that you then have to scroll up to be sure you’re looking at the right one, you likely aren’t going to be enjoying yourself.

Another issue with a context-based CSS approach is that of sub-parents. Take this:

.article {
	// styles

	.title {
		// styles
	}

	.share-links {
		// styles
	}

	.header {
		// styles

		.title {
			// styles	
		}

		.share-links {
			// styles
		}
	}

	.footer {
		// styles
		
		.title {
			// styles
		}

		.share-links {
			// styles
		}
	}
}

In this example, there are three places in which you might need to edit the .title styles. As a component gets even more complex; with mixins, media queries and other elements added to the rule, you will find, once again that it begins to take more and more time to find the instance of .title you need to edit.

It’s not all bad though

Despite the frustrations I’ve found, I still wouldn’t argue that you should never use context-based styles at all. One that I end up using in almost every project is some variation of:

.nav__link {
	// styles

	&:not(:last-of-type) {
		margin-right: 1rem;
	}
}

In this case it makes complete sense to me. I’m not making sweeping changes based on context. Instead, it’s just a small change, that’s unlikely to cause much harm.

Alternatively, you might have sections that alternate in terms of both background colour and font colour depending on whether their n position is odd or even, or you may be introducing a "night mode" to your site, which works by toggling a .dark class on the body element. In such a case, it can absolutely be worth approaching the task by styling components based on their context.

Another example where it might be appropriate is to highlight special instances of a component. For example, if you have a portfolio site, you might want to highlight specific projects that you’ve worked on with different styling. In this case it doesn’t seem entirely inappropriate to nest something like a .featured class in your .portfolio rule to add any special styles that you want to apply.

Personally I’m a big fan of the BEM approach to naming classes, but I won’t deny that it’s ugly. If you have an element with 5 or more BEM classes, it quickly becomes difficult to read in your HTML; but it does mean you’re much more likely to find the rules you’re trying to apply. A search for .featured-slug__button--ghost is unlikely to yield more than one result and so I can be quite confident that I’m looking in the right place to apply my styles.

So what can I take away?

I’m not writing this to declare that context based CSS is wrong. I think there’s certainly a place for it. I’m also sure that there will be benefits I’m unaware of because of my lack of experience with the approach. You’ve probably noticed that the issues I’ve personally encountered with contextual CSS can be prevented by a well structured project. For example, the project I worked on with one several-thousand line long SCSS file was quickly refactored and split into several stylesheets just to improve readability. I would still consider tall style declarations to be a drawback of using a context based approach to writing CSS though, as even if you have a separate SCSS file for every component on your project, gradual increases in complexity will lead to it being more difficult to nest the style in the correct place.

At the end of the day, just like with any problem, it's up to you to make a judgement as to an appropriate way to tackle the issue. This article just outlines the issues I've had with this method.

Twitposting

  • @Xhenxhefil People who defend the show after what they've done to Jaime don't deserve the right to vote

  • What started out as a quick fact check turned into a 1.5 hour investigation into liquid variable scoping. Where has my night gone?

  • @Xhenxhefil the best thing is you know they're going to "like" every single one they listen to, then never listen to it again