SASS and data attribute multiple - css

I have a problem with the nesting of SAS to make multiple selections, nose much about it, I hope you can help me and understand (because I do not write very good English).
SASS mixin:
#mixin data($x) {
$sel: &;
$collector: ();
#for $i from 1 through length($sel) {
$s: nth($sel, $i);
$last: nth($s, -1);
#if str-slice($last, -1) == "]" {
// if is just the bare attribute with no value, $offset will be -1, otherwise it will be -2
$offset: -1;
$current-x: $x;
#if str-slice($last, -2) == '"]' {
// this attribute already has a value, so we need to adjust the offset
$offset: -2;
} #else {
// no attribute value, so add the equals and quotes
$current-x: '="' + $x + '"';
}
$last: str-slice($last, 1, $offset - 1) + $current-x + str-slice($last, $offset);
$collector: append($collector, set-nth($s, -1, $last), comma);
} #else {
// following line will append $x to your non-attribute selector
$collector: append($collector, selector-append($s, $x), comma);
// the following line will not change your non-attribute selector at all
//$collector: append($collector, $s, comma);
}
}
#at-root #{$collector} {
#content;
}
}
SASS:
[data-content] {
#include data("content") {
background: black;
}
}
Output:
[data-content="content"] {
background: black;
}
The problem is I can not nest more than one item, for example does not work:
[data-content] {
#include data("content", "menu") {
background: black;
}
}
Output:
[data-content="content"],
[data-content="menu"] {
background: black;
}
Any way to solve?

You can always do something like this if you don't mind having to specify your selectors instead of passing them through as variables.
[data-content="content"], [data-content="menu"]{
#include data() {
background: black;
}
}

Related

How to insert an ampersand conditionally in a SASS mixin?

Below is some sass Im trying out, The css generated is correct, Im just trying to reduce my mixin duplication. The two mixins genProps and genPropsAmp are exactly the same except that the Amp mixin uses the & to prefix the rule with &. I tried to just add another var to the original mixin $amp: false and prefix my rule with an if so it became #if $amp {&}.#{$class}-... but that didnt work nor did trying to have it as an interpolation. Is there away of conditionaly placing the ampersand so I can just reuse the one mixin?
SASS:
$max_size: 0;
$sub_suffixes: '', 'q', 'h', 't';
$sub_divs: '', '.25', '.5', '.75';
#mixin genProps($class, $props, $val_prefix: r, $max_size: $max_size, $sub_suffixes: $sub_suffixes) {
#for $i from 0 through $max_size {
#each $sub_suffix in $sub_suffixes {
.#{$class}-#{$i}#{$sub_suffix} {
#each $prop in $props {
#{$prop}: var(--#{$val_prefix}-#{$i}#{$sub_suffix});
}
}
}
}
}
#mixin genPropsAmp($class, $props, $val_prefix: r, $max_size: $max_size, $sub_suffixes: $sub_suffixes) {
#for $i from 0 through $max_size {
#each $sub_suffix in $sub_suffixes {
&.#{$class}-#{$i}#{$sub_suffix} {
#each $prop in $props {
#{$prop}: var(--#{$val_prefix}-#{$i}#{$sub_suffix});
}
}
}
}
}
.root{
#include genProps(f, font-size, a);
#include genPropsAmp(m, margin);
}
CSS:
/* without amp */
.root .f-0 {font-size: var(--a-0);}
.root .f-0q {font-size: var(--a-0q);}
/*...*/
/* with amp */
.root.m-0 {margin: var(--r-0);}
.root.m-0q {margin: var(--r-0q);}
/*...*/
You can use a ternary operator: if($amp, "&", "")
#mixin genProps($class, $props, $amp, $val_prefix: r, $max_size: $max_size, $sub_suffixes: $sub_suffixes) {
#for $i from 0 through $max_size {
#each $sub_suffix in $sub_suffixes {
#{if($amp, "&", "")}.#{$class}-#{$i}#{$sub_suffix} {
#each $prop in $props {
#{$prop}: var(--#{$val_prefix}-#{$i}#{$sub_suffix});
}
}
}
}
}
#mixin genProps($class, $props, $amp: false, $val_prefix: r, $max_size: $max_size, $sub_suffixes: $sub_suffixes) {
#for $i from 0 through $max_size {
#each $sub_suffix in $sub_suffixes {
#if $amp {
&.#{$class}-#{$i}#{$sub_suffix} {
#each $prop in $props {
#{$prop}: var(--#{$val_prefix}-#{$i}#{$sub_suffix});
}
}
} #else {
.#{$class}-#{$i}#{$sub_suffix} {
#each $prop in $props {
#{$prop}: var(--#{$val_prefix}-#{$i}#{$sub_suffix});
}
}
}
}
}
}
this is the best Iv got so far, not great as theres still a lot of duplication in the if elses but at least I only have one mixin now

BEM generator mixin want to match MDL structure

So I have the following mixins to generate my BEM classes:
$es: '__';
$ms: '--';
#function to-string($selector) {
$selector: inspect($selector); //cast to string
$selector: str-slice($selector, 2, -2); //remove brackets
#return $selector;
}
#function contains-modifier($selector) {
$selector: to-string($selector);
#if str-index($selector, $ms) {
#return true;
} #else {
#return false;
}
}
#function get-block($selector) {
$selector: to-string($selector);
$modifier-start: str-index($selector, $ms) - 1;
#return str-slice($selector, 0, $modifier-start);
}
#mixin blck($block) {
.#{$block} {
#content;
}
}
#mixin elem($element) {
$selector: &;
#if contains-modifier($selector) {
$block: get-block($selector);
#at-root {
#{$selector} {
#{$block+$es+$element} {
#content;
}
}
}
} #else {
#at-root {
#{$selector+$es+$element} {
#content;
}
}
}
}
#mixin modf($modifier) {
#at-root {
#{&}#{$ms+$modifier} {
#content;
}
}
}
#include blck(block) {
background: red;
#include elem(child){
color: blue;
};
#include modf(modifier) {
background: blue;
#include elem(child) {
color: red;
}
}
}
Now this actually generates perfect BEM style code but I want to to match the MDL code structure which means I want to more specificity nest my modifiers form this
.block
.block--modifer
to
.bock.block--modifier
The reason for this as said before is to match MDL an example of this formatting can be seen here: https://github.com/google/material-design-lite/blob/master/src/card/_card.scss
Now I can almost get the desired effect by changing this line:
#mixin modf($modifier) {
#at-root {
#{&}#{$ms+$modifier} {
#content;
}
}
}
To this:
#mixin modf($modifier) {
#at-root {
#{&}#{&}#{$ms+$modifier} {
#content;
}
}
}
But that changes the CSS output from this:
.block {
background: red;
}
.block__child {
color: blue;
}
.block--modifier {
background: blue;
}
.block--modifier .block__child {
color: red;
}
To this:
.block {
background: red;
}
.block__child {
color: blue;
}
.block.block--modifier {
background: blue;
}
.block.block--modifier .block.block__child {
color: red;
}
Now as you can see this fixes the modifier specificity but breaks the modifier child.
The desired output is as follows:
.block.block--modifier .block__child {
color: red;
}
You can see it all in action here: http://codepen.io/crashy/pen/wGWPvr
Here is something quick i came up with by forking your pen:
Add the #{$selector} in modf mixin.
Modify the get-block function so that it returns the actual base class. At first we slice the string up to the point of the modifier (--) and then we check if there are more than one classes in the concatenated string. If there are more than one, we get the first.
Link from codepen: http://codepen.io/MKallivokas/pen/bpBYEg
$es: '__'; // Element
$ms: '--'; // Modifier
$ns: 'ns'; // Name space (Set to null if un-wanted)
#if($ns) {
$ns: $ns + '-';
}
#function to-string($selector) {
$selector: inspect($selector); //cast to string
$selector: str-slice($selector, 2, -2); //remove brackets
#return $selector;
}
#function contains-modifier($selector) {
$selector: to-string($selector);
#if str-index($selector, $ms) {
#return true;
} #else {
#return false;
}
}
#function get-block($selector) {
// do what get-block was doing in order to
// remove the modifier's part from the string
$selector: to-string($selector);
$modifier-start: str-index($selector, $ms) - 1;
$selector: str-slice($selector, 0, $modifier-start);
// remove the first dot
$selector: str-slice($selector, 2, str-length($selector));
// check if there is another dot
$modifier-start: str-index($selector, '.') - 1;
#if $modifier-start >= 0 {
// if there's another dot we slice the string up to that point
$selector: str-slice($selector, 0, $modifier-start);
}
// we insert the dot that we removed to the start
$selector: str-insert($selector, '.', 1);
#return $selector;
}
#mixin blck($block) {
.#{$ns}#{$block} {
#content;
}
}
#mixin elem($element) {
$selector: &;
#if contains-modifier($selector) {
$block: get-block($selector);
#at-root {
#{$selector} {
#{$block+$es+$element} {
#content;
}
}
}
} #else {
#at-root {
#{$selector+$es+$element} {
#content;
}
}
}
}
#mixin modf($modifier) {
$selector: &;
#at-root {
#{$selector}#{$selector+$ms+$modifier} {
#content;
}
}
}
#include blck(block) {
background: red;
#include elem(child){
color: blue;
};
#include modf(modifier) {
background: blue;
#include elem(child) {
color: red;
}
}
}
I don't know if it suits exactly your needs or covers every case of what you want to do but i hope it helps.

Create a recursion mixin scss

I am trying to create a recursion #mixin. The mixin has the task to append a selector from a given list and apply a CSS rules. The result of this has to look like this:
[data-tag]:not([conref]) {
border: dashed 2px #2999d1;
}
[data-tag]:not([conref])[nodeid] {
border: dashed 2px #2999d1;
}
[data-tag]:not([conref])[nodeid][draggable] {
border: dashed 2px #2999d1;
}
[data-tag]:not([conref])[nodeid][draggable][class] {
border: dashed 2px #2999d1;
}
The #mixin which I have created looks like this:
#mixin set-border-to-selectors($list, $item){
#if false == (index($list, $item)){
#error "Fail: #{$item} not in list: #{$list}";
}
#else{
$index: index($list, $item);
#debug "index: #{$index}";
$item: nth($list, $index);
&[#{$item}]{
#include set-data-tag-border;
#if($index + 1 <= length($list)){
$item: nth($list, $index + 1);
#include set-border-to-selectors($list, $item);
}
}
}
}
The first time I use the function it works pretty well.
#mixin show-data-tag-border{
$data-tag-list: (#{$id}, draggable, class);
$data-tag-list-two: (#{$id}, #{$conref}, class);
&[data-tag]:not([#{$conref}]){
#include set-data-tag-border;
#include set-border-to-selectors($data-tag-list, #{$id});
}
&[data-tag]{
#include set-border-to-selectors($data-tag-list-two, #{$id});
}
But the second call of the set-border-to-selector function throws an error, because the $index in the recusion #mixin does not contain a number. It is empty.
I call the function from p{#include show-data-tag-border;} and the border will be set in #mixin set-data-tag-border{border: solid 1px black}. And this two variables:
$id: id;
$conref: conref;
Is there something wrong what I do not see or is this approach wrong to create an recursion in SCSS? I won't use this function only one time and write one or more similar function below to accomplish this.
I'm not gonna try to repair your code, as it's too big. Recursion should work just fine like this:
#mixin set-borders ($list, $index: 1) {
#if $index <= length($list) {
#if $index == 1 {
&#{nth($list, $index)} {
#include set-borders($list, $index + 1);
}
} #else {
&,
&#{nth($list, $index)} {
#include set-borders($list, $index + 1);
}
}
} #else {
// ... (set the required properties)
}
}
The problem was the declaration of the list elements. I have to make tick marks.
So instead of this:
$data-tag-list: (#{$id}, draggable, class);
$data-tag-list-two: (#{$id}, #{$conref}, class);
I have to enclose every element in the list with tick marks:
$data-tag-list: ('#{$id}', 'draggable', 'class');
$data-tag-list-two: ('#{$id}', '#{$conref}', 'class');
and it works!
Thank you hon2a for your help!
Here is the full code for check up:
$id: id;
$conref: conref;
#mixin set-data-tag-border{border: solid 1px black}
#mixin set-border-to-selectors($list, $item){
#if false == (index($list, $item)){
#error "Fail: #{$item} not in list: #{$list}";
}
#else{
$index: index($list, $item);
#debug "index: #{$index}";
$item: nth($list, $index);
&[#{$item}]{
#include set-data-tag-border;
#if($index + 1 <= length($list)){
$item: nth($list, $index + 1);
#include set-border-to-selectors($list, $item);
}
}
}
}
#mixin show-data-tag-border{
$data-tag-list: ('#{$id}', 'draggable', 'class');
$data-tag-list-two: ('#{$id}', '#{$conref}', 'class');
&[data-tag]:not([#{$conref}]){
#include set-data-tag-border;
#include set-border-to-selectors($data-tag-list, #{$id});
}
&[data-tag]{
#include set-border-to-selectors($data-tag-list-two, #{$id});
}
}
p{#include show-data-tag-border;}

Sass: alternative variable in less [duplicate]

I'm trying to loop through a list of values in Sass and use interpolation of the current key to dynamically output class names that utilize #include and #extend, respectively.
Here is a pen showing the problem, simplified. http://codepen.io/ghepting/pen/vBmLy
As you can see in the markup, I have tried including the "_" inside of the interpolated string as well as outside of it. Is there something I'm missing to work around this limitation of how Sass supports interpolation?
(Note: the OP's pen has disappeared. This is not the original code found in the pen, but a rough approximation of the problem)
$error-light: red;
$error-dark: darken(red, 10%);
$success-light: green;
$success-dark: darken(green, 10%);
$dialogs: error, success;
#each $d in $dialogs {
.#{$d} {
background: $#{$d}-light;
}
}
Interpolation doesn't work on mixins or variables at this point in time. You'll have to come up with a different way to achieve your goal.
As of Sass 3.3, you can use mappings for this purpose for variables:
$dialogs:
( error:
( light: red
, dark: darken(red, 10%)
)
, success:
( light: green
, dark: darken(green, 10%)
)
);
#each $name, $colors in $dialogs {
.#{$name} {
color: map-get($colors, dark);
}
}
And for functions:
#function green() {
#return lighten(green, 10%);
}
#function red() {
#return lighten(red, 10%);
}
#mixin my-bg($function-name) {
background: call($function-name);
}
.foo {
#include my-bg('red');
}
Alternative workaround (for a particular use case):
https://sass-lang.com/documentation/at-rules/mixin#passing-arbitrary-arguments
💡 Fun fact:
Because an argument list keeps track of both positional and keyword arguments, you use it to pass both at once to another mixin. That makes it super easy to define an alias for a mixin!
If you are interested in mixin interpolation because you have a group of mixins, like this:
//_mixins.scss
#mixin text-style-1($args...){ //sass here }
#mixin text-style-2($args...){ //sass here }
#mixin text-style-3($args...){ //sass here }
//_text.scss
.text-style-1 {
#include text-style-1;
}
.text-style-1-contrast {
#include text-style-1($contrast: true);
}
.text-style-2 {
#include text-style-2;
}
.text-style-2-contrast {
#include text-style-2($contrast: true);
}
We can take advantage of passing arbitrary arguments and use an alias for the group:
//_mixins.scss
#mixin text-style-1($args...){ //sass here }
#mixin text-style-2($args...){ //sass here }
#mixin text-style-3($args...){ //sass here }
#mixin text($mixin, $args...) {
#if $mixin == 'style-1' { #include text-style-1($args...); }
#else if $mixin == 'style-2' { #include text-style-2($args...); }
#else if $mixin == 'style-3' { #include text-style-3($args...); }
}
//_text.scss
$text-styles: 'style-1', 'style-2', 'style-3';
#each $style in $text-styles {
.text-#{$style} {
#include text($style);
}
.text-#{$style}-contrast {
#include text($style, $contrast: true);
}
}
Ran into this issue of trying to include an interpolated variable inside a mixin and was able to resolve it with placeholders:
%color-scheme-dark-bg-1 { background-color: #4e5163; }
%color-scheme-dark-color-1 { color: #4e5163 !important; }
%color-scheme-light-bg-1 { background-color: #c7c8ce; }
%color-scheme-dark-bg-2 { background-color: #fd6839; }
%color-scheme-dark-color-2 { color: #fd6839 !important; }
%color-scheme-light-bg-2 { background-color: #fecfc1; }
.card_color {
#mixin CardColorScheme($arg: 1) {
.borderPercent {
#extend %color-scheme-dark-bg-#{$arg};
}
.border {
#extend %color-scheme-light-bg-#{$arg};
}
ul li:before {
#extend %color-scheme-dark-color-#{$arg};
}
.percent {
#extend %color-scheme-dark-color-#{$arg};
}
.heading {
#extend %color-scheme-dark-color-#{$arg};
}
}
&--scheme {
&-1 {
#include CardColorScheme(1);
}
&-2 {
#include CardColorScheme(2);
}
}
}
Hat tip to: https://krasimirtsonev.com/blog/article/SASS-interpolation-in-a-name-of-variable-nest-variables-within-variables

SASS :not selector

I have a :not css selector in SASS mixin but it doesn't do anything:
Code Snippet:
#mixin dropdown-pos($pos:right) {
&:not(.notip) {
#if $comp-tip == true{
#if $pos == right {
top:$dropdown-width * -0.6;
#include tip($pos:$pos);
}
}
}
&.notip {
#if $pos == right {
top: 0;
left:$dropdown-width * 0.8;
}
}
}
The .notip class is being generated but no CSS is being generated for :not(.notip).
I tried re-creating this, and .someclass.notip was being generated for me but .someclass:not(.notip) was not, for as long as I did not have the #mixin tip() defined. Once I had that, it all worked.
http://sassmeister.com/gist/9775949
$dropdown-width: 100px;
$comp-tip: true;
#mixin tip($pos:right) {
}
#mixin dropdown-pos($pos:right) {
&:not(.notip) {
#if $comp-tip == true{
#if $pos == right {
top:$dropdown-width * -0.6;
background-color: #f00;
#include tip($pos:$pos);
}
}
}
&.notip {
#if $pos == right {
top: 0;
left:$dropdown-width * 0.8;
background-color: #00f;
}
}
}
.someclass { #include dropdown-pos(); }
EDIT: http://sassmeister.com/ is a good place to debug your SASS because it gives you error messages. Undefined mixin 'tip'. it what I get when I remove #mixin tip($pos:right) { }

Resources