I was looking to see if there are any CSS pre-processors that handle scoped variables inside of their mixins.
The goal was to be able to create a breakpoint
mixin that auto-injects variables defined in a map/hash.
This is how I've been forced to do it with Sass
SCSS Input
$BREAKPOINTS: (
default: min-width 0 6 10 10 10 10 24,
phablet: min-width 480 6 10 10 10 10 24,
tablet: min-width 768 12 20 20 20 20 24,
laptop: min-width 1024 12 20 20 20 20 36,
desktop: min-width 1366 12 20 20 20 20 36
);
$BREAKPOINTS-MAP: $BREAKPOINTS;
$DEFAULT-BREAKPOINT: nth(map-keys($BREAKPOINTS-MAP), 1);
$DEFAULT-BREAKPOINT-WIDTH: nth(map-get($BREAKPOINTS-MAP, nth(map-keys($BREAKPOINTS-MAP), 1)), 2);
$DEFAULT-BREAKPOINT-COLUMNS: nth(map-get($BREAKPOINTS-MAP, nth(map-keys($BREAKPOINTS-MAP), 1)), 3);
$DEFAULT-BREAKPOINT-GRID-HORIZONTAL-GUTTER: nth(map-get($BREAKPOINTS-MAP, nth(map-keys($BREAKPOINTS-MAP), 1)), 4);
$DEFAULT-BREAKPOINT-GRID-VERTICAL-GUTTER: nth(map-get($BREAKPOINTS-MAP, nth(map-keys($BREAKPOINTS-MAP), 1)), 5);
$DEFAULT-BREAKPOINT-COLUMN-HORIZONTAL-GUTTER: nth(map-get($BREAKPOINTS-MAP, nth(map-keys($BREAKPOINTS-MAP), 1)), 6);
$DEFAULT-BREAKPOINT-COLUMN-VERTICAL-GUTTER: nth(map-get($BREAKPOINTS-MAP, nth(map-keys($BREAKPOINTS-MAP), 1)), 7);
$DEFAULT-BREAKPOINT-VERTICAL-RHYTHM: nth(map-get($BREAKPOINTS-MAP, nth(map-keys($BREAKPOINTS-MAP), 1)), 8);
$CURRENT-BREAKPOINT: $DEFAULT-BREAKPOINT;
$BREAKPOINT-WIDTH: $DEFAULT-BREAKPOINT-WIDTH;
$BREAKPOINT-COLUMNS: $DEFAULT-BREAKPOINT-COLUMNS;
$BREAKPOINT-GRID-HORIZONTAL-GUTTER: $DEFAULT-BREAKPOINT-GRID-HORIZONTAL-GUTTER;
$BREAKPOINT-GRID-VERTICAL-GUTTER: $DEFAULT-BREAKPOINT-GRID-VERTICAL-GUTTER;
$BREAKPOINT-COLUMN-HORIZONTAL-GUTTER: $DEFAULT-BREAKPOINT-COLUMN-HORIZONTAL-GUTTER;
$BREAKPOINT-COLUMN-VERTICAL-GUTTER: $DEFAULT-BREAKPOINT-COLUMN-VERTICAL-GUTTER;
$BREAKPOINT-VERTICAL-RHYTHM: $DEFAULT-BREAKPOINT-VERTICAL-RHYTHM;
@mixin breakpoint ($breakpoint) {
@if map-has-key ($BREAKPOINTS, $breakpoint) {
$breakpoint-data: map-get($BREAKPOINTS, $breakpoint);
$breakpoint-type: #{nth($breakpoint-data, 1)};
$breakpoint-width: nth($breakpoint-data, 2);
$breakpoint-columns: nth($breakpoint-data, 3);
$breakpoint-grid-horizontal-gutter: nth($breakpoint-data, 4);
$breakpoint-grid-vertical-gutter: nth($breakpoint-data, 5);
$breakpoint-column-horizontal-gutter: nth($breakpoint-data, 6);
$breakpoint-column-vertical-gutter: nth($breakpoint-data, 7);
$breakpoint-vertical-rhythm: nth($breakpoint-data, 8);
@media (#{$breakpoint-type}: #{$breakpoint-width / 16}em) {
$CURRENT-BREAKPOINT: $breakpoint !global;
$BREAKPOINT-WIDTH: $breakpoint-width !global;
$BREAKPOINT-COLUMNS: $breakpoint-columns !global;
$BREAKPOINT-GRID-HORIZONTAL-GUTTER: $breakpoint-grid-horizontal-gutter !global;
$BREAKPOINT-GRID-VERTICAL-GUTTER: $breakpoint-grid-vertical-gutter !global;
$BREAKPOINT-COLUMN-HORIZONTAL-GUTTER: $breakpoint-column-horizontal-gutter !global;
$BREAKPOINT-COLUMN-VERTICAL-GUTTER: $breakpoint-column-vertical-gutter !global;
$BREAKPOINT-VERTICAL-RHYTHM: $breakpoint-vertical-rhythm !global;
@content;
$CURRENT-BREAKPOINT: $DEFAULT-BREAKPOINT !global;
$BREAKPOINT-WIDTH: $DEFAULT-BREAKPOINT-WIDTH !global;
$BREAKPOINT-COLUMNS: $DEFAULT-BREAKPOINT-COLUMNS !global;
$BREAKPOINT-GRID-HORIZONTAL-GUTTER: $DEFAULT-BREAKPOINT-GRID-HORIZONTAL-GUTTER !global;
$BREAKPOINT-GRID-VERTICAL-GUTTER: $DEFAULT-BREAKPOINT-GRID-VERTICAL-GUTTER !global;
$BREAKPOINT-COLUMN-HORIZONTAL-GUTTER: $DEFAULT-BREAKPOINT-COLUMN-HORIZONTAL-GUTTER !global;
$BREAKPOINT-COLUMN-VERTICAL-GUTTER: $DEFAULT-BREAKPOINT-COLUMN-VERTICAL-GUTTER !global;
$BREAKPOINT-VERTICAL-RHYTHM: $DEFAULT-BREAKPOINT-VERTICAL-RHYTHM !global;
}
}
}
.button {
content: $CURRENT-BREAKPOINT;
@include breakpoint(phablet) {
content: $CURRENT-BREAKPOINT;
}
}
SCSS Output
.button {
content: default;
}
@media (min-width: 30em) {
.button {
content: phablet;
}
}
So I get what I expect from this, but it's so damn bloated, and there's all of this setting and unsetting, plus all the !global
and $DEFAULT
variables needing to exist so that !global
works in the first place. Having to reset the values after each breakpoint is needed to stop the variables from leaking into the global space. It's just a mess.
So I looked into Stylus and came up with this
Stylus Input
$BREAKPOINTS = {
default: min-width 0 6 10 10 10 10 24,
phablet: min-width 480 6 10 10 10 10 24,
tablet: min-width 768 12 20 20 20 20 24,
laptop: min-width 1024 12 20 20 20 20 36,
desktop: min-width 1366 12 20 20 20 20 36
}
breakpoint(breakpoint) {
$BREAKPOINT-TYPE = slice($BREAKPOINTS[breakpoint], 0, 1)
$BREAKPOINT-WIDTH = slice($BREAKPOINTS[breakpoint], 1, 2)
@media ({$BREAKPOINT-TYPE} ($BREAKPOINT-WIDTH/16)em) {
$CURRENT-BREAKPOINT = breakpoint
$BREAKPOINT-COLUMNS = slice($BREAKPOINTS[breakpoint], 3, 4)
$BREAKPOINT-GRID-HORIZONTAL-GUTTER = slice($BREAKPOINTS[breakpoint], 3, 4)
$BREAKPOINT-GRID-VERTICAL-GUTTER = slice($BREAKPOINTS[breakpoint], 4, 5)
$BREAKPOINT-COLUMN-HORIZONTAL-GUTTER = slice($BREAKPOINTS[breakpoint], 5, 6)
$BREAKPOINT-COLUMN-VERTICAL-GUTTER = slice($BREAKPOINTS[breakpoint], 6, 7)
$BREAKPOINT-VERTICAL-RHYTHM = slice($BREAKPOINTS[breakpoint], 7)
{block}
}
}
button
content $CURRENT-BREAKPOINT
+breakpoint(phablet)
content $BREAKPOINT-TYPE
Stylus Output
button {
content: $CURRENT-BREAKPOINT;
}
@media (min-width: 30em) {
button {
content: phablet;
}
}
Far less bloat. Also the scoping of $CURRENT-BREAKPOINT
makes more sense now. I don't have to set defaults or globals or anything.
As I haven't set the value of $CURRENT-BREAKPOINT
anything outside of a breakpoint
mixin is unaffected. If I wanted to have it be set to default
I'd just set that up in my main variables file