SCSS How to change order of rendering included mixins - sass

This is a hard question, so I am aware that no one may come up with solution, but that's the problem I really need to solve in my framework.
I have a screen() mixin written in SCSS, which takes $size as an argument, to return any #content wrapped in a media query.
The problem occurs when one element #includes multiple screen() mixins, because resulting media queries will overwrite each other in the same order as they were included. How can I make sure the resulting media queries will be rendered in the correct order (biggest screen to smallest), even if I forget to include them in the right order?
http://sassmeister.com/gist/951520fa83d1e1c69c9d
#mixin screen(
$size: null
){
#if $size == md {
#media (max-width: 1024px) {
#content;
}
}
#if $size == sm {
#media (max-width: 768px) {
#content;
}
}
#if $size == xs {
#media (max-width: 320px) {
#content;
}
}
}
/* output should be 1024, 768, 320 */
.screen {
&:before {
// this should be included as the Last one
#include screen(xs){
content: "xs";
}
#include screen(sm){
content: "sm";
}
// this should be included as the First one
#include screen(md){
content: "md";
}
}
}
I tried to solve that issue by creating placeholder selectors in the right order %media-sm{...}, %media-xs {...}..., and #extend them from the mixin, but #content can't be passed through the #extend directive.
Another solution is a hard one - create an array of keys - sizes, and values - #contents and render them from another function.

No. Sass only does exactly what you tell it to do. If you want your styles to appear in a specific order, write them in that specific order.

Might be easier to pass in the media width you are trying to target:
#mixin media($width) {
#media only screen and (max-width: $width) {
#content;
}
}
#include media(320px) {
background: red;
}

Related

SCSS Placeholder and Mixin to generate

I want to generate multiple CSS-Files
class.css
Should contain from an configuration file (tailwindcss-like) generated utility-first CSS-Classes
.block {display:block; }
.text-center { text-align:center; }
#media (min-width: 768px) {
.md:text-center {
text-align:center;
}
}
components.css
Should contain component-based CSS-Classes
// Input
.avatar { #extend block, background($blue)}
// Output
.avatar { display: block; background-color: blue;}
Question
Is there a way to generate these separated files? I have no idea!
My way to first generate #mixins (or %placeholders, I tried both ways) from a config-file (colors, spacing,...) and afterwards in an second step, building my components.css from these #mixins is not very maintainable

Is it possible to create functions (or something else) that include selectors with SASS?

I want to be able to write:
div {
background-size: 100%;
#bgimgfunction('img1.png');
}
and have SASS produce something like:
div {
background-size: 100%;
/* Generated by the call to #bgimgfunction */
background-image:('/img/img1-medium.png');
#media (max-width: 640px) {
background-image:('/img/img1-low.png');
}
#media (min-width: 1600px) {
background-image:('/img/img1-high.png');
}
/* End generated by the call to #bgimgfunction */
}
mixins I think don't work because I can't pass a parameter
functions I think don't work because they are only valid after a selector.
Is there a way to do this?
What you need is a #mixin which does take parameters. Given the structure of your image URLs, I think you need two arguments, one for the image name and one for its extension:
#mixin bgImageFunction($imageName, $imageExt) {
$path: '/img/' + $imageName;
background-image: url("#{$path}-medium.#{$imageExt}");
#media (max-width: 640px) {
background-image: url("#{$path}-low.#{$imageExt}");
}
#media (min-width: 1600px) {
background-image: url("#{$path}-high.#{$imageExt}");
}
}
div {
background-size: 100%;
#include bgImageFunction('img1', 'png');
}
You can also use a default parameter for the extension and only pass the name as argument:
#mixin bgImageFunction($imageName, $imageExt: 'png') {
...
}

How to add "orientation media query" to bootstrap 4 sass media query

i am using the "vanilla" bootstrap 4 sass media query in my scss files:
#include media-breakpoint-up(xs){}
#include media-breakpoint-up(sm){}
#include media-breakpoint-up(lg){}
#include media-breakpoint-up(xl){}
i know that if i use the css width media query i can couple it with the orientation media query, but i want to use the sass framework.
I want to add the orientation media query in on of them, the XS one. thus it is specific. Because as you know bootsrap 4 is not supporting orientation query for now (strangely).
i tried to concatenat the "orientation query" with the "SASS bootstrap media query (xs)" in different way but i always have a sass error.
Thus What i did is to nest it in the SASS bootstrap media query (xs):
#include media-breakpoint-up(xs){
... some SCSS rules
#media (orientation: landscape){
header{
display:none !important;
}
.navbar{
display:none !important;
}
}
}
The problem i have even tought it is nested into the XS query is that it apply to all breakpoint. it s like it does nt take into account to be nested.
My question: how to concatenate the "orientation query" with the "SASS bootstrap media query (xs)"? Or how to make it specific to the XS breakpoint by nesting it.
Thank you
I've found the solution.
It's possible to combine sass mixin by nesting them, thus I've created the following mixin in my _mixins.scss file:
#mixin orientation($direction) {
$orientation-landscape: "(orientation:landscape)";
$orientation-portrait: "(orientation:portrait)";
#if $direction == landscape {
#media #{$orientation-landscape} { #content; }
}
#if $direction == portrait {
#media #{$orientation-portrait} { #content; }
}
}
Note: i didn't put the "and" in the variable value: "and (orientation:landscape)". SASS or bootstrap put it automatically i suppose.
Then in my SCCS file I've added the following rules:
#include media-breakpoint-down(sm) {
#include orientation(landscape) {
.path-frontpage header {
display: none !important;
}
.path-frontpage .navbar {
display: none !important;
}
}
}
Note: in my first post i was saying that the CSS rules I've nested was applied to all breakpoints, it s because when the CSS is generated the SASS Bootstrap 4 XS breakpoint of is not written, i suppose it's because the value is 0. thus the orientation media query was not combines with a min-width value. So i changed the value to a max-width instead of a min-width, as the Bootstrap 4 SM breakpoint have the 576px value.
The result in the CSS file is what i wanted:
#media (max-width: 767.98px) and (orientation: landscape) {
.path-frontpage header {
display: none !important;
}
.path-frontpage .navbar {
display: none !important;
}
}
I hope it will help the community.
I use this outside of Bootstrap. You should be able to use it with Bootstrap or any other framework, giving you more flexibility in your media queries.
// Extra map functions by Hugo Giraudel
#function map-deep-get($map, $keys...) {
#each $key in $keys {
$map: map-get($map, $key);
}
#return $map;
}
#function map-has-keys($map, $keys...) {
#each $key in $keys {
#if not map-has-key($map, $key) {
#return false;
}
}
#return true;
}
#function map-has-nested-keys($map, $keys...) {
#each $key in $keys {
#if not map-has-key($map, $key) {
#return false;
}
$map: map-get($map, $key);
}
#return true;
}
These are extra map functions Hugo Giraudel wrote up. map-deep-get is basically a simplified nested map-get function. map-has-keys is just like map-has-key, which is built-in to sass, but checks for multiple keys. map-has-nested-keys expands on that by checking for nested keys. This is crucial for this method. I'd definitely look into the extra Sass functions he's built. I've quite easily found use for just about all of them.
// Map
$sizes: (
null: (
breakpoint: 0,
container: 100%
),
xs: (
breakpoint: 480px,
container: 464px
),
sm: (
breakpoint: 768px,
container: 750px
),
md: (
breakpoint: 992px,
container: 970px
),
lg: (
breakpoint: 1200px,
container: 1170px
)
);
This is a simple breakpoint map. I usually use this as a base map for all settings on my projects, so I'll include things like base font-sizes and whatnot in it.
// Breakpoint mixin
#mixin break($screen-min: null, $screen-max: null, $orientation: null) {
$min: $screen-min;
$max: $screen-max;
$o: $orientation;
$query: unquote("only screen");
#if $min != null and $min != "" {
#if map-has-nested-keys($base, sizes, $screen-min) {
$min: map-deep-get($base, sizes, $screen-min, breakpoint);
}
#else {
$min: $screen-min;
}
#if is-number($min) {
$query: append($query, unquote("and (min-width: #{$min})"));
}
}
#if $max != null and $max != "" {
#if map-has-nested-keys($base, sizes, $screen-max) {
$max: map-deep-get($base, sizes, $screen-max, breakpoint);
}
#else {
$max: $screen-max;
}
#if is-number($max) {
$query: append($query, unquote("and (max-width: #{$max})"));
}
}
#if $orientation == landscape or $orientation == portrait {
$o: $orientation;
$query: append($query, unquote("and (orientation: #{$o})"));
}
#else {
$o: null;
}
#media #{$query} {
#content;
}
};
Here's the mixin. You can use the keys from the sizes map (xs, sm, md, lg) for the first two arguments, or you can use custom values (like 30em). The third argument accepts either landscape or portrait. You could even customize the mixin the make l = landscape and p = portrait if you wanted.
Additionally, if you only wanted, for example, an orientation, you could pass the arguments (null, null, landscape).
For clarity, here's some examples:
#include break(null, md, landscape) {
...
}
#include break(null, null, landscape) {
...
}
#include break(md) {
...
}
#include break(null, md) {
...
}
#include break(480px) {
...
}
Output:
#media only screen and (max-width: 992px) and (orientation: landscape) {
...
}
#media only screen and (orientation: landscape) {
...
}
#media only screen and (min-width: 992px) {
...
}
#media only screen and (max-width: 992px) {
...
}
#media only screen and (min-width: 480px) {
...
}

Bourbon Neat grid-push not working as expected?

I have a really simple issue that seems to be difficult to solve. I want to stick with the normal standard 12 column neat grid, but I want the two .homeSplit divs to take up 5 columns each. I would like the second one(.insights) to get 1col of space in the middle so I gave it a grid-push(1). When it gets to mobile sizes, I want each of these divs to take up the full 12 columns and stack on top of each other. The issue is that once I get down to mobile size, that 1col space I assigned with grid-push(1) is persisting and I can't figure out how to get rid of it.
CSS/SASS:
.homeSplit {
position: relative;
#include grid-column(5);
&.news {
.post {
margin-bottom: 26px;
}
}
&.insights {
#include grid-push(1);
padding-left: 30px;
z-index: 999;
.post {
margin-bottom: 26px;
}
}
}
#media only screen and (max-width: 959px) {
.homeSplit {
#include grid-column(12);
&.insights {
#include grid-push(0);
}
}
}
I have tried grid-push(-1) as well but it goes too far. Am I misunderstanding how to use Neat? Pulling my hair out over here.
The best path here would be to use the grid-media() mixin to have a series of grids. Here is an example of what that would look like (with some of the extraneous code removed.
Also, neat by default favors min-width over max-width in media queries. based on your layout, min-width makes things a lot easier.
$my-desktop-grid: (
media: 959px,
);
.homeSplit {
#include grid-column(); // default's to 12 here
#include grid-media($my-desktop-grid) {
#include grid-column(5);
&.insights {
#include grid-push(1);
}
}
}
I've created a codepen as an example so you can see what this looks like in action.
https://codepen.io/whmii/pen/aVvqma
Option 1: the nesting is wrong.
In looking at the example above the #media declaration is nested within the .homeSplit block, but then .homeSplit is declared again w/in #media. However the code you have above would likely not run and would error out, so I'm going to assume there was something lost in translation when it was copped and pasted in to your question.
Option 2: grid-push wants false or just to be empty.
grid-push(0) isn't really a thing but in sass 0 may == false so you can try the following:
.homeSplit {
position: relative;
#include grid-column(5);
&.news {
.post {
margin-bottom: 26px;
}
}
&.insights {
#include grid-push(1);
padding-left: 30px;
z-index: 999;
.post {
margin-bottom: 26px;
}
}
#media only screen and (max-width: 959px) {
#include grid-column(12);
&.insights {
#include grid-push(); // 'false' is the default here
}
}
}
Note: I also cleaned up some of the nesting at the bottom
Im going to add a second answer that shows how to do this using the grid-media mixin.

Passing arguments from a mixin to a content block

This open issue in the Sass queue seems to imply passing arguments to #content is not a feature yet, but Susy 2 seems to be able to do this. Tracking down how it's done is a bit of a rabbit hole though and I haven't figured it out yet. Perhaps someone can shed some light with a straightforward example? I want to create a custom mixin that will inherit a layout passed from susy-breakpoint() using a custom map.
Example: Defining a 4 column layout in a global Sass map, will return a width of 100% when a span of 4 is specified inside susy-breakpoint()'s #content. When a custom layout of 8 cols is passed to directly tosusy-breakpoint() via the $layout argument, the nested span() mixin picks up the new layout. But a custom nested mixin will not pick up the new layout. Why?
#import 'susy';
$susy: (
columns: 4,
);
#mixin inherit-layout($layout: 4) {
columns: $layout;
}
#include susy-breakpoint(30em) {
// nested code uses an 4-column grid from global map
.global-cols {
#include span(4);
#include inherit-layout();
}
}
#include susy-breakpoint(48em, $layout: 8) {
// nested code uses an 8-column grid from $layout
.inherited-cols {
#include span(4);
#include inherit-layout();
}
}
Compiled CSS:
#media (min-width: 30em) {
.global-cols {
width: 100%;
float: left;
margin-left: 0;
margin-right: 0;
columns: 4;
}
}
#media (min-width: 48em) {
.inherited-cols {
width: 48.71795%;
float: left;
margin-right: 2.5641%;
columns: 4;
}
}
Update:
I've discovered that making the default variable for the inherit-value() mixin the value of columns key on the existing $susy map allows the mixin to grab context. But why? And why doesn't it work with a different map or outside of susy-breakpoint()?
See here: http://sassmeister.com/gist/d86e217aca3aa8337b83
Susy doesn't pass any arguments to the #content — instead, we change the global variable at the start of the content block, and then change it back at the end:
$example: 4;
#mixin local($local) {
$old: $example;
$example: $local !global;
#content
$example: $old !global;
}
// out here, $example == 4
#include local(12) {
// in here, $example == 12
}

Resources