What is the SASS equivalent of additive mixin definitions in LESS? - sass

Is there a way to have SASS emulate the way LESS concatenates repeated mixin definitions (redefining a mixin in LESS doesn't overwrite the original).
For instance, would it be possible to push a block of CSS rules into a buffer, and then flush them all at once?
Example:
With LESS I'd do this:
// _menu.less
#_base () { .menu{ /*styles*/ } }
#_mobile () { .menu{ /*styles*/ } }
#_desktop () { .menu{ /*styles*/ } }
...
// _widget.less
#_base () { .widget{ /*styles*/ } }
#_mobile () { .widget{ /*styles*/ } }
#_desktop () { .widget{ /*styles*/ } }
...
and then:
// styles.less
#import "_menu.less";
#import "_widget.less";
#media screen { #_base(); }
#media screen and (max-width:700px) { #_mobile(); }
#media screen and (min-width:701px) { #_desktop(); }
...
// styles-oldie.less
#import "_menu.less";
#import "_widget.less";
#media screen {
#_base();
#_desktop();
}

To the best of my knowledge, there is no way to replicate what you want to achieve by building on to existing mixins. If you define a mixin two times, the second will overwrite the first. See example
AFAIK the common practice in Sass is to use media query mixins inside the selector to keep the code clean and readable. Breakpoint is a popular library that adds a lot of nice functionality for doing this.
An example of the code would be.
#import "breakpoint";
$medium: 600px;
$large: 1000px;
$breakpoint-no-queries: false; // Set to true to ignore media query output
$breakpoint-no-query-fallbacks: true; // Set to true to output no-query fallbacks
$breakpoint-to-ems: true; // Change px to ems in media-queries
.menu {
content: "base";
// Mobile styles
#include breakpoint(min-width $medium - 1) {
content: "mobile";
}
// Tablet styles
#include breakpoint($medium $large) {
content: "tablet";
}
// Desktop styles with no-query fallback
#include breakpoint($large, $no-query: true) {
content: "large";
}
}
This could output (depending on your settings)
.menu {
content: "base";
content: "large";
}
#media (min-width: 37.4375em) {
.menu {
content: "mobile";
}
}
#media (min-width: 37.5em) and (max-width: 62.5em) {
.menu {
content: "tablet";
}
}
#media (min-width: 62.5em) {
.menu {
content: "large";
}
}
You can play around with the settings here
I often have a stylesheet for modern browsers that support media queries set up like this:
// main.scss
$breakpoint-no-queries: false;
$breakpoint-no-query-fallbacks: false;
#import "imports";
And another stylesheet for older browsers that don't support media queries
// no-mq.scss
$breakpoint-no-queries: true;
$breakpoint-no-query-fallbacks: true;
#import "imports";

Related

Using scss mixins with nextJs

I I've got the following next.config.js setup:
const path = require('path')
const withSass = require('#zeit/next-sass');
module.exports = withSass({
cssModules: true
})
module.exports = {
sassOptions: {
includePaths: [path.join(__dirname, 'styles')]
},
}
and I'm importing a global scss file using:
import '../styles/main.scss';
In this main.scss file, I'm using some mixins like:
#mixin wrapper() {
...
#media screen and ($max-hd) {
width: calc(100% - 6em);
}
#media screen and ($max-md) {
width: calc(100% - 2em);
}
}
where both max-hd and max-md are variables from another scss file:
$max-md: 'max-width: (#{$break-md - 1px})';
$max-hd: 'max-width: (#{$break-hd - 1px})';
If I use the variable ${max-hd} in as content in the same wrapper mixin, it prints the right value:
content: "max-width: (1739px)";
But the media queries seem to be completely ignored. I'm having a hard time debugging, as this is my first time with Next.js and I can't find the exported styles (google developer tools throws me back to the actual scss, which looks correct).
Does anyone have any idea of what I'm doing wrong?
This is how I use scss with my next js project:
Install sass
npm install --save-dev sass
(or npm i sass if you compile your code on the server).
I created a root directory named scss, in it I have my variables, mixins etc. For example:
breakpoints.scss
$screen-sm-min: 640px;
$screen-md-min: 768px;
$screen-lg-min: 1024px;
$screen-xl-min: 1280px;
#mixin sm {
#media (min-width: #{$screen-sm-min}) {
#content;
}
}
#mixin md {
#media (min-width: #{$screen-md-min}) {
#content;
}
}
#mixin lg {
#media (min-width: #{$screen-lg-min}) {
#content;
}
}
#mixin xl {
#media (min-width: #{$screen-xl-min}) {
#content;
}
}
Then I use scss modules like this:
MyComponent.module.scss:
#import '/scss/breakpoints';
.image {
position: relative;
#include lg {
position: fixed;
}
}
in my component, I import the styles and use them as described here: https://beta.nextjs.org/docs/styling/css-modules
There are more options here:
https://beta.nextjs.org/docs/styling/sass

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) {
...
}

SCSS How to change order of rendering included mixins

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;
}

Compass: separate sprite declaration for IE8

In my .scss I'm using CSS3 pseudo classes. For example :
.btn:disabled {
#include assets-sprites(btn_disabled);
}
.btn.disabled {
#include assets-sprites(btn_disabled);
}
Compass combine these two declaration into one :
.btn:disabled, .btn.disabled {
background: url("img/assets.png")
}
But Internet Explorer 8 doesn't read the declaration if a CSS3 pseudo class is present in the selector (like :disabled, :checked, :not, etc).
So how can I output this into something like that ?
.btn:disabled {
background: url("img/assets.png")
}
.btn.disabled {
background: url("img/assets.png")
}
Thanks :)
You can combine placeholders and a mixin to manage this with a DRY approach:
SCSS
#import "compass";
// Generate separate CSS3 pseudo-selector / fallback selector.
//
// #param string $selector
// The CSS3 selector name, without the first colon.
// #param string $sprite
// The sprite name without the file extension.
#mixin sprite-css3-pseudo($selector, $sprite, $map: $assets-sprites) {
// CSS3 selector
&:#{$selector} {
#extend %assets_css3-map;
#include sprite-background-position($map, $sprite);
}
// IE8 fallback
&.#{$selector} {
#extend %assets-map;
#include sprite-background-position($map, $sprite);
}
}
// $<map>-sprite-base-class to customize the base class
// used when we importing the sprite map.
$assets-sprite-base-class: '%assets-map';
// Compass generates the following rule:
// %assets-map {
// background: $assets-sprites no-repeat;
// }
#import "assets/*.png";
// We have to split the CSS3 selectors of the classic selectors (the
// fallback) so we need to declare a new placeholder with the same
// content generated by Compass for the base class.
%assets_css3-map {
background: $assets-sprites no-repeat;
}
.btn {
#include sprite-css3-pseudo('disabled', 'btn_disabled');
}
.fb {
#include sprite-css3-pseudo('checked', 'fb_icon');
}
.icon-alarm {
// We can still use the regular sprite generator
#include assets-sprite('alarm');
// And our mixin :)
#include sprite-css3-pseudo('disabled', 'alarm');
}
CSS
.btn.disabled, .fb.checked, .icon-alarm, .icon-alarm.disabled {
background: url('../images/assets-sacf5a47174.png') no-repeat;
}
.btn:disabled, .fb:checked, .icon-alarm:disabled {
background: url('../images/assets-sacf5a47174.png') no-repeat;
}
.btn:disabled {
background-position: 0 -224px;
}
.btn.disabled {
background-position: 0 -224px;
}
.fb:checked {
background-position: 0 -176px;
}
.fb.checked {
background-position: 0 -176px;
}
.icon-alarm {
background-position: 0 0;
}
.icon-alarm:disabled {
background-position: 0 0;
}
.icon-alarm.disabled {
background-position: 0 0;
}
Here'es the solution, thanks to #pascalduez:
$assets: sprite-map("assets/*.png");
.btn:disabled { background: sprite($assets, user); }
.btn.disabled { background: sprite($assets, user); }

Combining Sass mixin selectors [duplicate]

This question already has answers here:
Styling a specific set of input types in a reusable way with Sass
(2 answers)
Closed 7 years ago.
I'm trying to figure out if it's at all possible to combine mixin selector strings. I don't believe this is possible in the context of my code, but I could very well be missing something!
Let's say I have the following scss:
// Apply a set of rules to input form fields.
#mixin input-form-fields {
input:not([type="hidden"]),
textarea {
#content;
}
}
// Apply a set of rules to button form fields.
#mixin button-form-fields {
button, button {
#content;
}
}
// Apply a set of rules to select form fields.
#mixin select-form-fields {
select {
#content;
}
}
// Apply a set of rules to all form fields.
#mixin all-form-fields {
#include input-form-fields {
#content;
}
#include button-form-fields {
#content;
}
#include select-form-fields {
#content;
}
}
Basically the all-form-fields mixin will call other mixins, thus generating the same set of rules for different selectors.
If I compile the following code:
#include all-form-fields {
margin-bottom: .5em;
}
I would get something like:
input:not([type="hidden"]),
textarea {
margin-bottom: .5em;
}
button,
.button {
margin-bottom: .5em;
}
select {
margin-bottom: .5em;
}
This is not ideal, I would love it if I could combine those selectors.
Does anyone have any ideas on how I could possibly combine the selector strings returned by the 3 different mixins?
If you don't mind storing your selectors in strings, you could define different field types using variables:
$input-form-fields: "input:not([type=hidden]), textarea";
$button-form-fields: "button";
$select-form-fields: "select";
Your then define your mixins with interpolated strings like so:
// Apply a set of rules to input form fields.
#mixin input-form-fields {
#{$input-form-fields} {
#content;
}
}
// Apply a set of rules to button form fields.
#mixin button-form-fields {
#{$button-form-fields} {
#content;
}
}
// Apply a set of rules to select form fields.
#mixin select-form-fields {
#{$select-form-fields} {
#content;
}
}
// Apply a set of rules to all form fields.
#mixin all-form-fields {
#{$input-form-fields},
#{$button-form-fields},
#{$select-form-fields} {
#content;
}
}
As a result, #include all-form-fields will result in
input:not([type=hidden]), textarea,
button,
select {
margin-bottom: .5em; }

Resources