120

I have an item class and a compact "modifier" class:

.item { ... }
.item.compact { /* styles to make .item smaller */ }

This is fine. However, I'd like to add a @media query that forces the .item class to be compact when the screen is small enough.

On first thought, this is what I tried to do:

.item { ... }
.item.compact { ... }
@media (max-width: 600px) {
  .item { @extend .item.compact; }
}

But this generates the following error:

You may not @extend an outer selector from within @media. You may only @extend selectors within the same directive.

How would I accomplish this using SASS without having to resort to copy/pasting styles?

cimmanon
  • 67,211
  • 17
  • 165
  • 171
soundly_typed
  • 39,257
  • 5
  • 28
  • 36
  • Fyi, here's an issue that would make the example you gave work correctly: https://github.com/sass/sass/issues/1050 – Ajedi32 Apr 10 '15 at 20:04

6 Answers6

160

The simple answer is: you can't because Sass can't (or won't) compose the selector for it. You can't be inside of a media query and extend something that's outside of a media query. It certainly would be nice if it would simply take a copy of it instead of trying to compose the selectors. But it doesn't so you can't.

Use a mixin

If you have a case where you're going to be reusing a block of code inside and outside of media queries and still want it to be able to extend it, then write both a mixin and an extend class:

@mixin foo {
    // do stuff
}

%foo {
    @include foo;
}

// usage
.foo {
    @extend %foo;
}

@media (min-width: 30em) {
    .bar {
        @include foo;
    }
}

Extend the selector within a media query from the outside

This won't really help your use case, but it is another option:

%foo {
  @media (min-width: 20em) {
    color: red;
  }
}

@media (min-width: 30em) {
  %bar {
    background: yellow;
  }
}

// usage
.foo {
  @extend %foo;
}

.bar {
  @extend %bar;
}

Wait until Sass lifts this restriction (or patch it yourself)

There are a number of ongoing discussions regarding this issue (please don't contribute to these threads unless you have something meaningful to add: the maintainers are already aware that users desire this functionality, it's just a question of how to implement it and what the syntax should be).

cimmanon
  • 67,211
  • 17
  • 165
  • 171
  • @mindeavor This worked for you? You were able to use the extended class in a media query? In Sass 3.2? – Yahreen Jan 17 '14 at 01:44
  • 2
    `%foo` is unnecessary, `.foo` can directly `@include foo`. – phil294 Jul 10 '17 at 18:27
  • For my case, I've just used %placeholder with extend outside media queries. Then, inside the media query, for my selector just added extend %placeholder. Will see if the discussions comes with something handy. Thanks Cimmanon. – keypaul Sep 01 '20 at 11:38
15

For the record, here is how I ended up solving the problem with only duplicating generated styles once:

// This is where the actual compact styles live
@mixin compact-mixin { /* ... */ }

// Include the compact mixin for items that are always compact
.item.compact { @include compact-mixin; }

// Here's the tricky part, due to how SASS handles extending
.item { ... }
// The following needs to be declared AFTER .item, else it'll
// be overridden by .item's NORMAL styles.
@media (max-width: 600px) {
  %compact { @include compact-mixin; }

  // Afterwards we can extend and
  // customize different item compact styles
  .item {
    @extend %compact;
    /* Other styles that override %compact */
  }
  // As shown below, we can extend the compact styles as many
  // times as we want without needing to re-extend
  // the compact mixin, thus avoiding generating duplicate css
  .item-alt {
    @extend %compact;
  }
}
soundly_typed
  • 39,257
  • 5
  • 28
  • 36
2

I believe SASS/SCSS does not support the @extend directive inside of a media query. http://designshack.net/articles/css/sass-and-media-queries-what-you-can-and-cant-do/

You might need to use a mixin instead, though the code bloat needs to be weighed against your objective.

JHogue
  • 206
  • 3
  • 9
  • 1
    A link to a solution is welcome, but please ensure your answer is useful without it: [add context around the link](//meta.stackexchange.com/a/8259) so your fellow users will have some idea what it is and why it’s there, then quote the most relevant part of the page you're linking to in case the target page is unavailable. [Answers that are little more than a link may be deleted.](//stackoverflow.com/help/deleted-answers) – dippas May 12 '18 at 11:44
1

This is the cleanest, partial solution I've found. It takes advantage of @extend where possible and falls back to mixins when inside media queries.

Cross-Media Query @extend Directives in Sass

See the article for full details but the gist is that you call a mixin 'placeholder' that then decides whether to output @extend or an @include.

@include placeholder('clear') {
   clear: both;
   overflow: hidden;
}

.a {
    @include _(clear);
}
.b {
    @include _(clear);
}
.c {
    @include breakpoint(medium) {
      @include _(clear);
   }
}

Ultimately it may not be better than just using mixins, which is currently the accepted answer.

Tom Genoni
  • 151
  • 7
0

I use breakpoints, but it's the same idea:

@mixin bp-small {
    @media only screen and (max-width: 30em) {
        @content;
    }

How to use it:

.sidebar {
    width: 60%;
    float: left;
    @include bp-small {
        width: 100%;
        float: none;
    }
}

There is a text about mixins where you can find out more about this option.

Nesha Zoric
  • 6,218
  • 42
  • 34
-2

Could you restructure?

.compact { //compact-styles }
.item {}
.item.compact { @extend .compact } 

@media (max-width: 600px) {
    .item { @extend .compact; }
}

If I understand the documentation correctly, that should work. I think the reason the way you're trying won't work is that it doesn't see .item.compact when it's parsing the @extend, but that's an uninformed guess, so take that with a truck load of salt! :)

Jason M. Batchelor
  • 2,951
  • 16
  • 25
  • How are you compiling your SASS? Externally, with JS, or with a server-side component of some sort? – Jason M. Batchelor Feb 12 '13 at 20:24
  • It's being compiled through the standard `rails-sass` gem, using SASS v3.2.4 – soundly_typed Feb 12 '13 at 20:30
  • 1
    Looks like the ability to extend inside @media queries is deprecated, and due to be axed in 3.3: http://chriseppstein.github.com/blog/2012/08/23/sass-3-2-is-released/ (read the area that says "Limitations on `@extend` in CSS directives") – Jason M. Batchelor Feb 12 '13 at 20:45
  • If I understood that information correctly, that might be what's causing the issue. I'd be interested to know what you find out! – Jason M. Batchelor Feb 12 '13 at 20:46