Quantcast
Channel: Press Up
Viewing all articles
Browse latest Browse all 101

Don’t Over-@extend Yourself in Sass (Or: There’s a Class for That!)

$
0
0

I recently built the theme for WPShout.com in Sass, the well-loved CSS preprocessor and that we’ve written about a few times. As I dug deeper into the project, I noticed a conceptual issue coming up repeatedly that I’d like to share.

Essentially: I’m worried about the power of the @extend selector to move us away from semantic, easy-to-interpret HTML markup and CSS styling rules. In many cases, I’m finding that @extend solves problems that may be more cleanly addressed through simple classing and appropriately marked-up HTML.

First Issue: Too Many Selectors

One of the most common knocks against Sass is that—to quote an article addressing Sass’s critics—”The extend directive creates a lot of ugly selectors.”

As a short illustration, this Sass:

.called-out { 
	font-weight: bold;
	text-shadow: 1px 1px 0 #333;
}

strong {
	@extend .called-out;
}

.nav-menu-item {
	@extend .called-out;
}

p.highlighted {
	@extend .called-out;
	color: #555;
}

Compiles to this CSS:

.called-out, strong, .nav-menu-item, p.highlighted {
	font-weight: bold;
	text-shadow: 1px 1px 0 #333;
}

p.highlighted {
	color: #555;
}

Now why is that a problem? Picture a stylesheet in which 30 elements extend .called-out. Imagine how ugly the CSS would look: that would be 31 selectors all inheriting the same style—or, if you prefer, “a lot of ugly selectors.”

To make this real, let’s look at an actual CSS styling rule from the WordPress plugin WooCommerce:

.product .single_add_to_cart_button, .cart .button, input.checkout-button.alt.button, .shipping-calculator-form .button, .multistep_step .button, #place_order.button, .single-product .single_add_to_cart_button.button.alt, .woocommerce a.button, .woocommerce button.button, .woocommerce input.button, .woocommerce #respond input#submit, .woocommerce #content input.button, .woocommerce-page a.button, .woocommerce-page button.button, .woocommerce-page input.button, .woocommerce-page #respond input#submit, .woocommerce-page #content input.button {
	background-color: #605f5e;
}

This type of unusable, uninterpretable styling rule needlessly complicates working in WordPress, and requires unreasonable amounts of creativity to work around.

How did WooThemes get there? Well, as it turns out, they did it with indiscriminate nesting, another widely diagnosed problem that results from misusing CSS preprocessors. However, this is a very serviceable real-world example of the same Selector Hell that overuse of @extend can land you in, so we’re going to go with it.

Is There a Systematic Problem with @extend?

Is there something about @extend that just makes me uneasy? Is that uneasiness justified?

Over the past two large Sass projects, I found myself using @extend more and more sparingly. At first, this tendency fit comfortably into the usual “everything in moderation” arguments. But then, as I started to imagine someone else reading my CSS stylesheet—and not the SCSS markup it’s compiled from—I found myself starting to prefer generally frowned-upon practices like argumentless @mixin definitions, just to get away from @extend. Is there something about @extend itself, under any circumstances, that just makes me uneasy? Is that uneasiness justified?

What @extend Does

The following sentence (here’s the original source) nicely encapsulates the essential workings of @extend:

“One class can have all the styles of another class, as well as its own specific styles.”

The question is: Is that a good thing? Let’s look at an example of Sass @extends supposedly done right. The full code is here; we’ll just be examining a key piece of CSS:

h1, article .title, footer .big-deal h2 {
  font-size: 2rem;
  line-height: 1.75;
}

As in our own example with .called-out above, what you see in this compiled CSS is: a seemingly random assortment of arbitrarily marked-up CSS selectors inheriting the same properties.

How @extend Can Obfuscate Meaning

The problem is not just that there are too many selectors; it’s also that there’s no good way for a human to keep track of which elements are affected by which styling rules. article .title behaves just like an h1, and so, randomly, does footer .big-deal h2.

Imagine the site being modified by a future coder who is unfamiliar with both Sass and your own thought processes. That coder has access to two things:

  1. Innocent-looking HTML markup like the following:
    <footer>
    	<div class="big-deal">
    		<h2>Footer</h2>
    	</div>
    </footer>
  2. CSS rules that apply seemingly at random across that markup. What selectors can we expect to resemble an h1? An element with class .title will, but not if you try it in the sidebar. A footer h2 will, but only if it’s inside the .big-deal div. (And an arbitrary number of other rules, jammed together into one giant selector.)

In other words:


Improper use of the @extend directive obfuscates the reasoning behind a site’s styling rules.

The use of @extend risks essentially creating CSS classes that nobody but users of Sass can see.

The HTML markup contains no alert that a footer h2 is going to suddenly resemble (or refuse to resemble) an h1; and the big bucket of selectors of varying length and specificity that get h1-like treatment is going to be very difficult for future readers of the CSS to understand, as well.

Phrased differently: The use of @extend above has essentially created a CSS class that nobody but users of Sass can see. Our “h1-like” class shows up nowhere at all in the HTML markup, and it applies haphazardly across existing CSS selectors following a logic that only the Sass-savvy original site designer (who can trace the @extends) can understand.

Nobody understands what’s going on!

Mark Up Your Markup!

Why make styling rules that reference a separately defined class, when you can just give elements the class itself?

Maybe we can solve this problem by getting back to the roots of HTML/CSS: adding classes to HTML elements we wish to style. Why use @extends to make styling rules that reference a separately defined class, when you can just give elements the class itself and avoid risking confusion?

Let’s look at the following solution to the “h1-like” problem explored above:

HTML:

<footer>
	<div class="big-deal">
		<h2 class="largest-title">Footer</h2>
	</div>
</footer>

CSS:

.largest-title {
	font-size: 2rem;
	line-height: 1.75;
}

Isn’t it better to add information to your HTML markup, and make the CSS the simplest possible reflection of that information?

This solution has one rule, one selector, and many potential HTML elements that inherit that rule. Every element with the .largest-title class gets the rule, and no element without it doesn’t.

I like this an awful lot better than the @extend “invisible, nameless, haphazardly-applied class” solution to the same problem. Assuming you have access to the HTML side of the site you’re styling, isn’t it a lot nicer to add information to your HTML markup, and make the CSS the simplest possible reflection of that information?

If the argument is that it’s easier, or otherwise better, to add classes to the parent div element rather than all its child elements (and pretending that the .big-deal class gives other important properties to the div itself or its children), I still prefer this solution:

HTML:

<footer>
	<div class="big-deal contains-largest-title">
		<h2>Footer</h2>
	</div>
</footer>

CSS:

.largest-title, .contains-largest-title h2 {
	font-size: 2rem;
	line-height: 1.75;
}

This is slightly more complicated than the previous solution, but it still avoids the @extend trap of blind inheritance. The styling rule now only applies to things that are of class .largest-title, or are inside elements of class .contains-largest-title (and are h2s). There’s no risk that, for example, article .title gets a style that nothing in the HTML suggests it should get for reasons that are buried in a paragraph-long selection rule.

Similarly, if WooCommerce could add the appropriate classes on the HTML side, wouldn’t the following have been so much nicer?

.wc-button {
	background-color: #605f5e;
}

If you want something to look like a WooCommerce button, you give it the .wc-button class. Then you can go and see in your markup what’s a button. The whole thing works, without any surprises.

What Do You Think?

This post has been an attempt to voice my suspicion of the @extend directive in Sass, and its penchant for creating logics that only Sass users can understand.

What do you think? Is @extend just a weird idea? Are there sensible ways to use it I’ve omitted above? Should they deprecate @extend and send me a stylish Sass jacket for being so thoughtful? (Side note: I need a new jacket.) I’d love to hear from you in the comments below!

The post Don’t Over-@extend Yourself in Sass (Or: There’s a Class for That!) appeared first on Press Up.


Viewing all articles
Browse latest Browse all 101

Trending Articles