The ext(); mixin sets a selector budget and controls duplicates on Sass' @extend directive.

If you unserstand how Sass' @extend works, jump to getting started.

Basically, this Sass directive lets you join selectors so they share the same piece of CSS instead of coping the whole thing on each of them. And with placeholders, we can have chunks or even entire patterns of CSS without relating them with any specific selector.

// Sass

// This is a placeholder. If not extended ever, it would not be outputted in the compiled CSS
	color: #000;

	@extend .foo1;

	@extend .foo1;
	background: red;

Once you compile this, it would get you this:

// CSS Output

	color: #000;	

	background: red;

If you want a full explanation about how @extend and placeholders work, I'd recommend this article.

The problem many developers have faced when using @extend is that, since you can extend as much as you want and have multiple extensions in the same selector, you can get bloats of selectors like so:

.foo_parent .foo,
.foo_parent .foo h2,
.heading span,
.foo--mod .foo__child,
	color: #000;

As you can see, in this scenario it's not worth it to extend such a small block of CSS, because the conjunted selectors take more weight than the saved by the lines not copied on every selector.

So we need to be smart about how we use @extend and start doing things like setting a selector budget. But currently there is no way of doing that, is it? We can't just go to the CSS and count the selectors by hand!

Well, ext(); will do this and much more for you. It'll give you the functionality and control you would expect from @extend. This is how it looks like:

$ext-budget: (
	'total': 10

%foo{ color: #000; }

	@include ext(foo);

Now thanks to the use of this plugin we can do way more stuff than if we were using the regular @extend directive, like:

Getting started

Using ext(); is really straightforward. The only dependency is Sass 3.4+. First of all, you will need to install it with Bower:

bower install ext --save
If you don't use Bower, you can download _ext.scss. Once you got it, import it into your project.

Then, on your defaults file (_defaults.scss, _vars.scss, etc.) you need to set up your selector budget ($ext-budget).

$ext-budget: (
	'total': 15

Here you must set the maximun number of selectors you want any placeholder to be limited to be extended. And you are ready to go! Start extending your placeholders like so:

	color: #000;

	@include ext(foo);

By default (you can change the options though), ext(); will throw you a warning (using Sass @warn) when:

NOTE: Please, bear in mind that extending classes is not supported since it goes against the idea of this mixin. You should only use placeholders.

Targeted budgets

Although total is the initial budget, you may want to have different budgets for different placeholders, like headings, which easily have 6 selectors as a minimun. Therefore can add more to $ext-budget like so:

$ext-budget: (
	'total': 15,
	'headings': 12,
	'forms': 10

By default, ext(); will always count an extension in the total budget to get a generic idea all across the site. If you don't want this to happen, just remove it from the budget map.

If you want an extension to count as part of another budget too, simply add it as another parameter when you extend the placeholder.

@include ext(foo, headings);

These budgets will also be reflected in the debug.

FLAGS: Although it is not recommended for this mixin, you can add the !optional flag as a third parameter.

@include ext(foo, headings, !optional);

If you want a summary of all the budgets and their related placeholders and use, go on and enable the debug mode.

Debug mode

You can enable the visual, fantastic debug mode by adding the following after all your @import:

@include ext-debug();

Using body:after you will get a complete report, similar to this.

# ext() v1.0

All good! - 0 of 14 total (15, 1/210 ratio)

⚠ HEY! - 2 of 2 heading (9, 20/18 ratio)
10/9 - %heading => .heading for: 'h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4',
10/9 - %header__heading => .header__heading for: 'h1, h2, h3,...

By default, all the budgets summaries and their exceeded placeholders are shown. You can filter this by budget key in the ext-debug() parameter to limit the results to it.

@include ext-debug(headings);

You can also change the options to see all the placeholders even if they are under the budget, or even limit the budget summaries shown to the ones that have budget-exceeding placeholders.


If you want to change ext(); behaviour you can play with the options. Just include them in your defaults file:

$ext-options: (
  'strict': false,
  'warn-over': true,
  'warn-duplicates': true,
  'over-only': true,
  'show-all': true

There is no need yo use them all or to keep them in that same order. Changing these values would affect in:

If you want to contribute to this, please open an issue or send me a pull request.