Calvin Bonner
Calvin Bonner

Reputation: 580

Can I list all of the values of a SASS Map/List in one line?

I'm trying to set up some login in my SCSS code to make a single class act differently depending on the type of element it's applied to. In this case, I want to make the class of shadow-#{$size} apply a text-shadow to text elements (as outlined in a Sass Map/List) and apply a box-shadow to everything else.

I currently have my SASS Map laid out like so:

$text-type-map: (
    h1,
    h2,
    h3,
    h4,
    h5,
    h6,
    p,
    a,
    span,
    ul,
    li
);

And I have my text-shadow applying correctly on all elements in that map with a simple @each loop:

@each $name, $size in $shadow-size-map {
    @each $type in $text-type-map {
        #{$type}.shadow-#{$name} {
            text-shadow: 0px #{$size} rgba($dark, 0.5);
        }
    }
}

But when it comes to applying the logic to every element that is not in the list, I am having a bit of trouble.

My first thought on this was to apply a :not() pseudo-selector. But the implementation for this has been a little tricky. The goal here would be to create something like this:

.shadow-#{$name}:not(h1):not(h2):not(h3) ect... {
    box-shadow: 0px #{$size} rgba($dark, 0.5);
}

But I'm not really sure how to do that. I have tried using the map-keys() SCSS function:

.shadow-#{$name}:not(map-keys($text-type-map)) {
    box-shadow: 0px #{$size} rgba($dark, 0.5);
}

But that doesn't seem to be doing anything.

Does anyone know if there is a better way to implement this (like an @if / @else function) or a way to make the :not() selector work correctly - essentially listing all items within the Sass Map on a single line with each value in it's own :not().

Upvotes: 2

Views: 613

Answers (1)

dawaltco
dawaltco

Reputation: 346

As long as the list is comma-separated (which the output of map-keys should be) you can actually achieve this just by escaping the Sass list inside the :not selector

.example:not(#{map-keys($text-type-map)}) {
    color: red;
}

// output:
// .example:not(h1, h2, h3, h4, h5, h6, p, a, span, ul, li) {
//   color: red;
// }

However, support for a selector list is a somewhat newer feature of :not, and is not supported in IE and other odd browsers.

If you need that support, you will probably need to write a function to chain the not selectors:

@function not-chain($list) {
  $output: '';
  @each $selector in $list {
    $output: $output + ':not(#{$selector})';
  }
  @return $output;
}

$exclude: map-keys($text-type-map);

.example#{not-chain($exclude)} {
    color: red;
}

// output:
// .example:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(p):not(a):not(span):not(ul):not(li) {
//   color: red;
// }

Upvotes: 2

Related Questions