Leveraging Maps for Scalable and Maintainable SASS
Tanner Langley
SASS Scalability and Maintenance Goals
- Quickly update existing code
- Know where to add new code
- Have a portable code base (project to project)
- Have complete control over specificity
- Reduce training time for new developers
I want to...
A tiny bit of history
CSS was released in 1994
- Created to style documents
- Not meant for applications
- One layer of abstraction (markup/styles)
How can we make it better?
Step 1:
Separate Identity and Classification
Identifiers
- Car
- Tanner's car
- Nissan
- Nissan Altima
- Tanner's Nissan Altima
Classifications
- Wheels
- Engine
- Windshield
- Seat
- Trunk
- Air conditioner
Defining a car
Markup based on Identity
<div class="paragraph--type--simple-cta">
<h3>Heading</h3>
<a href="/about">Learn more</a>
</div>
Markup based on Classification (kind of)
<div>
<h3 class="heading large">Heading</h3>
<a class="btn btn-medium orange" href="/about">Learn more</a>
</div>
Why "kind of?"
<div>
<h3 class="heading large">Heading</h3>
<a class="btn btn-medium orange" href="/about">
Learn more
</a>
</div>
Identifier
Classifier
- Markup should describe identity
- Styles should define classification
Separation of Concerns
What is the problem?
We usually use both identity and classification
What if we let SASS handle the classifiers?
Bright Idea:
SASS to the rescue!
- Define mixins
- Define functions
- Use placeholders and extends
- Write variables
- Problem solved!
.paragraph--type--simple-cta {
a {
@include btn($background: $blue, $color: #fff);
}
}
So simple!

.paragraph--type--simple-cta {
a {
// With named parameters
@include btn($bg: $blue, $color: #fff, $border-color: $blue, $active-bg: transparent,
$active-color: $blue, $active-border-color: $blue, $size: small);
// Without named parameters...
@include btn($blue, #fff, $blue, transparent, $blue, $blue, small);
}
}
The realistic example...
Why is this failing to scale?
.paragraph--type--simple-cta {
a {
@include btn($blue, #fff, $blue, transparent, $blue, $blue, small);
}
}
Identifier
What kind of button?
.paragraph--type--simple-cta {
a {
@include btn(blue);
}
}
Identifier
What if we could give that configuration a name?
Classifier
But how?
A lesson from Game Programming
(and mostly from Chris Coyier)
z-index sucks!
- Hard to test
- Hard to debug
- Easy to break things
The Solution?
Use maps to keep all definitions in one place
$zindex: (
modal : 9000,
overlay : 8000,
dropdown : 7000,
header : 6000,
footer : 5000
);
.header {
z-index: map-get($zindex, header);
}
Taking this concept further
Config based buttons
Configuration
$buttons: (
blue: (
background: $blue,
color: #fff,
border: $blue,
active-background: #fff,
active-color: $blue,
active-border: $blue,
),
green: (
background: $green,
color: #fff,
border: $green,
active-background: #fff,
active-color: $green,
active-border: $green,
)
);
Usage
.paragraph--type--simple-cta {
a {
@include btn(blue);
}
}
Implementation
Creating a Standalone Framework
Default variables


Color is light blue
Color is dark blue
Mixins with default variables
// framework/_buttons.scss
$buttons: (
default: (
color: white,
background: blue,
border: blue
)
) !default;
@mixin btn($key) {
$config: map-get($buttons, $key);
color: map-get($config, color);
background: map-get($config, background);
border: 1px solid map-get($config, border);
}
Settings
- Colors
- Font information
- Global gutter sizes
- Global animation duration
Config
Anything that configures pieces of your site
VS
Folder Structure
Base
- Define global variables
- colors
- breakpoints
- font names
- etc.
- Configure variations of similar elements
Framework
- Define mixins and functions which make use of our configuration
- Should be completely standalone
- Never actually styles anything on the page!
Everything else
- Responsible for applying styles to pages
- Most things here are specific to your website
Example

Pulling everything together
Mixin definition
// framework/_buttons.scss
$buttons: (
default: (
color: white,
background: blue,
border: blue
)
) !default;
@mixin btn($key) {
$config: map-get($buttons, $key);
color: map-get($config, color);
background: map-get($config, background);
border: 1px solid map-get($config, border);
}
Config definition
// base/_config.scss
$buttons: (
blue: (
background: $blue,
color: #fff,
border: $blue,
active-background: #fff,
active-color: $blue,
active-border: $blue,
),
green: (
background: $green,
color: #fff,
border: $green,
active-background: #fff,
active-color: $green,
active-border: $green,
)
);
Usage
.paragraph--type--simple-cta {
a {
@include btn(blue);
}
}
Things that might benefit from being configured in one place:
- Buttons
- Max widths
- Link styles
- Selector groups
- Typography
- Any custom styles which are mostly the same but have variants
Config based containers
Configuration
$max-widths: (
site-container: (
width: 1600px,
gutters: false
),
content: (
width: 640px,
gutters: (
small: 15px,
medium: 20px
)
),
);
Usage
.paragraph--type--compound-logo-grid {
@include max-width(content);
}
Revisiting Goals
- ✔ Quickly update existing code
- ✔ Know where to add new code
- ✔ Have a portable code base (project to project)
- ✔ Have complete control over specificity
- ✔ Reduce training time for new developers
I want to...
Some helpful... helpers
The key() function
Key
@function key($map, $key, $sub-key: null) {
@if map-has-key($map, $key) {
$val: map-get($map, $key);
@if $sub-key and map-has-key($val, $sub-key) {
$val: map-get($val, $sub-key);
}
@return $val;
}
@warn "Unknown '#{$key}' in '#{$map}'.";
@return null;
}
map-get() with an extra level
Usage
$nested-map: (
first-key: (
nested-key: "I'm a nested key!",
another: "just another nested key :("
)
);
@mixin cool-mixin() {
// value: I'm a nested key!
$nested-key: key($nested-map, first-key, nested-key);
}
extend-in-map()
extend-in-map
@function extend-in-map($map-to-search, $sub-map-key) {
$map-to-merge: key($map-to-search, $sub-map-key);
@if (map-has-key($map-to-merge, extend)) {
$key-of-map-to-extend: map-get($map-to-merge, extend);
@if (map-has-key($map-to-search, $key-of-map-to-extend)) {
@return map-merge(key($map-to-search, $key-of-map-to-extend), $map-to-merge);
}
}
@return $map-to-merge;
}
Extend a subkey with another subkey
Mixin Usage
@mixin btn($button-key) {
$button: extend-in-map($buttons, $button-key);
background: key($button, background);
color: key($button, color);
// and so on...
}
Config Usage
$buttons: (
blue: (
background: $blue,
color: #fff,
border: $blue,
active-background: #fff,
active-color: $blue,
active-border: $blue,
),
blue-transparent: (
extend: blue,
active-background: transparent
)
);
Know when to add parameters
.paragraph--type--simple-cta {
a {
@include btn(blue, small);
}
}
Questions
Leveraging Maps for Scalable and Maintainable SASS
By Tanner langley
Leveraging Maps for Scalable and Maintainable SASS
- 576