Sass Modules & Tools
Miriam Suzanne
@
Smashing Online
@hcatlin
Hampton Catlin
Original Inventor
2006…
HAML for Ruby
2007…
Extended For CSS
ul
:margin-bottom 1em
li
:list-style-type disk
Original Sass Variables
!total_cols = 12
!col_width = 4em
Original Sass Mixins
=grid-container(!grid = !total_cols )
:margin-left auto
:margin-right auto
:overflow hidden
:width=(!grid * !col_width) + ((!grid - 1) * !gutter_width)
:max-width 99%
Original Control Structures
@if !switch
:float right
@else
:float left
2010 Sass » Scss
More CSSy Syntax
Design Principles
- Strict Superset: All CSS is Valid Scss
- Clear Differentiation Between CSS & non-CSS
CSS Superset
style.css
➡ style
.scss
Two-Syntax Options
One Sass Language
- Sass Syntax ➡
*.sass
- Scss Syntax ➡
*.scss
2013
Multiple Implementations
LibSass » SassC | GoSass | Sass.js | Node Sass
2018
Ruby Sass ➡ Dart Sass
Dart Sass
Reference Implementation
npm install
sass
Dart Sass
Up-To-Date Implementation
Dart Sass
New Features Regularly
Along with bug-fixes
tl;dr
Use Dart Sass
Organize
Into Partial Files
Use the _partial.scss
filename prefix
sass/
tools/
config/
_colors.scss
_fonts.scss
_sizes.scss
_index.scss
initial/
patterns/
layouts/
components/
style.scss
sass/config/
_index.scss
@forward 'color';
@forward 'fonts';
@forward 'scale';
Use @forward
To Combine Partials
Use @use
To Access Partials
@import
Is A CSS Feature
@use
& @forward
Only in Dart Sass
Sass @import
Dangerously Combines Files
Sass @import
Creates Global Namespace
@use
Loads
Partials As Modules
A Module Includes
- A Namespace
- Public Members
- Variables
- Functions
- Mixins
- CSS Output
_colors.scss
$brand: rebeccapurple;
$_private: papayawhip;
@function tint($color, $amount) { ... }
@use 'colors'
a {
color: colors.$brand;
background: colors.tint(maroon, 95%);
}
@use 'colors'
as *
a {
color: $brand;
background: tint(maroon, 95%);
}
@use 'colors'
as config
a {
color: config.$brand;
background: config.tint(maroon, 95%);
}
sass/config/
_index.scss
@forward 'color';
@forward 'fonts';
@forward 'scale';
@use 'config'
buttons {
background-color: config.$brand;
font-size: config.$small;
}
sass/config/
_index.scss
@forward 'color' as color-*;
@forward 'fonts' as font-*;
@forward 'scale' as size-*;
@use 'config'
buttons {
background-color: config.$color-brand;
font-size: config.$size-small;
}
Helps You
Write Modular Code
Helps You
Visualize Dependencies
Helps You
Avoid Conflicts
Helps Sass
Avoid CSS Conflicts
Helps Sass
Add New Features
Built-In
Sass Modules
math
, color
, string
, list
, map
, selector
, meta
@use
'sass:math'
nav {
width: math.percentage(1/3);
}
nav {
background: silver;
}
nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav li { display: inline-block; }
nav {
background: silver;
ul {
margin: 0;
padding: 0;
list-style: none;
}
li { display: inline-block; }
}
Useful for
Scope Ownership
Useful for
Managing Overrides
Especially With
Media Bubbling
nav {
display: flex;
flex-direction: column;
@media (min-width: 30em) {
flex-direction: row;
}
}
Also Works With
@support Queries
nav {
float: left;
width: 25%;
@supports (display: grid) {
width: auto;
}
}
&
Parent Selector
a {
&:link, &:visited {
}
}
Be Careful
Breaking Selectors
.block {
&__element {
&--modifier {
}
}
}
Don’t Overly Sandbox
Avoid the temptation to mimic html structure
body {
nav {
ul {
li { display: inline-block; }
}
}
}
body nav ul li { display: inline-block; }
Sass is
A Pre-Processor
Your Sass is only as good
As The CSS It Generates
Criteria for
Any CSS Tools
In The Browser
There is Only CSS
Using Variables
nav {
background: $brand-color;
}
Interpolating Variables
#{$selector} {
--brand-color: #{$brand-color};
}
Fun Legacy Fact
$variable-name == $variable_name
Sass Variables Scope
To Modules & Brackets
Custom Properties Inherit
Through DOM Structures
Sass Variables Compile
To A Single Value
Custom Properties Resolve
In The Cascade
Sass Variables
For Code Automation
Custom Properties
For Dynamic Style
Many Tools
Have Variables
Many Tools
Generate CSS
Sass is
Designed For CSS
Sass Values
Based on CSS
Numbers & Lengths
3.14
| 34em
| 15vw
| &c.
Sass Strings
'nav main'
| bold
Sass Colors
#df207f
| hsl(...)
| rgb(...)
| &c…
Sass Lists
'Helvetica Neue', sans-serif
| auto 1fr 30em
| &c.
Additional
Sass-Only Values
true
| false
| null
| <functions>
Data Maps
$map-variable: (
keys: values,
structured: data,
);
$color-brand-blue: hsl(195, 85%, 35%);
$color-brand-orange: hsl(24, 100%, 39%);
$color-brand-pink: hsl(330, 85%, 48%);
$brand: (
'blue': hsl(195, 85%, 35%),
'orange': hsl(24, 100%, 39%),
'pink': hsl(330, 85%, 48%),
);
@use 'colors';
@use 'sass:map';
a {
color: map.get(colors.$brand, 'blue');
}
@use 'sass:map';
$_brand: ( ... );
$_ui: ( ... )
$colors: map.merge($_brand, $_ui);
Meaningful Grouping
For Humans & Machines
@if
& @else
Define Optional Code
@use 'sass:color';
button {
color: $btn-color;
@if (color.lightness($btn-color) > 50%) {
background: black;
} @else {
background: white;
}
}
@each
& @for
Define Looping Code
@for
Loops
Number Ranges
@for $n from 1 through 10 {
.item:nth-child(#{$n}) {
}
}
@each
Loops
Lists & Maps
html {
@each $name, $color in $brand {
--color-brand-#{$name}: #{$color};
}
}
@each $name, $color in $buttons {
[data-btn-color='#{$name}'] {
background-color: $color;
}
}
Maps can be
Updated Dynamically
@use 'sass:color';
@use 'sass:map';
@each $name, $color in $colors {
$generated: (
'#{$name}-light': color.scale($color, $lightness: 50%),
'#{$name}-dark': color.scale($color, $lightness: -50%),
);
$colors: map.merge($colors, $generated);
}
Maps can be
Accessed Dynamically
@mixin background($color-name) {
background: map.get($color-name);
}
Sass Mixins
Store Blocks of Code
Like massive variables…
@mixin button-base {
border: thin solid;
border-radius: 0.12em;
font: inherit;
padding: 0.25em 1em;
}
[data-btn='danger'] {
@include button-base;
background: maroon;
color: white;
}
@mixin button($background, $text) {
background: $background;
color: $text;
border: thin solid;
border-radius: 0.12em;
font: inherit;
padding: 0.25em 1em;
}
[data-btn='danger'] {
@include button(maroon, white);
}
Sass Functions
Return a Value
@use 'sass:color';
@function contrast($color) {
@if (color.lightness($color) > 50%) {
@return black;
} @else {
@return white;
}
}
button {
background: $brand-color;
color: contrast($brand-color)
}
@mixin button($background) {
background: $background;
color: contrast($background);
border: thin solid;
border-radius: 0.12em;
font: inherit;
padding: 0.25em 1em;
}
Simpler map.get
Color Function
@use 'config';
@use 'sass:map';
a {
color: map.get(config.$colors, 'action');
}
@use 'sass:map';
$colors: ( ... );
@function color($name) {
@return map.get($colors, $name);
}
@use 'config';
a {
color: config.color('action');
}
Create
Modular Toolkits
Capture
Control Structures
@use 'sass:color';
@use 'sass:map';
@each $name, $color in $colors {
$generated: (
'#{$name}-light': color.scale($color, $lightness: 50%),
'#{$name}-dark': color.scale($color, $lightness: -50%),
);
$colors: map.merge($colors, $generated);
}
@use 'sass:color';
@use 'sass:map';
@function build-palette($colors) {
@each $name, $color in $colors {
$generated: (
'#{$name}-light': color.scale($color, $lightness: 50%),
'#{$name}-dark': color.scale($color, $lightness: -50%),
);
$colors: map.merge($colors, $generated);
}
}
@use 'tools';
$colors: ( ... )
$colors: tools.build-palette($colors);
Tools Encourage
Consistency
Tools Encourage
Best Practice
Tools Encourage
Systems
Tools Free Us
To Focus on Details
My job is to make sure
the system is modular and flexible enough
to be used in any number of unpredictable ways.
–
Mina Markham, Pantsuit
👍 Opinionated
About Process
👎 Opinionated
About Style
Balance of Firm
And Flexible
Tools
Are A ByProduct
Notice Patterns
And Capture Them
Reuse Tools
And Adapt
Over-Engineering
Is Part of The Process
Ok, so…
There’s A Map Problem
Internal Reference
$colors: (
'brand-blue': hsl(195, 85%, 35%),
'action': 'brand-blue',
);
Internal Reference
@use 'sass:map';
$colors: (
'brand-blue': hsl(195, 85%, 35%),
'action': map.get($colors, 'brand-blue');
);
ERROR
Undefined variable $colors
Recursive
Map-Get Function
$colors: (
'brand-pink': hsl(330, 85%, 48%),
'escher': 'brand-pink',
'godel': 'escher',
'bach': 'godel',
'kevin bacon': 'bach',
);
@use 'sass:map';
@function color($name) {
$result: map.get($colors, $name);
@if map.has-key($colors, $result) {
$result: color($result);
}
@return $result;
}
color('kevin bacon')
hsl(330, 85%, 48%)
Map Adjustments
$colors: (
'brand-pink': hsl(330, 85%, 48%),
'escher': 'brand-pink',
'godel': 'escher',
'bach': 'godel',
'kevin bacon': 'bach' ('lighten': 20%),
);
✨
(more function magic we won’t dig into)
color('kevin bacon')
hsl(330, 85%, 68%)
¯\_(ツ)_/¯
👎 Requires More Tooling
And Custom Syntax
👍 Meaningful Organization
👍 Visible Relationships
👍 Single Source Of Truth
👍 Automated Systems
Find or Build
Tools That Fit You
Tools That
Encourage You
Tools That
Get Out of Your Way