With SCSS files, I am attempting to make a responsive layout by setting variables in response to an #media query.
I currently have two #media queries, and I was hoping that only one of them would proceed to call the #mixin, with the map defined for the specific situation: mobile or desktop.
My code is :
$page-header-height : 45px; // some dummy defaults
$page-subheader-height: 45px;
$page-footer-height : 50px;
$mobile-varmap : (
"page-header-height" : 50px,
"page-subheader-height": 50px
);
$desktop-varmap : (
"page-header-height" : 90px,
"page-subheader-height": 120px
);
#mixin setvariables($map) {
$page-header-height: map-get($map, "page-header-height") !global;
$page-subheader-height: map-get($map, "page-subheader-height") !global;
$page-footer-height: 50px;
}
$screen-size-mobile: 360px;
$screen-size-tablet: 600px;
#media screen and (min-width:$screen-size-mobile) {
body {
#include setvariables($mobile-varmap);
}
}
#media screen and (min-width:$screen-size-tablet) {
body {
#include setvariables($desktop-varmap);
}
}
div.page-wrapper {
display: grid;
grid-template-areas: 'page-header''page-subheader''page-content''page-footer';
grid-template-columns: auto;
grid-template-rows: $page-header-height $page-subheader-height 1fr $page-footer-height;
max-height: calc(100vh - 55px); // TODO: use variables to calc these
min-height: calc(100vh - 50px);
overflow: none;
}
I had expected that this would lead to $page-header-height, etc, being set according to the matching #media query, but the result is that whichever #media query appears last in the code, determines the values which are produced.
What would I need to do in order to call setvariables() with the varmap that corresponds to the screen size?
NB: I only added the body tags to the #media queries in response to viewing some examples - I'm not sure that they are correcty used or indeed necessary.
Your code is fine, but as i understand it, you expect the variables set via #include setvariables($desktop-varmap); to dynamically respond to device width... which will not happen because the two blocks with the media query do not render anything, the variables are just changed at compile time.
A possible way to build what you want is to have only one configuration map which then could be iterated over like
#mixin setvariables($map) {
$page-header-height: map-get($map, "page-header-height") !global;
$page-subheader-height: map-get($map, "page-subheader-height") !global;
$page-footer-height: 50px !global;
}
$responsive-vars: (
mobile: (
min-width: 360px,
page-header-height: 50px,
page-subheader-height: 50px
),
desktop: (
min-width: 600px,
page-header-height: 90px,
page-subheader-height: 120px
)
);
#each $alias, $map in $responsive-vars {
#media screen and (min-width: map-get($map, 'min-width')) {
#include setvariables($map);
div.page-wrapper {
display: grid;
grid-template-areas: 'page-header''page-subheader''page-content''page-footer';
grid-template-columns: auto;
grid-template-rows: $page-header-height $page-subheader-height 1fr $page-footer-height;
max-height: calc(100vh - 55px); // TODO: use variables to calc these
min-height: calc(100vh - 50px);
overflow: none;
}
}
}
However this is not the most practical approach, since mostly you want to tweak only some values and have other declarations for all devices. Instead, i'd recommend to drop the setvariables() mixin entirely and access the maps directly like:
// https://css-tricks.com/snippets/sass/deep-getset-maps/#deep-get
#function map-deep-get($map, $keys...) {
#each $key in $keys {
$map: map-get($map, $key);
}
#return $map;
}
$responsive-vars: (
mobile: (
min-width: 360px,
page-header-height: 50px,
page-subheader-height: 50px
),
desktop: (
min-width: 600px,
page-header-height: 90px,
page-subheader-height: 120px
)
);
div.page-wrapper {
display: grid;
grid-template-areas: 'page-header''page-subheader''page-content''page-footer';
grid-template-columns: auto;
max-height: calc(100vh - 55px); // TODO: use variables to calc these
min-height: calc(100vh - 50px);
overflow: none;
#media screen and (min-width: map-deep-get($responsive-vars, 'mobile', 'min-width')) {
grid-template-rows:
map-deep-get($responsive-vars, 'mobile', 'page-header-height')
map-deep-get($responsive-vars, 'mobile', 'page-subheader-height')
1fr
map-deep-get($responsive-vars, 'mobile', 'page-footer-height');
}
#media screen and (min-width: map-deep-get($responsive-vars, 'desktop', 'min-width')) {
grid-template-rows:
map-deep-get($responsive-vars, 'desktop', 'page-header-height')
map-deep-get($responsive-vars, 'desktop', 'page-subheader-height')
1fr
map-deep-get($responsive-vars, 'desktop', 'page-footer-height');
}
}
This may look like a lot of code at first glance, but it renders far less css.
Related
Error with scss code, can't style anything in media queries without "!important". What could be wrong and also everytime I save any scss file other than main.scss it causes an error but when I save the main.scss file then it works fine. Any suggestions? I'm using webpack.
.loading {
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
.logo {
width: 80%;
}
}
#media screen and (min-width: 8em) and (orientation: landscape) {
.logo {
width: 60% !important;
}
}
#media screen and (min-width: 767px) {
.logo {
width: 40% !important;
}
}
#media screen and (min-width: 1200px) {
.logo {
width: 20% !important;
}
}
The problem seems to be that you are using .logo inside .loading
try to do it like this
.loading {
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.logo {
width: 80%;
}
This way you should be able to use the media queries without !important.
The nested way has higher complexity than the media query so that's why you had to use !important.
I am facing a problem with the SCSS mixins whereby I have to repeat the same code to get the work done. I have the following SCSS code:
#mixin column($count, $size) { // Can not make any change in this code.
width: getWidth($count, $size);
margin-left: 3%;
margin-right: 3%;
}
#mixin desktop {
#media screen and (min-width: 1024px) {
#content;
}
}
#mixin tablet {
#media screen and (min-width: 768px) and (max-width: 1023px) {
#content;
}
}
#function getWidth($count, $size) {
#return 50; // For example purpose only.
}
.c {
#include desktop {
#include column(10, large);
}
#include desktop {
#include column(11, large);
}
margin: 0 auto;
}
which is compiled into:
.c {
margin: 0 auto;
}
#media screen and (min-width: 1024px) {
.c {
width: 50;
margin-left: 3%;
margin-right: 3%;
}
}
#media screen and (min-width: 1024px) {
.c {
width: 50;
margin-left: 3%;
margin-right: 3%;
}
}
The problem here is that margin is overwritten by the mixins. Or I have to repeat the same code(duplicate code) like below:
.c {
#include desktop {
#include column(10, large);
margin: 0 auto; // Duplicate
}
#include desktop {
#include column(11, large);
margin: 0 auto; // Duplicate
}
margin: 0 auto;
}
to get:
.c {
margin: 0 auto;
}
#media screen and (min-width: 1024px) {
.c {
width: 50;
margin-left: 3%;
margin-right: 3%;
margin: 0 auto;
}
}
#media screen and (min-width: 1024px) {
.c {
width: 50;
margin-left: 3%;
margin-right: 3%;
margin: 0 auto;
}
}
Is there any better way to remove this duplicate code so that I have to write code only once?
Try this to see if the #media query comes after makes you happy.
.c {
#include desktop {
#include column(10, large);
}
#include desktop {
#include column(11, large);
}
& {
margin: 0 auto;
}
}
I've defined two mixins in SASS that allow me to place media queries easily. The problem I'm encountering is that I'm repeating myself frequently across many queries. That is to say, some of my style changes are the same for tablet and mobile breakpoints and others are different. Example:
.foo
float: left
width: 50%
+tablet()
float: none
display: block
width: 100%
+mobile()
float: none
display: block
width: 100%
Where my mixins are defined like this:
=tablet
#media (min-width: #{$mobile-width} + 1) and (max-width: #{$tablet-width})
#content
=mobile
#media (max-width: #{$mobile-width})
#content
I'd love to do something like this:
...
+tablet(), +mobile
float: none
display: block
width: 100%
That doesn't compile, so what is the best way to keep my SASS stylesheets DRY?
You can define mobile and tablet medias as strings and then concatenate these strings.
Scss can be easily converted to sass.
$mobile-width: 320px;
$tablet-width: 760px;
// Media queries as strings
$tablet: "(min-width: #{$mobile-width + 1}) and (max-width: #{$tablet-width})";
$mobile: "(max-width: #{$mobile-width})";
// Converts a list to a string with delimiters between elements
#function join-list($list, $separator: ", ") {
$result-string: "";
#each $item in $list {
// Index of the current item of `$list` list
$index: index($list, $item);
$result-string: $result-string + $item;
// If this is not the last item, adds separator
#if ($index != length($list)) {
$result-string: $result-string + $separator;
}
}
#return $result-string;
}
#mixin get-media($medias...) {
#media #{join-list($medias, " and ")} {
#content;
}
}
.foo {
float: left;
width: 50%;
#include get-media($mobile, $tablet) {
// or #include get-media($mobile) {
// or #include get-media($tablet) {
float: none;
display: block;
width: 100%;
}
}
Css output:
.foo {
float: left;
width: 50%;
}
#media (max-width: 320px) and (min-width: 321px) and (max-width: 760px) {
.foo {
float: none;
display: block;
width: 100%;
}
}
SassMeister demo.
As per #Stefan F's comment, the easiest thing to do in this case was to create a third mixin called (something like): +both() which encapsulated the mobile and tablet sizing. (I'm answering this myself only because he did not and it has been some time.)
Example:
=both
#media (max-width: #{$tablet-width})
#content
Usage:
.foo
float: left
width: 50%
+both()
float: none
display: block
width: 100%
I am wondering if there is a way to write media queries in sass, so I can give a certain style between let's say: 300px to 900px
in css it looks like this
#media only screen and (min-width: 300px) and (max-width: 900px){
}
I know I can write
#media (max-width: 900px)
in sass but how to make that range?
$small: 300px;
$medium: 900px;
.smth {
//some CSS
#media screen and (max-width: $small) {
//do Smth
}
#media screen and (min-width: $medium) {
//do Smth
}
}
Something like this?
This is what I use for a Mixin with sass, it allows me to quickly reference the breakpoint that I want. obviously you can adjust the media query list to suite your project mobile fist etc.
But it will jin multiple queries for you as I believe you're asking for.
$size__site_content_width: 1024px;
/* Media Queries */ Not necessarily correct, edit these at will
$media_queries : (
'mobile' : "only screen and (max-width: 667px)",
'tablet' : "only screen and (min-width: 668px) and (max-width: $size__site_content_width)",
'desktop' : "only screen and (min-width: ($size__site_content_width + 1))",
'retina2' : "only screen and (-webkit-min-device-pixel-ratio: 2) and (min-resolution: 192dpi)",
'retina3' : "only screen and (-webkit-min-device-pixel-ratio: 3) and (min-resolution: 288dpi)",
'landscape' : "screen and (orientation:landscape) ",
'portrait' : "screen and (orientation:portrait) "
);
#mixin for_breakpoint($breakpoints) {
$conditions : ();
#each $breakpoint in $breakpoints {
// If the key exists in the map
$conditions: append(
$conditions,
#{inspect(map-get($media_queries, $breakpoint))},
comma
);
}
#media #{$conditions} {
#content;
}
}
Use it like this in your scss:
#masthead {
background: white;
border-bottom:1px solid #eee;
height: 90px;
padding: 0 20px;
#include for_breakpoint(mobile desktop) {
height:70px;
position:fixed;
width:100%;
top:0;
}
}
Then this will compile to:
#masthead {
background: white;
border-bottom: 1px solid #eee;
height: 90px;
padding: 0 20px;
}
#media only screen and (max-width: 667px), only screen and (min-width: 1025px) {
#masthead {
height: 70px;
position: fixed;
width: 100%;
top: 0;
}
}
Check this out for scss.
https://github.com/Necromancerx/media-queries-scss-mixins
Usage
.container {
#include xs {
background: blue;
}
#include gt-md {
color: green
}
}
Demo: Stackblitz
Based on Angular FlexLayout MediaQueries
$small: 300px;
$medium: 900px;
#media screen and (min-width: $small) and (max-width: $medium) {
//css code
}
So I've got a #mixinthat lets me define my logo width and gives me an automatic height based on the ratio of my logo image.
#mixin m-logoSize($width) {
width: $width;
height: floor($width / 4.72727272727273);
}
I'm trying to find out if there's a way for me to access the $width variable, so I can assign it to a different property like so.
// sass
.logo {
#include m-logoSize(300px);
background-size: $width $height;
}
// translates to this css
.logo {
width: 300px;
height: 63px;
background-size: 300px 63px;
}
Is there any wizardry that makes this possible. Any help is appreciated.
Thanks in advance!
I think you are better off setting global variables for these two values, then you can reach them anywhere.
Example
// Global variables
$logo-width: 0 !default;
$logo-height: floor($logo-width / 4.72727272727273) !default;
// Mixin
#mixin m-logoSize() {
width: $logo-width;
height: $logo-height;
}
// Sass
$logo-width: 300px;
.logo {
#include m-logoSize();
background-size: $logo-width $logo-height;
}
Update
To change the value at different breakpoints you would just set the variable in the scope of the media query.
Responsive Example
.logo {
#media screen and (max-width: 600px) {
$logo-width: 150px;
#include m-logoSize();
background-size: $logo-width $logo-height;
}
#media screen and (min-width: 601px) {
$logo-width: 300px;
#include m-logoSize();
background-size: $logo-width $logo-height;
}
}