Splitting up a list of variables in Sass - variables

In Sass is there a way to split up a list of variables / classes with hyphens?
It's a fuzzy question title so it's probably best I show what I'm trying to achieve.
In the below example I'm trying to create some utility classes that I can apply to HTML elements to help with vertical rhythm.
For example I may want to give an element a small margin that is consistent with my vertical rhythm strategy and so I'll add the class .m-t-s (which stands for margin-top-small).
I then want to output versions of those utility classes against for each media query I have for fine grain control e.g. I may want a class .m-t-s-768 which will add a small top margin when there is a minimum viewport width of 768px.
I have achieved this below, but it is a very long-winded and repetitive way of doing it. Can anyone suggest a more concise way?
Variables
––––––––––
$mediaQueries-px:
640,
768,
1024
;
$s: 20px; /* FYI I've simplified these examples for the sake of demonstration, normally I use something like ($baseLineHeight / 1.5) + rem */
$m: 50px;
$l: 60px;
Creating the classes
–––––––––––––––––––––
.m-t-s {
margin-top: $s;
}
/* Create versions for each defined media query */
#each $mediaQuery in $mediaQueries-px {
#media screen and (min-width: ($mediaQuery / 16px)) {
.m-t-s-#{$mediaQuery} {
margin-top: $s;
}
}
}
.m-t-m {
margin-top: $m;
}
/* Create versions for each defined media query */
#each $mediaQuery in $mediaQueries-px {
#media screen and (min-width: ($mediaQuery / 16px)) {
.m-t-m-#{$mediaQuery} {
margin-top: $m;
}
}
}
This repeats for .m-t-l too (margin top large), and then it continues for padding classes (e.g. .p-t-s and so on), so it gets to be a pretty long list of utility classes.

To programatically generate that output, you need another list and an inner loop:
$mediaQueries-px:
640,
768,
1024
;
$s: 20px;
$m: 50px;
$l: 60px;
$sizes: s $s, m $m, l $l;
#each $size in $sizes {
.m-t-#{nth($size, 1)} {
margin-top: nth($size, 2);
}
}
#each $mediaQuery in $mediaQueries-px {
#media screen and (min-width: ($mediaQuery / 16 * 1em)) { // modified for compilation purposes
#each $size in $sizes {
.m-t-#{nth($size, 1)}-#{$mediaQuery} {
margin-top: nth($size, 2);
}
}
}
}
Output:
.m-t-s {
margin-top: 20px;
}
.m-t-m {
margin-top: 50px;
}
.m-t-l {
margin-top: 60px;
}
#media screen and (min-width: 40em) {
.m-t-s-640 {
margin-top: 20px;
}
.m-t-m-640 {
margin-top: 50px;
}
.m-t-l-640 {
margin-top: 60px;
}
}
#media screen and (min-width: 48em) {
.m-t-s-768 {
margin-top: 20px;
}
.m-t-m-768 {
margin-top: 50px;
}
.m-t-l-768 {
margin-top: 60px;
}
}
#media screen and (min-width: 64em) {
.m-t-s-1024 {
margin-top: 20px;
}
.m-t-m-1024 {
margin-top: 50px;
}
.m-t-l-1024 {
margin-top: 60px;
}
}

Related

Media query fails for only one property

I am unable to change padding for one element on mobile devices. The queries are working for several properties, but padding will not work (neither will line height if I try to use that). Basic styling in custom css is:
#topright {
font-size: 20px;
letter-spacing: 4px;
color: rgba(255,255,255,1.0);
padding-top: 8px !important;
padding-bottom: 8px !important;
font-weight: 200;
}
Media query for phone is
/* Custom, iPhone Retina */
#media only screen and (min-width : 320px) {
.header-2 .logo {
width: 250px;
}
.footer-widget ul li {
width: 100%;
}
.footer-widget ul {
padding-left: 0;
padding-right: 0;
}
div.vc_column-inner vc_custom_1476556729591 {
padding-left: 0;
padding-right: 0;
}
.footer-widget .textwidget p {
text-align: center;
}
#topright {
padding-top: 1px;
padding-bottom: 1px
}
}
The smaller padding number will not be applied. If I remove !important from main css, then the phone query gets applied to all devices. It's weird because all the other properties for the phone query are working fine.
From this helpful page on media queries, min-width: 320px means:
"If [device width] is greater than or equal to 320px, then do {...}"
In other words, the media query you think you created to target only iPhone will actually be firing for all devices which have a width of 320px or greater. Instead, I think you intended to use max-width
So use this CSS:
/* Custom, iPhone Retina */
#media only screen and (max-width : 320px) {
.header-2 .logo {
width: 250px;
}
...
#topright {
padding-top: 1px;
padding-bottom: 1px
}
}
And you should also remove the !important directives from your main.css file.

Using rulesets in LESS for media queries

When using Sass I would do something global like this (which I got from CSS-tricks btw)
// Variables for MQ's
$mq-mobile-portrait : 320px !default;
$mq-mobile-landscape : 480px !default;
$mq-tablet-portrait : 768px !default;
$mq-tablet-landscape : 1024px !default;
$mq-desktop : 1382px !default;
Then I would create mixins for the media queries like this (I'll only include a few to give you an idea
// Mixins
// Both portrait and landscape
#mixin mobile-only {
#media (max-width : $mq-mobile-landscape) {
#content;
}
}
// Everything up to and including the portrait width of the phone
// Since it's the smallest query it doesn't need a min
#mixin mobile-portrait-only {
#media (max-width : $mq-mobile-portrait) {
#content;
}
}
So Sass has this #content which is great because it means that I don't have to declare the content within the mixin but can do an #include mixinName and it creates the parent wrapper for any CSS properties I need to put into it across different files. I discovered that this worked well for my work flow.
So here's an example of that in a partial .scss file:
section.footer {
height: 90px;
padding: 0 10px;
#include mobile-portrait-only {
padding-top: 10px;
background: $gum;
div.ftrLogo {
display: inline-block;
margin: 0;
height: 70px;
width: 45%;
div.smlLogo {
display: block;
background: url('../images/svg/small-logo2.svg');
width: 106px;
height: 49px;
margin: 0 auto;
padding: 0;
}
p.footer {
font-size: .375em;
color: $white;
text-align: center;
}
}
}
So as you can probably gather the #content allows you to just call an empty media query wrapper anywhere in your files (obviously you have to import all of your partials into one main file) but this is great.
Today I'm using LESS on a project and I like it a lot the problem is I can't seem to find an equivalent solution in LESS-land.
I was reading up on passing rulesets http://lesscss.org/features/#detached-rulesets-feature which looks like it's close to what I want but my brain is not understanding it today; I'm optimistic about tomorrow.
If anyone has tried anything like this or can immediately see the error in my ways; please provide your two cents. I really want to figure it out and thought to ask this gifted community of SO'ers.
Thank you in advance you're a baller!
// Variables for MQ's
#mq-mobile-portrait: 320px;
// Mixins
.mobile-portrait-only(#rules) {
#media (min-width: #mq-mobile-portrait) {
#rules();
}
}
Now you can use the following code:
div {
color: white;
.mobile-portrait-only({
color: white;
width: 100%;
max-width: 500px;
});
}
The above will compile into CSS code as follows:
div {
color: white;
}
#media (min-width: 320px) {
div {
color: white;
width: 100%;
max-width: 500px;
}
}
So detached rules are rules between {} assigned to a variable:
#detached: {};
Detached rules can be used as an argument for a mixin:
.mixin(#detached){}
You as call the above mixin with a detached rule as a parameter:
.mixin({color: red;});
or
#detached: {color: red;} // watch out for the last declaration wins rule for variables
.mixin(#detached);
Inside the mixin you should call the detached rules set to copy its properties and selectors (in fact you don't copy but insert them read for processing):
.mixin(#detached-rules) {
#detached-rules(); // parenthesis are required here
}
Finally for your example your code should look like that shown below:
#gum: url();
#white: white;
// Variables for MQ's
#mq-mobile-portrait: 320px;
// Mixins
.mobile-portrait-only(#rules) {
#media (min-width: #mq-mobile-portrait) {
#rules();
}
}
section.footer {
height: 90px;
padding: 0 10px;
.mobile-portrait-only( {
padding-top: 10px;
background: #gum;
div.ftrLogo {
display: inline-block;
margin: 0;
height: 70px;
width: 45%;
div.smlLogo {
display: block;
background: url('../images/svg/small-logo2.svg');
width: 106px;
height: 49px;
margin: 0 auto;
padding: 0;
}
p.footer {
font-size: .375em;
color: #white;
text-align: center;
}
}
});
}
I hadn't thought of doing it like Bass Jobsen suggested (although I've now seen that his approach is basically how the less docs do it), but I invented a mixin which I think is a bit more flexible. Though they are similar in result, I think the following solution allows for more customization and is easier to implement on the fly.
First I define the different sizes I want to use - to keep it simple, I'll just do two using a 'mobile first approach' (meaning if I don't include a media query, the rules will apply to all sizes and I should only include queries for sizes larger than mobile).
#tablet:~"(min-width:768px)";
#desktop:~"(min-width:1100px)";
Then the mixin:
.respond(#_size;#_rules){
#media #_size {
#_rules();
}
}
And Used Like the following:
.selector {
background:green;
.respond(#tablet,{
color:red;
background:blue;
});
}
And That Outputs:
.selector {
background:green;
}
#media (min-width:768px){
.selector{
color:red;
background:blue
}
}
With only two sizes to remember, it is easy enough just to do it the way Bass Jobsen suggested, but in practice, depending on how fine-grained I want my control to be, I may define up to 8 different media sizes (though I rarely use them all), and my approach above makes the process like calling one function rather than defining 8 different functions ( as I would do were I using the alternate approach ).
Hope this helps someone. It saves me a ton of time.

What is wrong with these SCSS media queries [duplicate]

This question already has answers here:
Using Sass Variables with CSS3 Media Queries
(8 answers)
Closed 7 years ago.
I am using the official Sass port of Twitter Bootstrap 3.3.3.
What I am trying to do is shrink the height of the navbar when the window is resized. Below is my media queries, however they don't work as I expect them to.
$navbar-height: 60px !default;
body {
padding-top: 70px !important;
}
#media(min-width: 768px) {
$navbar-height: 70px;
body {
padding-top: 80px !important;
}
}
#media(min-width: 992px) {
$navbar-height: 80px;
body {
padding-top: 90px !important;
}
}
#media(min-width: 1200px) {
$navbar-height: 90px;
body {
padding-top: 100px !important;
}
}
To make it work modify the element inside the #media query not the variable. So for example...
$navbar-height: 60px !default;
body {
padding-top: 70px !important;
}
#media(min-width: 768px) {
.nav-bar: $navbar-height + 10px;
body {
padding-top: 80px !important;
}
}
#media(min-width: 992px) {
.nav-bar: $navbar-height + 20px;
body {
padding-top: 90px !important;
}
}
#media(min-width: 1200px) {
.nav-bar: $navbar-height + 30px;
body {
padding-top: 100px !important;
}
}

Set "min-width" or "max-width" in a media query passing a parameter to a mixin

I would like to make dynamic MIN/MAX suffix in properties defined in a Less MediaQuery.
I wrote this code but it does not compile:
#screen-md: 800px;
.MEDIAQUERY(#min-max, #size)
{
#media screen and (#{min-max}-width: #size)
{
#{min-max}-width:100px;
}
}
header
{
background-color: blue;
.MEDIAQUERY ( #min-max: max, #size: #screen-md );
}
While #{min-max}-width:100px; is a correct syntax, equivalent applied in Mediaquery definition is not allowed, but I need to set sometime "max-width" value, and others "min-width" value in my media queries. How to obtain this?
Option 1: (Using a variable and interpolation)
You can do it like below
.MEDIAQUERY(#min-max, #size) {
#mediaQuery: ~"screen and (#{min-max}-width: #{size})";
#media #mediaQuery {
#{min-max}-width:100px;
}
}
Option 2: (Using Guards)
You can use guards in the mixin like below to check what was the value that was passed for the #min-max parameter and then output the appropriate CSS based on it.
.MEDIAQUERY(#min-max, #size){
& when (#min-max = min) {
#media screen and (min-width: #size) {
min-width:100px;
}
}
& when (#min-max = max) {
#media screen and (max-width: #size) {
max-width:100px;
}
}
}
When the above mixin is called like below (with either of the options mentioned above):
header
{
background-color: blue;
.MEDIAQUERY ( #min-max: max, #size: #screen-md );
}
div{
background-color: red;
.MEDIAQUERY ( #min-max: min, #size: #screen-md );
}
it would compile into the below CSS:
header {
background-color: blue;
}
#media screen and (max-width: 800px) {
header {
max-width: 100px;
}
}
div {
background-color: red;
}
#media screen and (min-width: 800px) {
div {
min-width: 100px;
}
}

Bootstrap, pull-left for small devices

I'm building a site in Bootstrap 3.
Is there anyway to make a element use the class pull-left on smaller devices and use pull-right on larger ones?
Something like: pull-left-sm pull-right-lg.
I've managed to do it with jquery, catching the resize of the window. Is there any other way? Pref without duplicating the code in a hidden-x pull-left. Or is it considered more ok to duplicate code/content now when going responsive?
Just add this to your SASS file:
#media (max-width: $screen-xs-max) {
.pull-xs-left {
float: left;
}
.pull-xs-right {
float: right;
}
}
#media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
.pull-sm-left {
float: left;
}
.pull-sm-right {
float: right;
}
}
#media (min-width: $screen-md-min) and (max-width: $screen-md-max) {
.pull-md-left {
float: left;
}
.pull-md-right {
float: right;
}
}
#media (min-width: $screen-lg-min) {
.pull-lg-left {
float: left;
}
.pull-lg-right {
float: right;
}
}
Insert actual px values for $screen-* if you use plain CSS of course.
HTML:
<div class="pull-md-left pull-lg-left">
this div is only floated on screen-md and screen-lg
</div>
You can use CSS Media Queries
basic usage will be like this; if you want to float left below devices of width 500px, then
#media (max-width: 500px) {
.your_class {
float: left;
}
}
#media (min-width: 501px) {
.your_class {
float: right;
}
}
There is no need to create your own class with media queries. Bootstrap 3 already has float ordering for media breakpoints under Column Ordering.
The syntax for the class is col-<#grid-size>-(push|pull)-<#cols> where <#grid-size> is xs, sm, md or lg and <#cols> is how far you want the column to move for that grid size. Push or pull is left or right of course.
I use it all the time so I know it works well.
Possibly you can use column ordering.
<div class="row">
<div class="col-md-9 col-md-push-3">.col-md-9 .col-md-push-3</div>
<div class="col-md-3 col-md-pull-9">.col-md-3 .col-md-pull-9</div>
</div>
Looks like floating columns will be getting added to version 4 by like #Alex has done - https://github.com/twbs/bootstrap/issues/13690
Yes. Create your own style. I don’t know what element you’re trying to float left/right, but create an application.css file and create a CSS class for it:
/* default, mobile-first styles */
.logo {
float: left;
}
/* tablets and upwards */
#media (min-width: 768px) {
.logo {
float: right;
}
}
Don’t be afraid to write custom CSS. Bootstrap is meant to be exactly that: a bootstrap, a starter point.
This is what i am using . change #screen-xs-max for other sizes
/* Pull left in mobile resolutions */
#media (max-width: #screen-xs-max) {
.pull-xs-right {
float: right !important;
}
.pull-xs-left {
float: left !important;
}
.radio-inline.pull-xs-left + .radio-inline.pull-xs-left ,
.checkbox-inline.pull-xs-left + .checkbox-inline.pull-xs-left {
margin-left: 0;
}
.radio-inline.pull-xs-left, .checkbox-inline.pull-xs-left{
margin-right: 10px;
}
}
LESS version of #Alex's answer
#media (max-width: #screen-xs-max) {
.pull-xs-left {
.pull-left();
}
.pull-xs-right {
.pull-right();
}
}
#media (min-width: #screen-sm-min) and (max-width: #screen-sm-max) {
.pull-sm-left {
.pull-left();
}
.pull-sm-right {
.pull-right();
}
}
#media (min-width: #screen-md-min) and (max-width: #screen-md-max) {
.pull-md-left {
.pull-left();
}
.pull-md-right {
.pull-right();
}
}
#media (min-width: #screen-lg-min) {
.pull-lg-left {
.pull-left();
}
.pull-lg-right {
.pull-right();
}
}