HiveBrain v1.2.0
Get Started
← Back to all entries
patterncssMinor

Improve mixin that outputs page-specific CSS

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
cssimprovemixinthatpageoutputsspecific

Problem

As part of this answer, I created a mixin to generate some page-specific CSS for me.

I need to style an element based on a class I assign to the html-element. I do this for four different classes on the root-element, which gives me four similar CSS rules.

I'm looking for suggestions for the mixin itself. This is not limited to its function, also naming and the general idea may have so room for improvements.

Usage:

  • Pass the $element you want to style...



  • ...and the $property (needs to hold a color value) you want to style i with



  • optionally, pass true as a third argument, if the light color variation should be used



@include themify(".page-title", background-color, true);


Example Output:

.page--blog .page-title {
    /* $blog-color-light */
    background-color: #6fbf00;
}

.page--portfolio .page-title {
    /* $portfolio-color-light */
    background-color: #ffaa10;
}

.page--profil .page-title {
    /* $profil-color-light */
    background-color: #6d91da;
}

.page--impressum .page-title {
    /* $impressum-color-light */
    background-color: #e04345;
}


Mixin:

@mixin themify($element, $property, $light-colors: false) {
    $pages: null;
    @if $light-colors == true {
        $pages: blog      $blog-color-light,
                portfolio $portfolio-color-light,
                profil    $profil-color-light,
                impressum $impressum-color-light;
    } @else {
        $pages: blog      $blog-color,
                portfolio $portfolio-color,
                profil    $profil-color,
                impressum $impressum-color;
    }

    @each $page in $pages {
        .page--#{nth($page, 1)} #{$element} {
            #{$property}: nth($page, 2);
        }
    }
}

Solution

This uses mappings, which are part of Sass 3.3. You can do the same thing without mappings, it's just not as pretty. In addition to allowing multiple selectors, it allows for more options than just light/dark colors (eg. background images, etc):

$themes:
    ( blog:
        ( default: red
        , light: lighten(red, 20%)
        )
    , portfolio:
        ( default: orange
        , light: lighten(orange, 20%)
        )
    , profile:
        ( default: green
        , light: lighten(green, 20%)
        )
    , impressum:
        ( default: blue
        , light: lighten(blue, 20%)
        )
    );

@mixin themify($property, $color: default) {
    @each $theme, $colors in $themes {
        .page--#{$theme} & {
            #{$property}: map-get($colors, $color);
        }
    }
}

.foo, .bar {
    @include themify(background-color);
    @include themify(color, light);
}


Output:

/* line 27, ../sass/test.scss */
.page--blog .foo, .page--blog .bar {
  background-color: red;
}
/* line 27, ../sass/test.scss */
.page--portfolio .foo, .page--portfolio .bar {
  background-color: orange;
}
/* line 27, ../sass/test.scss */
.page--profile .foo, .page--profile .bar {
  background-color: green;
}
/* line 27, ../sass/test.scss */
.page--impressum .foo, .page--impressum .bar {
  background-color: blue;
}
/* line 27, ../sass/test.scss */
.page--blog .foo, .page--blog .bar {
  color: #ff6666;
}
/* line 27, ../sass/test.scss */
.page--portfolio .foo, .page--portfolio .bar {
  color: #ffc966;
}
/* line 27, ../sass/test.scss */
.page--profile .foo, .page--profile .bar {
  color: #00e600;
}
/* line 27, ../sass/test.scss */
.page--impressum .foo, .page--impressum .bar {
  color: #6666ff;
}


However, as you can tell from the output, it generates fairly inefficient CSS if you're using more than one property per selector. You can deal with this by manipulating global variables (again, 3.3 syntax):

$colors: null; // private, don't touch

$themes:
    ( blog:
        ( base: red
        , light: lighten(red, 20%)
        )
    , portfolio:
        ( base: orange
        , light: lighten(orange, 20%)
        )
    , profile:
        ( base: green
        , light: lighten(green, 20%)
        )
    , impressum:
        ( base: blue
        , light: lighten(blue, 20%)
        )
    );

@mixin themify {
    @each $theme, $c in $themes {
        .page--#{$theme} {
            $colors: $c !global;

            @content;
        }
    }
}

@function theme($color: base) {
    @return map-get($colors, $color);
}

@include themify {
    .foo, .bar {
        background-color: theme(base);
        color: theme(light);
    }
}


Output:

/* line 42, ../sass/test.scss */
.page--blog .foo, .page--blog .bar {
  background-color: red;
  color: #ff6666;
}

/* line 42, ../sass/test.scss */
.page--portfolio .foo, .page--portfolio .bar {
  background-color: orange;
  color: #ffc966;
}

/* line 42, ../sass/test.scss */
.page--profile .foo, .page--profile .bar {
  background-color: green;
  color: #00e600;
}

/* line 42, ../sass/test.scss */
.page--impressum .foo, .page--impressum .bar {
  background-color: blue;
  color: #6666ff;
}

Code Snippets

$themes:
    ( blog:
        ( default: red
        , light: lighten(red, 20%)
        )
    , portfolio:
        ( default: orange
        , light: lighten(orange, 20%)
        )
    , profile:
        ( default: green
        , light: lighten(green, 20%)
        )
    , impressum:
        ( default: blue
        , light: lighten(blue, 20%)
        )
    );

@mixin themify($property, $color: default) {
    @each $theme, $colors in $themes {
        .page--#{$theme} & {
            #{$property}: map-get($colors, $color);
        }
    }
}

.foo, .bar {
    @include themify(background-color);
    @include themify(color, light);
}
/* line 27, ../sass/test.scss */
.page--blog .foo, .page--blog .bar {
  background-color: red;
}
/* line 27, ../sass/test.scss */
.page--portfolio .foo, .page--portfolio .bar {
  background-color: orange;
}
/* line 27, ../sass/test.scss */
.page--profile .foo, .page--profile .bar {
  background-color: green;
}
/* line 27, ../sass/test.scss */
.page--impressum .foo, .page--impressum .bar {
  background-color: blue;
}
/* line 27, ../sass/test.scss */
.page--blog .foo, .page--blog .bar {
  color: #ff6666;
}
/* line 27, ../sass/test.scss */
.page--portfolio .foo, .page--portfolio .bar {
  color: #ffc966;
}
/* line 27, ../sass/test.scss */
.page--profile .foo, .page--profile .bar {
  color: #00e600;
}
/* line 27, ../sass/test.scss */
.page--impressum .foo, .page--impressum .bar {
  color: #6666ff;
}
$colors: null; // private, don't touch

$themes:
    ( blog:
        ( base: red
        , light: lighten(red, 20%)
        )
    , portfolio:
        ( base: orange
        , light: lighten(orange, 20%)
        )
    , profile:
        ( base: green
        , light: lighten(green, 20%)
        )
    , impressum:
        ( base: blue
        , light: lighten(blue, 20%)
        )
    );

@mixin themify {
    @each $theme, $c in $themes {
        .page--#{$theme} {
            $colors: $c !global;

            @content;
        }
    }
}

@function theme($color: base) {
    @return map-get($colors, $color);
}

@include themify {
    .foo, .bar {
        background-color: theme(base);
        color: theme(light);
    }
}
/* line 42, ../sass/test.scss */
.page--blog .foo, .page--blog .bar {
  background-color: red;
  color: #ff6666;
}

/* line 42, ../sass/test.scss */
.page--portfolio .foo, .page--portfolio .bar {
  background-color: orange;
  color: #ffc966;
}

/* line 42, ../sass/test.scss */
.page--profile .foo, .page--profile .bar {
  background-color: green;
  color: #00e600;
}

/* line 42, ../sass/test.scss */
.page--impressum .foo, .page--impressum .bar {
  background-color: blue;
  color: #6666ff;
}

Context

StackExchange Code Review Q#40574, answer score: 6

Revisions (0)

No revisions yet.