Skip to content

Instantly share code, notes, and snippets.

@aarongeorge
Last active May 1, 2020 03:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aarongeorge/1955b1d97078614d14d1f59ad1ec7e06 to your computer and use it in GitHub Desktop.
Save aarongeorge/1955b1d97078614d14d1f59ad1ec7e06 to your computer and use it in GitHub Desktop.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment