I'm creating a React application that has a hero display on the landing page that displays one of three images: [hero-1.jpg, hero-2.png, hero-3.png] based on the users viewport screen size.
I have been unsuccessful trying to find resources online that show a DRY method for achieving this, for the sake of participation, I'll leave this code that I attempted that - in theory made sense to me.
N.B. I am extremely new to Sass/Scss
snippet.html
<section className="hero is-fullheight has-background-black">
<div className="hero-body">
<div className="container">
</div>
</div>
</section>
hero.scss
$i: 1;
$breakpoint-phone: 480px;
$breakpoint-tablet: 768px;
$breakpoint-desktop: 1024px;
#mixin modifier ($i:1) {
#content;
#media only screen and (max-width:$breakpoint-phone) { $i: 2; }
#media only screen and (max-width:$breakpoint-tablet) { $i: 3; }
#media only screen and (max-width:$breakpoint-desktop) { $i: 1; }
}
.hero {
background-position: center;
background-size: cover
}
#include modifier {.hero {background-image: url('../assets/hero-#{$i}.jpg');}}
Methodology:
Display content by default (which is pulled from #include).
Mixin modifier will modify the $i passed to the mixin, which is interpolated in the image path.
Expected Result:
Based on each breakpoint, $i will be set to the appropriate value and change the background image dynamically.
Actual Result:
The global $i is used, and the web page displays hero-1.jpg.
There are a few ways you can achieve this. If I was going about this, this is how I would do it.
Also, it would be very wise to practice mobile first development. Use min-width and go up instead of using max-width going down. The way you currently have it structured would mean you wouldn't have a valid URL if that $i variable wasn't set at 1 at the top of your document. Writing SASS or CSS will be much easier this way once you get used to it.
$tablet: 768px;
$desktop: 1024px;
#mixin hero-image() {
.hero {
background-position: center;
background-size: cover;
background-image: url('../assets/hero-2.jpg');
#media screen and (min-width: $tablet) {
background-image: url('../assets/hero-3.jpg');
}
#media screen and (min-width: $desktop) {
background-image: url('../assets/hero-1.jpg');
}
}
}
#include hero-image();
You're still going to have to write the background-image property 3 times. The way you were doing it was close, but you would have had to #include modifier() 3 times in your consuming scss file. At the end of the day SASS compiles to CSS. You could potentially use a SASS function or For Loop to achieve this, but mixins can get really complicated and powerful, but also incredibly difficult to read and understand. Here's what the mixin I just showed you compiles to in CSS.
.hero {
background-position: center;
background-size: cover;
background-image: url("../assets/hero-2.jpg");
}
#media screen and (min-width: 768px) {
.hero {
background-image: url("../assets/hero-3.jpg");
}
}
#media screen and (min-width: 1024px) {
.hero {
background-image: url("../assets/hero-1.jpg");
}
}
I recommend putting your SCSS/SASS into this compiler to see your results before compiling your actual project.
https://www.sassmeister.com/
Even though you are repeating background-image 3 times inside of the mixin this is very much still DRY code because you can include that one mixin everywhere your images will be shown and if you need to edit it, you can edit it in one place.
Related
SCSS variables don't seem to work when used in a container query. For example this code works just fine:
.parent {
container-type: inline-size;
background: yellow;
}
#container (max-width: 800px) {
.child {
background:green;
}
}
See pen: https://codepen.io/pwkip/pen/jOprKya
But when I try to use a sass-variable to define the breakpoint, it fails.
$width: 800px;
.parent {
container-type: inline-size;
background: yellow;
}
#container (max-width: $width) {
.child {
background:green;
}
}
See pen: https://codepen.io/pwkip/pen/BaPzVZW
What's the problem here? Any workaround?
I cannot find the definitive sass lang entry, but hash and curly brace is often used in #media queries.
#container (max-width: #{$width}) {
.child {
background:green;
}
}
In Brave it works in that codepen; and so do the following:
#container (max-width: ${width} ) {
As "A Boston" already pointed out the issue seems to be that you don't use the hash syntax like in his first example.
I use https://www.sassmeister.com/ quite often to check what it compiles to.
In your Codepen you forgot to add a semicolon after
$width: 800px;
^
According to: https://developer.mozilla.org/en-US/docs/Web/CSS/#container
#container queries are not supported for Firefox yet. Only Firefox Nightly supports it.
I've tested it in Chrome and it worked with the tweaks. Make sure your browser version is supported. My current Safari version did not work either (due to the version mismatch).
I've set up some variables in SASS as follows:
// fundamental layout variables
$raw-layout-var-1: 60px;
$raw-layout-var-2: 200px;
// calculated layout variables
$calc-layout-var-1: #{$raw-layout-var-1} + #{$raw-layout-var-2};
I am attempting to use these variables with media queries so I can do something like the following, and have the calculated variables and the rest of the stylesheet update when the relevant criteria are met. I would like to be able to override the fundamental or calculated variables as I need.
#media only screen and (min-height: 500px) {
$raw-layout-var-1: 120px;
#media only screen and (min-width: 500px) {
$raw-layout-var-2: 100px;
}
Currently I have a workaround where all updates to variables use the !global keyword to update them globally, but this results in a somewhat complex setup where the fundamental variables, calculated variables, and the main css sheet are placed in mixins, to be called in each individual media query:
#mixin reset-raw-vars() {
$raw-layout-var-1: 60px !global;
$raw-layout-var-2: 200px !global;
}
#mixin update-calc-vars() {
$calc-layout-var-1: #{$raw-layout-var-1} + #{$raw-layout-var-2} !global;
}
#mixin add-main() {
div {
width: $calc-layout-var-1;
height: $raw-layout-var-1;
}
}
#media only screen and (min-width: 500px) {
#include reset-raw-vars(); // resets the raw variables in case these were changed globally in a previous media query
// here you can change any fundamental variables you need
#include update-calc-vars(); // recalculates calculated variables
// here you can override how any calculated variables are made
#include add-main(); // update the rest of the stylesheet with new layout
}
Even worse, if I have a pair of media queries such as those shown in the second code block, I have to manually create a hybrid with both min-height and min-width in order to apply both sets of conditions. Clearly this isn't DRY and could get seriously out of control with even slightly complex responsive pages. I can see from Using Sass Variables with CSS3 Media Queries that SASS doesn't have this functionality - is there a better way than I've outlined above?
When using MJML to create the email templates, it provides the carousel feature in email templates. Here is the code from the their sample. (icon-width and tb-width were added by me)
<mj-carousel icon-width="30px" tb-width="20px">
<mj-carousel-image src="https://www.mailjet.com/wp-content/uploads/2016/11/ecommerce-guide.jpg" />
<mj-carousel-image src="https://www.mailjet.com/wp-content/uploads/2016/09/3#1x.png" />
<mj-carousel-image src="https://www.mailjet.com/wp-content/uploads/2016/09/1#1x.png" />
</mj-carousel>
As you see, the attribute tb-width controls the width thumbnails of the carousel, I want to change the value of tb-width to be smaller when it goes into mobile screen, otherwise I will have to stick to one value cross all screen sizes.
I tried this:
#media all and (max-width: 480px) {
[tb-width] {
width: 50px !important;
} // this is not working
}
Then I tried this based on the official document
#media all and (max-width: 480px) {
div[style*="tb-width:20px;"] {
width: 300px !important;
}
}
None of above code works.
How do you guys solve this problem? Thanks in advance.
Thanks for #dermothghes from mjml slack channel, here is the solution:
All these mjml components will have a corresponded css class in the outputted HTML, what we need to do is to compile the .mjml file first and find the corresponded css class from the output.
In my question, this should work:
#media all and (max-width: 480px) {
.mj-carousel-thumbnail {
width: 50px !important;
}
}
.mj-carousel-thumbnail is the generated class from mj-carousel-thumbnail mjml component.
In our current design, form elements get styled differently on a mobile device:
#media screen and (max-width: 759px) {
form label {
display: block;
float: none;
}
/*There's more...*/
}
Given the limited screen space, we're basically forcing a vertical form layout (#3-4 in this classic uxmatters link).
Because some form labels have to be insanely verbose For sensible reasons, we now want to apply this style to some desktop forms, i.e. outside of scope of the media query. This, however, is invalid SASS syntax:
#media screen and (max-width: 759px), .vertical-form {
/*...*/
}
How can the ruleset be applied on output within the media query and to .vertical-form form label (or, ideally, form.vertical-form label) elements?
Media queries are not selectors, they are special language constructs. You can't combine them with anything other than media queries. You will have to use a mixin to do what you're asking for because extends don't work across media queries either.
#mixin vertical-form {
label {
display: block;
float: none;
}
}
#media screen and (max-width: 759px) {
form {
#include vertical-form;
}
}
.vertical-form {
#include vertical-form;
}
In Sass, I can't quite discern the difference between using #include with a mixin and using #extend with a placeholder class. Don't they amount to the same thing?
Extends do not allow customization, but they produce very efficient CSS.
%button
background-color: lightgrey
&:hover, &:active
background-color: white
a
#extend %button
button
#extend %button
Result:
a, button {
background-color: lightgrey;
}
a:hover, button:hover, a:active, button:active {
background-color: white;
}
With mixins, you get duplicated CSS, but you can use arguments to modify the result for each usage.
=button($main-color: lightgrey, $active-color: white)
background-color: $main-color
border: 1px solid black
border-radius: 0.2em
&:hover, &:active
background-color: $active-color
a
+button
button
+button(pink, red)
Results in:
a {
background-color: lightgrey;
border: 1px solid black;
border-radius: 0.2em;
}
a:hover, a:active {
background-color: white;
}
button {
background-color: pink;
border: 1px solid black;
border-radius: 0.2em;
}
button:hover, button:active {
background-color: red;
}
Please follow this consecutive set of code examples to see how you can make your code cleaner and more maintainable by using extends and mixins effectively: http://thecodingdesigner.com/posts/balancing
Note that SASS unfortunately does not allow using extends inside media queries (and corresponding example from the above link is wrong). In the situation where you need to extend based on media queries, use a mixin:
=active
display: block
background-color: pink
%active
+active
#main-menu
#extend %active // Active by default
#secondary-menu
#media (min-width: 20em)
+active // Active only on wide screens
Result:
#main-menu {
display: block;
background-color: pink;
}
#media (min-width: 20em) {
#secondary-menu {
display: block;
background-color: pink;
}
}
Duplication is inevitable in this case, but you shouldn't care too much about it because web server's gzip compression will take care of it.
PS Note that you can declare placeholder classes within media queries.
Update 2014-12-28: Extends produce more compact CSS than mixins do, but this benefit is diminished when CSS is gzipped. If your server serves gzipped CSS (it really should!), then extends give you almost no benefit. So you can always use mixins! More on this here: http://www.sitepoint.com/sass-extend-nobody-told-you/
A good approach is to use both - create a mixin that will allow you lots of customisation and then make extends for common configurations of that mixin. For example (SCSS Syntax):
#mixin my-button($size: 15, $color: red) {
#include inline-block;
#include border-radius(5px);
font-size: $size + px;
background-color: $color;
}
%button {
#include my-button;
}
%alt-button {
#include my-button(15, green);
}
%big-button {
#include my-button(25);
}
This saves you from calling the my-button mixin over and over. It also means you don't have to remember the settings for common buttons but you still have the ability to make a super unique, one-off button should you choose.
I take this example from a blog post I wrote not long ago. Hope this helps.
In my opinion extends are pure evil and should be avoided. Here is why:
given the scss:
%mystyle {color: blue;}
.mystyle-class {#extend %mystyle}
//basically anything not understood by target browser (such as :last-child in IE8):
::-webkit-input-placeholder {#extend %mystyle}
The following css will be generated:
.mystyle-class, ::-webkit-input-placeholder { //invalid in non-webkit browsers
color: blue;
}
When a browser doesn’t understand a selector, it invalidates the entire line of selectors. This means that your precious mystyle-class is no longer blue (for many browsers).
What does this really mean? If at any time you use an extend where a browser may not understand the selector every other use of the extend will be invalidated.
This behavior also allows for evil nesting:
%mystyle {color: blue;}
#mixin mystyle-mixin {#extend %mystyle; height: 0;}
::-webkit-input-placeholder {#include mystyle-mixin}
//you thought nesting in a mixin would make it safe?
.mystyle-class {#extend %mystyle;}
Result:
::-webkit-input-placeholder, .mystyle-class { //invalid in non-webkit browsers
color: blue;
}
::-webkit-input-placeholder {
height: 0;
}
Tl;dr: #extend is perfectly ok for as long as you never use it with any browser spesific selectors. If you do, it will suddenly tear down the styles wherever you have used it. Try to rely on mixins instead!
Use mixins if it accepts a parameter, where the compiled output will change depending on what you pass into it.
#include opacity(0.1);
Use extend (with placeholder) for any static repeatable blocks of styles.
color: blue;
font-weight: bold;
font-size: 2em;
I totally agree with the previous answer by d4nyll. There is a text about extend option and while I was researching this theme I found a lot of complaints about extend, so just have in mind that and if there is a possibility to use mixin instead of extend, just skip extend.