I have this Mixin for padding utility:
Sass code:
$padding: (
top: "top",
right: "right",
bottom: "bottom",
left: "left",
all: "all"
);
#mixin no-padding($map) {
#each $padding-side, $side in $map {
#if $side == 'all' {
& {
padding: 0 !important;
}
} #else {
&-#{$side} {
padding-#{$side}: 0 !important;
}
}
}
}
Use of it:
.u-noPadding {
#include no-padding($padding);
}
I want to use the same Mixin but now for margin, is there any solution to avoid repeating the same mixin and make a good use of best practices?
#mixin no($type,$sides:null) {
$i:0 !important;
#if $sides == null {
#{$type}:$i;
} #else {
#each $side in $sides {
#{$type}-#{$side}:$i;
}
}
}
.u-noPadding {
#include no(padding, top left etc...); // choose any side separated with a space
}
.u-noMargin {
#include no(margin); // instead of 'all', type nothing
}
Like this? Your $sides will be stored in a temporary map automatically if your second parameter is set, no need extra map for this.
About the second parameter: If you want no sides, let it empty and all sides will have 0. Similiar to your 'all' idea.. it's shorter.
Related
This SASS code...
#mixin test
{
#at-root #{selector-replace(&, '.class1', '.class1:nth-child(odd)')}
{
color:red;
}
}
.class1
{
.class2
{
#include test;
}
}
...compiles to:
.class1:nth-child(odd) .class2
{
color: red;
}
Is this possible when not using selector-replace (because I don't know how class1 is called)?
I just want to add a nth-child selector to the grandparent.
I am only allowed to change the mixin, not the original code.
Ok, this will do the trick:
#mixin test
{
// Find the first selector
$parent : nth(nth(&, 1), 1);
// Defines a list for the rest of the selectors
$rest : ();
// For each selector of &, starting from the second
#for $i from 2 through length(nth(&, 1)) {
// Adds the selector to the list of the "rest of the selectors"
$rest: append($rest, nth(nth(&, 1), $i));
}
// Adds the selector at root
#at-root #{$parent}:nth-child(odd) #{$rest} {
color: red;
}
}
.class1
{
.class2
{
#include test;
}
}
This compiles to:
.class1:nth-child(odd) .class2 {
color: red;
}
Hope it helps!
I am using Drupal FortyTwo theme. In the FortyTwo base-theme there is a flexbox mixin provided see below:
#mixin flex-order($number) {
order: #{$number};
}
#mixin flex-align($align) {
#if $align == 'start' or $align == 'end' {
align-items: flex-#{$align};
} #else {
align-items: #{$align};
}
}
#mixin flex-flow($direction: none, $wrap: none) {
#if $wrap != none {
flex-wrap: #{$wrap};
}
#if $direction != none {
flex-direction: #{$direction};
}
}
#mixin flex-grow($value) {
flex-grow: #{$value};
}
#mixin flex-shrink($value) {
flex-shrink: #{$value};
}
#mixin flex-child($value) {
flex: #{$value};
}
#mixin flex($wrap: none, $justify: none, $align: none, $flow: none, $direction: none, $inline: none) {
#if $inline != none {
display: inline-flex;
} #else {
display: flex;
}
#if $direction != none {
flex-direction: #{$direction};
}
#if $wrap != none {
flex-wrap: #{$wrap};
}
#if $align != none {
align-items: #{$align};
}
#if $justify != none {
justify-content: #{$justify};
}
}
I am updating the theme. I can't figure out how to use this mixin? In the old theme there is e.g. this part:
#my-block {
html.flexbox & {
#include flex;
#include bvp(flex-direction, column);
}
div.content {
html.flexbox & {
#include bvp(flex, 1);
}
position: relative;
}
}
Also I have to get rid of the bvp mixin. How do I add flexbox here the proper way using above flexbox mixins?
So as you saw, we have a series of mixins.
The last one should be be most helpful, but from playing around with it in Code Pen, I'm not convinced it actually works correctly. And some of the others aren't especially helpful.
For example, we can see that #mixin flex-order simply spits out the order flexbox property with whatever number we pass to it.
So this:
#my-box {
#include flex-order(2);
}
Outputs this:
#my-box {
order: 2;
}
Well unless you just want a visual reminder that order only relates to flex items, that's not exactly helping you much as you could have just as easily done order: 2 in your SCSS in the first place.
The same thing applies to the mixins flex-align, flex-grow, flex-shrink, and flex-child.
So being that the single-property mixins aren't super useful and the last mixin seems broken, I would recommend just specifying your flex properties as needed in your SCSS and maybe using the flex-flow mixin if you want.
#mixin flex-flow
The flex-flow flexbox property requires two values: one for flex wrapping, and one for flex direction. In the mixin, it outputs that shorthand property as two separate properties, or it outputs only the property you pass to it if you only pass one property, that way you don't end up with an invalid CSS rule.
So this:
#my-box {
#include flex-flow(wrap, column);
}
#my-other-box {
#include flex-flow(wrap);
}
Outputs this:
#my-box {
flex-wrap: wrap;
flex-direction: column;
}
#my-other-box {
flex-wrap: wrap;
}
That way you have two acceptable CSS rules. Otherwise if you actually used the flex-flow property, you'd get:
#my-box {
flex-flow: wrap column; /* invalid; flex-direction must come first */
}
#my-other-box {
flex-flow: wrap; /* invalid; missing flex-direction */
}
Final example
Your final SCSS could look something like this, after removing the bvp mixin and specifying the individual flex properties without mixins, as I initially recommended.
#my-block {
html.flexbox & {
#include flex-flow(row, wrap);
align-items: center;
justify-content: center;
}
div.content {
html.flexbox & {
flex: 1 0 auto;
}
position: relative;
}
}
I would like to specify an additional default shortcut class to a set of classes, similarly to that
#each $pos, $some-css-rules in ("left": ..., "right": ..., ...) {
#if $pos == "left" {
.block,
}
.block-#($pos) {
...
}
}
that would be outputted as
.block,
.block-left {
...
}
.block-right {
...
}
However, it will stumble over .block, syntax error.
.block-left cannot be replaced here with .block.left because $pos will collide with existing classes (.left, etc).
I would prefer to avoid .block { #extend .block-left } if possible, there is a considerable amount of similar rules that will gain a lot of WET code this way.
Is there a way to conditionally output a part of rule selector? How can both SCSS and CSS be kept DRY in a pattern like that?
I'm not sure if I understand the question but I achieve the output CSS based on your code. I put the #if directive inside the selector to compare with $pos variable. Here is my code:
SASS
#each $pos, $some-css-rules in ("left": red, "right": blue) {
.block-#{$pos} {
#if $pos == "left" {
#at-root .block, &{
color:$some-css-rules;
}
}
#else{
color:$some-css-rules;
}
}
}
Output
.block, .block-left {
color: red;
}
.block-right {
color: blue;
}
I'm trying to find a way of comparing the variable name e.g. $topLeft within the #each loop with a string which would be for instance 'topLeft' - an example would be:
#mixin getCorner($topLeft:false, $topRight:false, $bottomRight:false, $bottomLeft:false) {
#each $corner in $topLeft, $topRight, $bottomRight, $bottomLeft {
#if #{$corner} == topLeft {
border-top-left-radius: $corner;
}
}
}
The above obviously doesn't work, but is there a way of doing it in Sass?
If you use the name top-left instead of topLeft, you can reduce the amount of code you have to write.
Here I have a list which does not do EXACTLY what you want, but you can easily use this to go ahead and do the comparison you want to do.
$corners: (top-left, top-right, bottom-left, bottom-right);
#mixin getCorner($cornerName, $cornerVal) {
$max: length($corners);
#for $i from 1 through $max {
$temp: nth($corners, $i);
#if ($temp == $cornerName) {
border-#{$temp}-radius: $cornerVal;
}
}
}
body {
#include getCorner(top-left, 2px);
}
When you assign a variable, all the interpreter knows is the value it contains, not what its name is. So when you're looping over your values, $corner is getting set to one of the values in the list. It will never be topLeft unless you pass that as the value for the $topLeft argument, which is why your #if statement never evaluates to true.
If you use a default value of null instead of false, you can simplify a lot:
#mixin getCorner($topLeft: null, $topRight: null, $bottomRight: null, $bottomLeft: null) {
border-top-left-radius: $topLeft;
border-top-right-radius: $topRight;
border-bottom-right-radius: $bottomRight;
border-bottom-left-radius: $bottomLeft;
}
.foo {
#include getCorner($topLeft: 50%, $bottomRight: 50%);
}
Output:
.foo {
border-top-left-radius: 50%;
border-bottom-right-radius: 50%;
}
I'm trying to make a mixin that will let me create adapted blocks of code depending on what variable name you up in.
$foo: #00A9EC;
#mixin menu-color($color) {
.color-#{$color} a.level2,
.color-#{$color} a.level2:visited {
color: $color;
&:hover {
color: adjust-lightness($color, 10); }
&:active {
color: adjust-lightness($color, -10); } } }
#include menu-color($foo);
outputs:
.color-foo a.level2,
.color-foo a.level2:visited {
color: #00A9EC; }
.color-foo a.level2:hover,
.color-foo a.level2:visited:hover {
color: #20C0FF; }
.color-foo a.level2:active,
.color-foo a.level2:visited:active {
color: #0084B9; }
In sass you can do this using map, you just pass the variable name instead of the variable itself:
$colors: (
-black: #000,
-blue: #088DC6
);
#mixin generateBgColor($colorName) {
.bg-color#{$colorName} {
background-color: map-get($colors, $colorName);
}
}
#include generateBgColor("-blue");
This will generate class:
.bg-color-blue {
background-color: #088DC6;
}
You achieve this also in less with standard variables, just by using curly brackets and double at character:
#blue: #088DC6;
.generate-bg-color(#color) {
.bg-color-#{color} {
background-color: ##color;
}
}
.generate-bg-color(~"blue");
You should not name CSS classes after specific colors. You would regret that. Just think, if you want to make the color red later on, you would need to go back over all your html and change the classes.
The reason we have CSS is so that you don't have to embed style information in the markup.
Use a semantic class the describes the data, not how it is displayed:
$foo: #00A9EC;
#mixin menu-color($name, $color) {
.custom-#{$name} a.level2,
.custom-#{$name} a.level2:visited {
color: $color;
&:hover {
color: adjust-lightness($color, 10); }
&:active {
color: adjust-lightness($color, -10); } } }
#include menu-color(profile, $foo);
And then in your HTML <div class="custom-profile">.
That way, two years from now when you want to make it black, and underlined (or whatever), you don't have to dig through your html and add a new '.underlined-and-black-color` class to all of those elements. You just change your SCSS in one place.