I have been designing with CSS for years, but I am only just now learning how to use SASS. This is very much a beginner question, so please bear with me.
The reason I started looking into SASS was because I wanted to develop a responsive web design, but was hoping there was a better way to do it than manually make different style sheets for every screen size.
I believe that in principle, a CSS preprossesor like SASS should help with this issue... but at the moment I don't understand how.
If I have a div with the id #squishable, and on large screens I want it to have a width of 1000 pixels, and on small screens a width of 100 pixels, but a red background in both cases, I think I would do something like this:
$color = red;
#squishable {
backgorundcolor: $color;
}
#media only screen
and (min-width : 321px) {
#squishable {
width:100px;
}
}
#media only screen
and (min-width : 1824px) {
#squishable {
width:1000px;
}
}
However, I'm not sure this approach is in much of an advantage over just using CSS. Maybe as an example, it's too simplistic. But, in any case, I don't see exactly what I would be doing differently with SASS to make it easier to code for a responsive web design. Is this all there is to it?
Can someone help me get over this conceptual hurdle and let me know how I take advantage of SASS to create elements that are responsive? Hopefully with a simple example similar to what I've got above?
You can absolutely improve your workflow further!
Using SASS #mixin, #include, #content and regular variables you can setup an environment where you create your media queries "inline", or inside of your regular selectors if you will.
This is my mixin for media queries:
(never mind the stupid variable names and placeholder breakpoints)
// Breakpoints
$mq-tiny: 5em;
$mq-small: 10em;
$mq-medium: 15em;
$mq-large: 20em;
$mq-huge: 25em;
$mq-crazy: 30em;
#mixin mq($size, $direction: min-width, $media-type: only all) {
#if $size == tiny { #media #{$media-type} and (#{$direction}: $mq-tiny) { #content; } }
#else if $size == small { #media #{$media-type} and (#{$direction}: $mq-small) { #content; } }
#else if $size == medium { #media #{$media-type} and (#{$direction}: $mq-medium) { #content; } }
#else if $size == large { #media #{$media-type} and (#{$direction}: $mq-large) { #content; } }
#else if $size == huge { #media #{$media-type} and (#{$direction}: $mq-huge) { #content; } }
#else if $size == crazy { #media #{$media-type} and (#{$direction}: $mq-crazy) { #content; } }
#else { #media #{$media-type} and (#{$direction}: $size) { #content; } }
}
And here's an example of how it can be used:
SCSS:
.selector {
width: 100px;
#include mq(large) {
width: 1000px;
}
}
CSS output:
.selector {
width: 100px
}
#media only all and (min-width: 20em) {
.selector {
width: 1000px
}
}
Take note that you don't need to use a variable name for the width. You can pass 1000px instead of large if you want to.
You may also have noticed the optional arguments in the #mixin; $direction and $media-type. These arguments default to min-width and only all respectively, but if you pass them through the #include they'll change for that specific element only.
Example with other arguments:
SCSS:
.selector {
#include mq(1000px, min-height) {
width: 100px;
}
}
CSS output:
#media only all and (min-height: 1000px) {
.selector {
width: 100px
}
}
Hope this helps!
Edit:
Here's a pen if you want to play around with it.
To increase maintainability of your code you can use variables to define media queries breakpoints:
// define breakpoints
$small-screen: 321px;
$large-screen: 1824px;
$small: "only screen and (min-width:"#{$small-screen}")";
$large: "only screen and (min-width:"#{$large-screen}")";
// and so on...
// now you can esely manipulate with your media breakpoints
#media #{$small} {
...
}
#media #{$large} {
...
}
Highly recommend you develop your SAAS css style import bootstrap framework, it will save you much time at responsive web designs
More detailed information please see this link:
http://pivotallabs.com/sass-with-bootstrap/
Related
So I I am trying to style a custom wordpress theme, however the breakpoints I'm using are being applied at all screen sizes, literally just overwriting the styles I've already written. I have never had this problem before, and I have used this exact code on other, non-WordPress sites. In fact I literally copied it over from a site I made in Gatsby.
this is my _breakpoints.scss file
$breakpoints: (
"xs":0,
"sm":30rem,
"md":45rem,
"lg":60rem,
"xl":75rem,
);
#mixin xs {
#media (min-width: map-get($breakpoints, "xs")){
#content;
}
}
#mixin sm {
#media (min-width: map-get($breakpoints, "sm")){
#content;
}
}
#mixin md {
#media (min-width: map-get($breakpoints, "md")){
#content;
}
}
#mixin lg {
#media (min-width: map-get($breakpoints, "lg")){
#content;
}
}
#mixin xl {
#media (min-width: map-get($breakpoints, "xl")){
#content;
}
}
#mixin breakpoint($bp: 0) {
#media (min-width: $bp) {
#content;
}
}
and this is the element I am working on. the element should be hidden until the lg breakpoint (60rem/960px)
.hero-logo-container {
display: none;
#include lg {
display: block;
}
}
I did wonder was it something odd with flexbox, but like I said I've literally just used the exact same _breakpoints .scss on another site and it works fine. I have also thought that this may be some odd quirk of WordPress?
any help you can give me would be appreciated
I have tried hard-coding the file path to the image, in case that was the culprit (rather than using get_theme_file_uri()) but that wasn't it, other styles are just being similarly overridden.
I have recreated what has been built so far in basic HTML, with the same SCSS files and the problem is happening there.
I just can't see what I'm doing wrong...
What is the better SASS way to write the code below. I end up writing lots of media queries with dimensions and also repeating classes in SASS - surely there is a nice way to use mixins or includes? Anyone else write media queries better than below?
$strFullWidth:100%;
.aboutWrapper {
.logo{width:$strFullWidth;max-width:620px}
}
// MOBILE
#media only screen and (max-width:850px){
.aboutWrapper {
.logo{width:$strFullWidth;max-width:420px}
}
}
// SMALL MOBILE
#media only screen and (max-width:550px){
.aboutWrapper {
.logo{width:$strFullWidth;max-width:320px}
}
}
I like the queries David Walsh proposes in his blog post. So in your case the mixins could look like:
#mixin mobile {
#media only screen and (max-width:850px) {
#content;
}
}
#mixin smallMobile {
#media only screen and (max-width:550px) {
#content;
}
}
With these mixings, your SCSS code boils down to:
$strFullWidth:100%;
.aboutWrapper {
.logo {
width:$strFullWidth;
max-width:620px;
}
}
#include mobile {
.aboutWrapper .logo { max-width:420px; }
}
#include smallMobile {
.aboutWrapper .logo { max-width:320px; }
}
I'm trying to combine the use of a Sass variable with #media queries as follows:
$base_width:1160px;
#media screen and (max-width: 1170px) {$base_width: 960px;}
#media screen and (min-width: 1171px) {$base_width: 1160px;}
$base_width is then defined at various points in the stylesheet width percentage-based measurements to produce fluid layouts.
When I do this, the variable seems to be recognized properly but the conditions for the media query are not. For example, the above code produces an 1160px layout regardless of screen width. If I flip-flop the #media statements like so:
#media screen and (min-width: 1171px) {$base_width: 1160px;}
#media screen and (max-width: 1170px) {$base_width: 960px;}
It produces a 960px layout, again regardless of screen width. Also note that if I remove the first line of $base_width: 1160px; it returns an error for an undefined variable. Any ideas what I'm missing?
This is simply not possible. Since the trigger #media screen and (max-width: 1170px) happens on the client-side.
Achieving your expected result would only be possible if SASS grabbed all rules and properties in your stylesheet containing your $base_width variable and copied/changed them accordingly.
Since it won't work automatically you could do it by hand like this:
#media screen and (max-width: 1170px)
$base_width: 960px // you need to indent it to (re)set it just within this media-query
// now you copy all the css rules/properties that contain or are relative to $base_width e.g.
#wrapper
width: $base_width
...
#media screen and (min-width: 1171px)
$base_width: 1160px
#wrapper
width: $base_width
...
This is not really DRY but the best you can do.
If the changes are the same every time you could also prepare a mixin containing all the changing values, so you wouldn't need to repeat it. Additionally you can try to combine the mixin with specific changes. Like:
#media screen and (min-width: 1171px)
+base_width_changes(1160px)
#width-1171-specific-element // additional specific changes, that aren't in the mixin
display: block
And the Mixin would look like this
=base_width_changes($base_width)
#wrapper
width: $base_width
Similar to Philipp Zedler's answer, you can do it with a mixin. That lets you have everything in a single file if you want.
#mixin styling($base-width) {
// your SCSS here, e.g.
#Contents {
width: $base-width;
}
}
#media screen and (max-width: 1170px) {
#include styling($base-width: 960px);
}
#media screen and (min-width: 1171px) {
#include styling($base-width: 1160px);
}
This isn't possible with SASS, but it is possible with CSS variables (or CSS custom properties). The only drawback is browser support – but there's actually a PostCSS plugin - postcss-css-variables - that "flattens" the use of CSS variables (which gives you support for older browsers, too).
The following example works great with SASS (and with postcss-css-variables you get support for older browsers too).
SCSS
$mq-laptop: 1440px;
$mq-desktop: 1680px;
:root {
--font-size-regular: 14px;
--gutter: 1rem;
}
// The fact that we have to use a `max-width` media query here, so as to not
// overlap with the next media query, is a quirk of postcss-css-variables
#media (min-width: $mq-laptop) and (max-width: $mq-desktop - 1px) {
:root {
--font-size-regular: 16px;
--gutter: 1.5rem;
}
}
#media (min-width: $mq-desktop) {
:root {
--font-size-regular: 18px;
--gutter: 1.75rem;
}
}
.my-element {
font-size: var(--font-size-regular);
padding: 0 calc(var(--gutter) / 2);
}
This would result in the following CSS. The repetitive media queries will increase the file size, but I have found that the increase is usually negligible once the web server applies gzip (which it will usually do automatically).
CSS
.my-element {
font-size: 14px;
padding: 0 calc(1rem / 2);
}
#media (min-width: 1680px) {
.my-element {
padding: 0 calc(1.75rem / 2);
}
}
#media (min-width: 1440px) and (max-width: 1679px) {
.my-element {
padding: 0 calc(1.5rem / 2);
}
}
#media (min-width: 1680px) {
.my-element {
font-size: 18px;
}
}
#media (min-width: 1440px) and (max-width: 1679px) {
.my-element {
font-size: 16px;
}
}
Edit: Please do not use this solution. The answer by ronen is much better.
As a DRY solution, you can use the #import statement inside a media query, e.g. like this.
#media screen and (max-width: 1170px) {
$base_width: 960px;
#import "responsive_elements";
}
#media screen and (min-width: 1171px) {
$base_width: 1160px;
#import "responsive_elements";
}
You define all responsive elements in the file included using the variables defined in the media query. So, all you need to repeat is the import statement.
With #ronen's great answer and a map, there's some real power available:
#mixin styling($map) {
.myDiv {
background: map-get($map, 'foo');
font-size: map-get($map, 'bar');
}
}
#media (min-height: 500px) {
#include styling((
foo: green,
bar: 50px
));
}
#media (min-height: 1000px) {
#include styling((
foo: red,
bar: 100px
));
}
It's now possible to have lots more DRY media queries targeting .myDiv with a bunch of different values.
Map docs: https://sass-lang.com/documentation/functions/map
Example map usage: https://www.sitepoint.com/using-sass-maps/
I had the same problem.
The $menu-width variable should be 240px on the mobile view #media only screen and (max-width : 768px) and 340px on the desktop view.
So i have simply created two variables:
$menu-width: 340px;
$menu-mobile-width: 240px;
And here is how i have used it:
.menu {
width: $menu-width;
#media only screen and (max-width : 768px) {
width: $menu-mobile-width;
}
}
Two recommendations
1
Write your "default" CSS statements to be for small screens and only use media queries for larger screens. There's usually no need for a max-width media query.
Example (assuming the element has class "container")
#mixin min-width($width) {
#media screen and (max-width: $width) {
#content;
}
}
.container {
width: 960px;
#include min-width(1170px) {
width: 1160px;
}
}
2 Use CSS variables to solve the problem, if you can.
#mixin min-width($width) {
#media screen and (max-width: $width) {
#content;
}
}
:root {
--container-width: 960px;
#include min-width(1170px) {
--container-width: 1160px;
}
}
.container {
width: var(--container-width);
}
Note:
Since it will have the width of 1160px when the window has a width of 1170px, it may be better to use a width of 100% and max-width of 1160px, and the parent element might have a horizontal padding of 5px, as long as the box-sizing property is set to border-box. There are a lot of ways to solve the problem. If the parent is not a flex or grid container you might use .container { margin: auto }.
This is also possible with %placeholders.
%placeholders can be wrapped in media queries. So you could set up multiple variables to use at different screen sizes, and then the placeholders would automagically pre-process accordingly. I'm using some mixins to shorten my media query declarations here also.
In your _vars.scss file:
$width-1: 960px;
$width-2: 1160px;
In your _placeholders.scss file:
%variable-site-width {
#media screen and (max-width: 1170px) { width: $width-1; }
#media screen and (min-width: 1171px) { width: $width-2; }
}
In your page.scss file:
.wrapper. { #extend %variable-site-width; background: red; etc... }
And this will compile to something similar to:
#media screen and (max-width: 1170px) {
.wrapper { width: 960px; }
}
#media screen and (min-width: 1171px) {
.wrapper { width: 1160px; }
}
Voila!
I use this technique extensively for things like variable font sizes and a raft of other things.
http://css-tricks.com/media-queries-sass-3-2-and-codekit/
I recently discovered this technique while reading about scss. I've come to enjoy writing lesscss and I would rather not switch. I was curious as to what options lesscss has to accomplishing a similar technique? I really like the idea of writing media queries inside the primary class/id declaration.
#mixin breakpoint($point) {
#if $point == papa-bear {
#media (max-width: 1600px) { #content; }
}
#else if $point == mama-bear {
#media (max-width: 1250px) { #content; }
}
#else if $point == baby-bear {
#media (max-width: 650px) { #content; }
}
}
.page-wrap {
width: 75%;
#include breakpoint(papa-bear) { width: 60%; }
#include breakpoint(mama-bear) { width: 80%; }
#include breakpoint(baby-bear) { width: 95%; }
}
UPDATE
I have found this answer http://blog.scur.pl/2012/06/variable-media-queries-less-css/ my only critism is how can I make it so that the media queries arnt redundant? how would I make this all compile into 1 mediaquery block?
This is how I do my media queries in LESS, utilising query bubbling as outlined in the article you linked to:
#palm : ~"screen and (max-width: 40em)";
#lap : ~"screen and (min-width: 50em)";
#desk : ~"screen and (min-width: 60em)";
.some-class {
color: red;
#media #lap {
color: blue;
}
}
Unfortunately though there is no way to have it all compile down to one media query block. This may also be the case with SASS/SCSS. The only reason this could be a problem is that is increases file size.
However you shouldn't worry about that. Why? The repetition of multiple media query blocks is negated by GZIP (more repetition == better compression). So providing your server is encoding with GZIP (most do, if not you should be able to enable it, it's worthwhile) you will not see any/much increase in file size.
Finally, even discounting GZIP, I still wouldn't want it to compile to one media query block. On any large CSS code base the proximity of having media queries next to the selectors is useful and desirable
I have a Sass mixin for my media queries based on Twitter Bootstrap's responsive media queries:
#mixin respond-to($media) {
#if $media == handhelds {
/* Landscape phones and down */
#media (max-width: 480px) { #content; }
}
#else if $media == small {
/* Landscape phone to portrait tablet */
#media (max-width: 767px) {#content; }
}
#else if $media == medium {
/* Portrait tablet to landscape and desktop */
#media (min-width: 768px) and (max-width: 979px) { #content; }
}
#else if $media == large {
/* Large desktop */
#media (min-width: 1200px) { #content; }
}
#else {
#media only screen and (max-width: #{$media}px) { #content; }
}
}
And I call them throughout my SCSS file like so:
.link {
color:blue;
#include respond-to(medium) {
color: red;
}
}
However, sometimes I want to style multiple queries with the same styles. Right now I'm doing them like this:
.link {
color:blue; /* this is fine for handheld and small sizes*/
/*now I want to change the styles that are cascading to medium and large*/
#include respond-to(medium) {
color: red;
}
#include respond-to(large) {
color: red;
}
}
but I'm repeating code so I'm wondering if there is a more concise way to write it so I can target multiple queries. Something like this so I don't need to repeat my code (I know this doesn't work):
#include respond-to(medium, large) {
color: red;
}
Any suggestions on the best way to handle this?
A mixin like that leaves you in a position that's not very flexible, and not just because you're using px (see: http://blog.cloudfour.com/the-ems-have-it-proportional-media-queries-ftw/). Simply put, you've made your mixin too specific and not very reusable for other sites.
I'm currently using a collection of 4 mixins to handle the most common media queries: min-width, max-width, between, and outside (I've sampled min-width and between below)
$output-media-width: true !default; // true = all, otherwise use a list of numeric values (eg. 320px 23em)
#mixin media-min-width($bp) {
#if type-of($output-media-width) != list {
#media (min-width: $bp) {
#content;
}
} #else {
$output-bp: find-comparable($bp, $output-media-width);
#if not comparable($output-bp, $bp) {
#debug "Output breakpoint: #{$output-bp}, Chosen minimum width: #{$bp}";
} #else if $output-bp >= $bp {
#content;
}
}
}
#mixin media-between($bp1, $bp2) {
#if type-of($output-media-width) != list {
#media (min-width: $bp1) and (max-width: make-less-than($bp2)) {
#content;
}
} #else {
$output-bp1: find-comparable($bp1, $output-media-width);
$output-bp2: find-comparable($bp2, $output-media-width);
#if not comparable($output-bp1, $bp1) or not comparable($output-bp2, $bp2) {
#debug "Output breakpoints: #{$output-bp1} and #{$output-bp2}, Chosen breakpoints: #{$bp1} and #{$bp2}";
} #else if $output-bp2 >= $bp1 and $output-bp2 < $bp2 {
#content;
}
}
}
#function find-comparable($val, $list) {
#each $item in $list {
#if comparable($val, $item) {
#return $item;
}
}
}
#function make-less-than($val) {
#return if(unit($val) == em, $val - .001, $val - 1);
}
This mixin suite lets me generate a responsive CSS file or a collection of non-responsive CSS files at any width I desire (specifically for devices that don't take kindly to media queries) just by having a variable like this at the top of my file:
$output-media-width: 800px 60em;
A list of sizes lets me use px in those rare cases where em is inappropriate (such as for dealing with images).
// Device widths
$device-x-narrow: 23em; // 320px
$device-narrow: 35em; // 480px
$device-medium: 60em; // 800px
$device-wide: 70em; // 1000px
article.event {
#mixin tableify {
// footer { display: table-row }
footer section { display: table-cell }
footer section + section { padding-left: 2em }
}
#include media-min-width($device-medium) { // 2-col layout still
#main > & { // single event view
#include tableify;
}
}
// sometimes you need a non-standard breakpoint, too...
#include media-min-width(27em) { // narrow devices
section & {
#include tableify;
}
}
#include media-max-width(27em) {
footer section.categories ul {
display: block;
padding-left: 0;
li { display: inline }
li + li { margin-left: 1em }
}
}
}
Despite the fact that #cimmanon answered my question before I posted that I was using Twitter Bootstrap, it had some really interesting ideas in it which I think I'll apply from now on for my Sass projects that use Twitter Bootstrap. Here is what I found worked great:
/* Responsive dimensions */
$handheld-max: 479px;
$small-min: $handheld-max + 1;
$small-max: 767px;
$medium-min: $small-max + 1;
$medium-max: 979px;
$large-min: $medium-max + 1;
$large-max: 1199px;
$xlarge: 1200;
/*Responsive query mixins */
#mixin media-above($min) {
#media (min-width: $min) { #content; }
}
#mixin media-below($max) {
#media (max-width: $max) { #content; }
}
#mixin media-between($min, $max) {
#media (min-width: $min) and (max-width: $max) { #content; }
}
and then call it in my code like so (based on my request in the question):
.link {
color: blue;
#mixin media-above($medium-min){
color: red;
}
}
Using bootstrap-sass variables, I defined such mixins in SASS syntax:
=media-width-below($max)
#media (max-width: $max)
#content
=media-width-between($min, $max)
#media (min-width: $min), (max-width: $max)
#content
=media-width-above($min)
#media (min-width: $min)
#content
=media-xs
+media-width-above($screen-xs-min)
#content
=media-sm
+media-width-above($screen-sm-min)
#content
=media-md
+media-width-above($screen-md-min)
#content
=media-lg
+media-width-above($screen-lg-min)
#content
Those mixins will be useable just like +make-sm-column or .col-md-5 classes. You can just use it like this:
body
+media-xs
background-color: yellow
+media-sm
background-color: blue
+media-md
background-color: red
+media-lg
background-color: green
When you will be making your browser smaller by changing it from large to xs, you'll see colors in this order: green, red, blue, yellow.