I'm trying to dynamically create my color palettes from a set of base colors using SASS. I was previously creating each base color manually by first defining a set of color variables, then creating a map of each base color with various shades. Then every color map was put into a palette map.
Then to return a color/shade I would use a function which retrieved the value from the color map within the palette map.
It was so long winded, not to mention annoying whenever I wanted to add another color, so I wanted to try and do this dynamically but I seem to be having a bit of trouble with creating a map using a variable name, as far as I can tell.
Here is my previous code just to give you an idea of what I was doing:
$color-percent-change: 3%;
$color-blue: hsla(196, 85%, 57%, 1);
$color-brown: hsla(15, 40%, 33%, 1);
$color-charcoal: hsla(0, 0%, 23%, 1);
$blue: (
'lighten-3': lighten($color-blue, ($color-percent-change * 3)),
'lighten-2': lighten($color-blue, ($color-percent-change * 2)),
'lighten-1': lighten($color-blue, ($color-percent-change * 1)),
'base': $color-blue,
'darken-1': darken($color-blue, ($color-percent-change * 1)),
'darken-2': darken($color-blue, ($color-percent-change * 2)),
'darken-3': darken($color-blue, ($color-percent-change * 3))
);
$brown: (
'lighten-3': lighten($color-brown, ($color-percent-change * 3)),
'lighten-2': lighten($color-brown, ($color-percent-change * 2)),
'lighten-1': lighten($color-brown, ($color-percent-change * 1)),
'base': $color-brown,
'darken-1': darken($color-brown, ($color-percent-change * 1)),
'darken-2': darken($color-brown, ($color-percent-change * 2)),
'darken-3': darken($color-brown, ($color-percent-change * 3))
);
$charcoal: (
'lighten-3': lighten($color-charcoal, ($color-percent-change * 3)),
'lighten-2': lighten($color-charcoal, ($color-percent-change * 2)),
'lighten-1': lighten($color-charcoal, ($color-percent-change * 1)),
'base': $color-charcoal,
'darken-1': darken($color-charcoal, ($color-percent-change * 1)),
'darken-2': darken($color-charcoal, ($color-percent-change * 2)),
'darken-3': darken($color-charcoal, ($color-percent-change * 3))
);
$palette: (
'blue': $blue,
'brown': $brown,
'charcoal': $charcoal,
);
@function color($color, $type: 'base') {
@if map-has-key($palette, $color) {
$current: map-get($palette, $color);
@if map-has-key($current, $type) {
@return map-get($current, $type);
}
}
@warn 'Unknown #{$color} - #{$type} in #{$palette}.';
@return null;
}
.blue {
color: color('blue', 'lighten-2');
}
So now I'm creating a colors map with each color name/value, then looping through each key in the map and dynamically creating the base colors shade palette and using a similar function to return the color value, but I'm getting the error:
argument `$map` of `map-has-key($map, $key)` must be a map
Here is the new code:
$color-percent-change: 3%;
$colors: (
'brand': hsla(265, 35%, 50%, 1),
'black': hsla(0, 0%, 0%, 1),
'blue': hsla(196, 85%, 57%, 1),
'brown': hsla(15, 40%, 33%, 1),
'charcoal': hsla(0, 0%, 23%, 1),
'emerald': hsla(140, 52%, 55%, 1),
'green': hsla(101, 55%, 60%, 1),
'grey': hsla(0, 0%, 47%, 1),
'indigo': hsla(225, 57%, 47%, 1),
'orange': hsla(34, 100%, 53%, 1),
'pink': hsla(309, 80%, 70%, 1),
'purple': hsla(285, 67%, 60%, 1),
'red': hsla(11, 85%, 57%, 1),
'silver': hsla(0, 0%, 80%, 1),
'slate': hsla(210, 20%, 33%, 1),
'teal': hsla(180, 100%, 24%, 1),
'white': hsla(0, 100%, 100%, 1),
'yellow': hsla(55, 100%, 57%, 1),
);
@each $name, $color in $colors {
$name: (
'lighten-10': lighten($color, ($color-percent-change * 10)),
'lighten-9': lighten($color, ($color-percent-change * 9)),
'lighten-8': lighten($color, ($color-percent-change * 8)),
'lighten-7': lighten($color, ($color-percent-change * 7)),
'lighten-6': lighten($color, ($color-percent-change * 6)),
'lighten-5': lighten($color, ($color-percent-change * 5)),
'lighten-4': lighten($color, ($color-percent-change * 4)),
'lighten-3': lighten($color, ($color-percent-change * 3)),
'lighten-2': lighten($color, ($color-percent-change * 2)),
'lighten-1': lighten($color, ($color-percent-change * 1)),
'base': $color,
'darken-1': darken($color, ($color-percent-change * 1)),
'darken-2': darken($color, ($color-percent-change * 2)),
'darken-3': darken($color, ($color-percent-change * 3)),
'darken-4': darken($color, ($color-percent-change * 4)),
'darken-5': darken($color, ($color-percent-change * 5)),
'darken-6': darken($color, ($color-percent-change * 6)),
'darken-7': darken($color, ($color-percent-change * 7)),
'darken-8': darken($color, ($color-percent-change * 8)),
'darken-9': darken($color, ($color-percent-change * 9)),
'darken-10': darken($color, ($color-percent-change * 10))
);
}
@function color($color, $type: 'base') {
@if map-has-key($color, $type) {
@return map-get($color, $type);
}
@warn 'Unknown #{$color} - #{$type} in #{$color}.';
@return null;
}
.red {
color: color('red', 'lighten-2');
}
I think that the @each
loop defining $name
as a variable name is causing a problem as it's not recognizing it as a SASS map? But I'm not completely sure, usually my SASS files are pretty simple, this is the first time I've really gone further than just using simple variables and functions with it, so I'm probably missing something relatively straight forward?
You need to merge a generated a generated list of colors to a main list using the color name as the key map, instead of generate a variable by interpolation:
// modification
$color-percent-change: 3%;
// color list definition
$colors: (
'brand': hsla(265, 35%, 50%, 1),
'black': hsla(0, 0%, 0%, 1),
'blue': hsla(196, 85%, 57%, 1),
'brown': hsla(15, 40%, 33%, 1),
'charcoal': hsla(0, 0%, 23%, 1),
'emerald': hsla(140, 52%, 55%, 1),
'green': hsla(101, 55%, 60%, 1),
'grey': hsla(0, 0%, 47%, 1),
'indigo': hsla(225, 57%, 47%, 1),
'orange': hsla(34, 100%, 53%, 1),
'pink': hsla(309, 80%, 70%, 1),
'purple': hsla(285, 67%, 60%, 1),
'red': hsla(11, 85%, 57%, 1),
'silver': hsla(0, 0%, 80%, 1),
'slate': hsla(210, 20%, 33%, 1),
'teal': hsla(180, 100%, 24%, 1),
'white': hsla(0, 100%, 100%, 1),
'yellow': hsla(55, 100%, 57%, 1),
);
// creates a list to store lists of color modifications, with key defined as color name
$colormap: ();
@each $name, $color in $colors {
// generate the list of colors modifications
$generated: (
'lighten-10': lighten($color, ($color-percent-change * 10)),
'lighten-9': lighten($color, ($color-percent-change * 9)),
'lighten-8': lighten($color, ($color-percent-change * 8)),
'lighten-7': lighten($color, ($color-percent-change * 7)),
'lighten-6': lighten($color, ($color-percent-change * 6)),
'lighten-5': lighten($color, ($color-percent-change * 5)),
'lighten-4': lighten($color, ($color-percent-change * 4)),
'lighten-3': lighten($color, ($color-percent-change * 3)),
'lighten-2': lighten($color, ($color-percent-change * 2)),
'lighten-1': lighten($color, ($color-percent-change * 1)),
'base': $color,
'darken-1': darken($color, ($color-percent-change * 1)),
'darken-2': darken($color, ($color-percent-change * 2)),
'darken-3': darken($color, ($color-percent-change * 3)),
'darken-4': darken($color, ($color-percent-change * 4)),
'darken-5': darken($color, ($color-percent-change * 5)),
'darken-6': darken($color, ($color-percent-change * 6)),
'darken-7': darken($color, ($color-percent-change * 7)),
'darken-8': darken($color, ($color-percent-change * 8)),
'darken-9': darken($color, ($color-percent-change * 9)),
'darken-10': darken($color, ($color-percent-change * 10))
);
// merge the generated color list to the main color list
$colormap: map-merge($colormap, ($name: $generated));
}
@function color($color, $type: 'base') {
// Check is the main color exists in the generated list, and if the color modification exist in the specific color list
@if map-has-key($colormap, $color) && map-has-key(map-get($colormap, $color), $type) {
// return the specific color
@return map-get(map-get($colormap, $color), $type);
}
// warning
@warn 'Unknown #{$color} - #{$type} in #{$color}.';
// return default color
@return null;
}
// test
.red {
color: color('red', 'lighten-2');
}
You can see the working example here.
Hope it helps.