Extending selectors from within media queries with Sass - css

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?

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).
https://github.com/sass/sass/issues/1050
https://github.com/sass/sass/issues/456

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;
}
}

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.

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.

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.

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! :)

Related

Is there a way to apply styles to a selector and a media query?

I need to apply some styles to a div for printing. I can use a media query #media print {} to accomplish this easily. But I also sometimes need to apply the same styles on the browser itself, before the print dialog is opened. I'm trying to do this by adding a div .print-view to the page. But can't find a way to do it without code duplication.
I've tried this, but it's invalid css:
.print-view, #media print {
.grey-background {
background-color: white;
}
}
I've also tried this (scss), but it causes an error #extend may only be used within style rules.
.print-view {
.grey-background {
background-color: white;
}
}
#media print {
#extend .print-view;
}
Is there some other way for me to accomplish this?
Edit: changed sample code to more accurately reflect what I'm trying to do
Found the answer here. #include can be used to add a mixin into both a media query and a normal selector.
#mixin print {
.grey-background {
background-color: white;
}
}
.print-view {
#include print;
}
#media print {
#include print;
}
This compiles to the following css:
.print-view .grey-background {
background-color: white;
}
#media print {
.grey-background {
background-color: white;
}
}

Sass apply css to media query and class in same time

I use Sass and I want to apply some style with two conditions
first if media query < desktop
second if the container have class "affiliations"
How can I wrote it without duplicate the style
I try the following but its not work
#include media ('<desktop'),.affiliations {
.search
{
color:red;
.icon{
color: blue;
}.........etc
}
}
What you are trying to do can't be done with only sass, usually you would structure your code like this
.someclass {
color: deepskyblue;
#media (max-width: 1023) {
.affiliations & {
color: deeppink;
}
}
}

less :extend() with media queries

I am trying to extend a simple class
.positionAbsolute {
position: absolute;
}
My issue here is that I can extend it like:
#something:extend(.positionAbsolute) {
something else
}
But from inside the media query itself, If i try from outside no rule is extended at all.
Is this the normal behaviour?, why is doing that?, In such case I will have to create like four equal classes to extend in each media query case, is there some workaround?
Thank you
I'm not sure I understand it right (it's always hard to guess w/o seeing an exact CSS output you need to achieve), but it looks like you need something like:
.positionAbsolute {
position: absolute;
}
#something:extend(.positionAbsolute) {
#media only screen and (min-width: 1024px) {
something: else;
}
}
The other way around is:
.positionAbsolute {
position: absolute;
}
#class-1,
#class-2 {
&:extend(.positionAbsolute);
}
#media only screen and (min-width: 1024px) {
#class-1 {
something: else;
}
#class-2 {
something-even: more else;
}
}
That way you will have to repeat selectors instead of media queries.
I was trying to do this, and was quite disappointed I couldn't put the extend within a media query. Fortunately I was able to use a mixin for what I wanted. This may work for some situations. I can't really tell what you're actually doing with your case.
.centerVertically()
{
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
margin-right: -0.25em;
border: 2px solid red;
.content
{
display: inline-block;
vertical-align: middle;
}
}
used in media query
#media screen and (min-width: #break_tablet)
{
/* doesn't work
&:before:extend(.centerVertically_wrapper){}
.content:extend(.centerVertically_child){}
*/
.centerVertically();
The best I came up with I turned into a GIST here. Basically, I created an externalized reference mixin for the query and imported all the possible contexts into it. LESS sees through the query when you are referencing a class to extend but it will export it.
Reference:
.narrow {
#media screen and (max-aspect-ratio: 1/1) {
#import "position";
#import "z";
}
}
Export:
#import (reference) "narrow";
.style-1:extend(.position .abs, .narrow .position .rel) {
}
.style-2:extend(.narrow .position .rel) {
}
.style-3:extend(.narrow .z .index-1) {
}

Combine extend and mixin in with same rules

Okey!
I have couple of extends in sass like
%heading
%paragraph
%gutter
and so on...
I want to reuse thouse in media queries, but that doesnt work. I know that.
Then i came up with the idea to have all my extends as mixins too. So when i want them in a media query i simply use mixin. for example
.my-widget {
#extend %gutter;
#media.... {
#include gutter-other;
}
}
and because i dont want to write all my rules again. How do i write my sass then?
I tried
%my-extend, #mixin my-extend {
...
}
but that didnt work.
Any ideas how to work with this?
No, you can't combine them that way. You'll have to write a mixin that is invoked by your extend class and anything inside of a media query.
#mixin my-extend {
background: yellow;
}
%my-extend {
#include my-extend;
}
.foo {
#extend %my-extend;
}
.bar {
#extend %my-extend;
}
.baz {
#media (min-width: 30em) {
#include my-extend;
}
}
Output:
.foo, .bar {
background: yellow;
}
#media (min-width: 30em) {
.baz {
background: yellow;
}
}

Media Queries - In between two widths

I'm trying to use CSS3 media queries to make a class that only appears when the width is greater than 400px and less than 900px. I know this is probably extremely simple and I am missing something obvious, but I can't figure it out. What I have come up with is the below code, appreciate any help.
#media (max-width:400px) and (min-width:900px) {
.class {
display: none;
}
}
You need to switch your values:
/* No less than 400px, no greater than 900px */
#media (min-width:400px) and (max-width:900px) {
.foo {
display:none;
}
}​
Demo: http://jsfiddle.net/xf6gA/ (using background color, so it's easier to confirm)
#Jonathan Sampson i think your solution is wrong if you use multiple #media.
You should use (min-width first):
#media screen and (min-width:400px) and (max-width:900px){
...
}
just wanted to leave my .scss example here, I think its kinda best practice, especially I think if you do customization its nice to set the width only once! It is not clever to apply it everywhere, you will increase the human factor exponentially.
Im looking forward for your feedback!
// Set your parameters
$widthSmall: 768px;
$widthMedium: 992px;
// Prepare your "function"
#mixin in-between {
#media (min-width:$widthSmall) and (max-width:$widthMedium) {
#content;
}
}
// Apply your "function"
main {
#include in-between {
//Do something between two media queries
padding-bottom: 20px;
}
}
.class {
display: none;
}
#media (min-width:400px) and (max-width:900px) {
.class {
display: block; /* just an example display property */
}
}

Resources