Strange SASS output when using extends

85 views Asked by At

I have the following SASS files

_android.scss:

$width: 111px;
@import 'child';

.android .child {
    @extend %child;
}

_ios.scss:

$width: 999px;
@import 'child';

.ios .child {
    @extend %child;
}

_child.scss:

%child {
    width: $width;
}

app.scss:

@import 'ios';
@import 'android';

Now when I run sass I get:

$> sass app.scss
.ios .child, .android .child {
   width: 999px; }

.ios .child, .android .child {
   width: 111px; }

Not really what I expected, which is

.ios .child {
    width: 999px;
}

.android .child {
    width: 111px;
}

What is it what I do wrong here ?

1

There are 1 answers

0
Toni Leigh On BEST ANSWER

It's because @extends are processed as they are first found in the sass, with all the selectors in which they are used grouped together at that point (@extends is a bit greedy) and you're including the @extends placeholder twice.

Let's step through what happens:

  1. ios.sass is loaded, this sets $width and includes %child
  2. sass encounters the placeholder because of the includes, so it looks through the code, get's all the instances of that placeholder, groups the selectors and uses the current value of width.
  3. next android.sass is loaded, $width is set to a new value and %child included again.
  4. sass repeats step 2. Each time an @extends is encountered all instances of it are grouped and output with the values in the placeholder.

This is why when you look at the output you see the both selectors grouped and this group repeated twice with each of the $width values defined.

What to do to fix it

A mixin is best here. When a mixin is encountered it is evaluated on the spot and the resulting property set is injected into the selector.

@mixin width($width-in: 1000px) {
    width: $width-in;
}

.ios .child {
    @include width(111px);
}

.android .child {
    @include width(999px);
}

Now, let's step through the mixin example

  1. The mixin is loaded, sass knows about it, but doesn't do anything with it - think of it like a function waiting to be called
  2. ios.sass is loaded and a selector is defined
  3. sass sees the call to the mixin with a parameter, so it takes the parameter, uses the mixin and injects the returned width value into the selector
  4. android.sass is loaded ...
  5. sass sees another call to the mixin and repeats the process, injecting a new value of width into the new selector.

PS in my mixin definition I use a default value of 1000px, which would be used if @include width(); was called.