8
\$\begingroup\$

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);
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Questions like this (ie. how do I make my mixin generate the desired output) are generally better suited for SO. \$\endgroup\$ – cimmanon Feb 3 '14 at 16:33
6
+50
\$\begingroup\$

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;
}
\$\endgroup\$
  • \$\begingroup\$ Hey. Thank you for these huge improvements. This really works out. I have a few questions. Using you second mixin, I need to specify which $color I want, even if it's the default one. Therefor, I can ommit : base in the function, right? Also, is there a reason why you choosed default for the first mixin and base for the second one? Just to distinct? \$\endgroup\$ – kleinfreund Feb 4 '14 at 17:53
  • \$\begingroup\$ I rewrote the second example later, 'default' didn't seem like a very good name so I changed it. It can be omitted when using the function (ie. theme(base) should return the same thing as theme()), I just thought it looked nicer that way. \$\endgroup\$ – cimmanon Feb 4 '14 at 18:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.