Where to Discuss?

Local Group

Preface

Goal: Generate custom color classes step by step, utilizing lessjs guard capability to achieve conditional if-then-else.

Even when utilizing CSS Framework, theme making require to create custom CSS. One of my need is color class helper, porting from materialize color. Materialize CSS’s color is so complete, something that is not available in Semantic UI. This spacing Class can be made utilizing LessJS.

lessjs: script

These guidance will save you a lot of coding time. And also tons of typing.

Example Case

GMC: Sass to Less

This article will port google materialize color, from sass to its less counterpart.

Reading

This article require loop knowledge that you can read on


1: Shade: Basic

Consider start from the very basic case

SASS Source

Materialize has this special colors. The process is so simple that it is only use each.

$shades: (
  "black":        #000000,
  "white":        #FFFFFF,
  "transparent":  transparent
);

// Shade classes
@each $color, $color_value in $shades {
  .#{$color} {
    background-color: $color_value !important;
  }
  .#{$color}-text {
    color: $color_value !important;
  }
}

LESS Port

The less counterpart is also simple.

@shades: {
  black:        #000000;
  white:        #FFFFFF;
  transparent:  transparent
}

each(@shades, {
  .@{key}{
    background-color: @value !important;
  }
  .@{key}-text {
    color: @value !important;
  }
})

CSS Output

Materialize has two CSS selectors

  • Background, using color as selector, and

  • Foreground, using color-text as selector.

The result can be shown as below code:

.black {
  background-color: #000000 !important;
}
.black-text {
  color: #000000 !important;
}
.white {
  background-color: #FFFFFF !important;
}
.white-text {
  color: #FFFFFF !important;
}
.transparent {
  background-color: transparent !important;
}
.transparent-text {
  color: transparent !important;
}

Thus with three colors, we have six CSS rules.


2: One Color: Simple

Consider, use the real color. It is very similar to above script.

SASS Source

I simplified the complex SASS from materialize CSS.

$red: (
  "base":       #F44336,
  "lighten-2":  #E57373,
  "lighten-1":  #EF5350,
);

$color_name: "red";
$color: $red;

@each $color_type, $color_value in $color {
  .#{$color_name}.#{$color_type} {
    background-color: $color_value !important;
  }
  .#{$color_name}-text.text-#{$color_type} {
    color: $color_value !important;
  }
}

LESS Port

The port is also very straightforward.

@red: {
  base:       #F44336;
  lighten-2:  #E57373;
  lighten-1:  #EF5350;
}

@color_name: red;
@color: @red;

each(@color, {
  .@{color_name}.@{key} {
    background-color: @value !important;
  }
  .@{color_name}-text.text-@{key} {
    color: @value !important;
  }
})

CSS Output

Notice that the output is still using .red.base {…}.

.red.base {
  background-color: #F44336 !important;
}
.red-text.text-base {
  color: #F44336 !important;
}
.red.lighten-2 {
  background-color: #E57373 !important;
}
.red-text.text-lighten-2 {
  color: #E57373 !important;
}
.red.lighten-1 {
  background-color: #EF5350 !important;
}
.red-text.text-lighten-1 {
  color: #EF5350 !important;
}

3: One Color: Conditional

Now comes the hard part. We will output .red {…}, instead of .red.base {…}.

Issue: LessJS does not have if then else rules.

SASS Source

Consider have a look at this SASS source first, so that you will have the idea.

$red: (
  "base":       #F44336,
  "lighten-2":  #E57373,
  "lighten-1":  #EF5350,
);

$color_name: "red";
$color: $red;

@each $color_type, $color_value in $color {
  @if $color_type == "base" {
    .#{$color_name} {
      background-color: $color_value !important;
    }
    .#{$color_name}-text {
      color: $color_value !important;
    }
  }
  @else {
    .#{$color_name}.#{$color_type} {
      background-color: $color_value !important;
    }
    .#{$color_name}-text.text-#{$color_type} {
      color: $color_value !important;
    }
  }
}

LESS Guard

Luckily we have guard in LESS.

each(@color, {
  @is-base: boolean(@key = base);

  & when (@is-base = true) {
    ...
  }

  & when (@is-base = false) {
    ...
  }
})

Notice, that first we need to define the truth:

  • @is-base: boolean(@key = base).

It needs more steps compared to SASS counterpart, but at least it works well.

LESS Port

The complete guard code is as below:

@red: {
  base:       #F44336;
  lighten-2:  #E57373;
  lighten-1:  #EF5350;
}

@color_name: red;
@color: @red;

each(@color, {
  @is-base: boolean(@key = base);

  & when (@is-base = true) {
    .@{color_name} {
      background-color: @value !important;
    }
    .@{color_name}-text {
      color: @value !important;
    }
  }

  & when (@is-base = false) {
    .@{color_name}.@{key} {
      background-color: @value !important;
    }
    .@{color_name}-text.text-@{key} {
      color: @value !important;
    }
  }
})

CSS Output

Notice that now, We have .red {…}, instead of .red.base {…}.

.red {
  background-color: #F44336 !important;
}
.red-text {
  color: #F44336 !important;
}
.red.lighten-2 {
  background-color: #E57373 !important;
}
.red-text.text-lighten-2 {
  color: #E57373 !important;
}
.red.lighten-1 {
  background-color: #EF5350 !important;
}
.red-text.text-lighten-1 {
  color: #EF5350 !important;
}

Mission accomplished, but consider move to this next issue.


4: Many Colors

Nested each can be an issue in lessjs, because nested use variable with the same name: @key and @value. But this also can be solved by separating the inner part in a function.

SASS Source

Consider see the original source frome Materialize CSS.

$red: (
  "base":       #F44336,
  "lighten-2":  #E57373,
  "lighten-1":  #EF5350,
);

$blue: (
  "base":       #2196F3,
  "lighten-2":  #64B5F6,
  "lighten-1":  #42A5F5,
);

$colors: (
  "red": $red,
  "blue": $blue
);

@each $color_name, $color in $colors {
  @each $color_type, $color_value in $color {
    @if $color_type == "base" {
      .#{$color_name} {
        background-color: $color_value !important;
      }
      .#{$color_name}-text {
        color: $color_value !important;
      }
    }
    @else if $color_name != "shades" {
      .#{$color_name}.#{$color_type} {
        background-color: $color_value !important;
      }
      .#{$color_name}-text.text-#{$color_type} {
        color: $color_value !important;
      }
    }
  }
}

LESS Nested Each

The skeleton is as simple as code below:

.foo_color(@color_name, @color) {
  each(@color, {
    ...
  })
}

each(@colors, {
  .foo_color(@key, @value)
})

LESS Port

But the real code is actually, a little bit longer.

@red: {
  base:       #F44336;
  lighten-2:  #E57373;
  lighten-1:  #EF5350;
}

@blue: {
  base:       #2196F3;
  lighten-2:  #64B5F6;
  lighten-1:  #42A5F5;
}

@colors: {
  red: @red;
  blue: @blue;
}

.foo_color(@color_name, @color) {
  each(@color, {
    @is-base: boolean(@key = base);

    & when (@is-base = true) {
      .@{color_name} {
        background-color: @value !important;
      }
      .@{color_name}-text {
        color: @value !important;
      }
    }

    & when (@is-base = false) {
      .@{color_name}.@{key} {
        background-color: @value !important;
      }
      .@{color_name}-text.text-@{key} {
        color: @value !important;
      }
    }
  })
}

each(@colors, {
  .foo_color(@key, @value)
})

CSS Output

Now the CSS result.

.red {
  background-color: #F44336 !important;
}
.red-text {
  color: #F44336 !important;
}
.red.lighten-2 {
  background-color: #E57373 !important;
}
.red-text.text-lighten-2 {
  color: #E57373 !important;
}
.red.lighten-1 {
  background-color: #EF5350 !important;
}
.red-text.text-lighten-1 {
  color: #EF5350 !important;
}
.blue {
  background-color: #2196F3 !important;
}
.blue-text {
  color: #2196F3 !important;
}
.blue.lighten-2 {
  background-color: #64B5F6 !important;
}
.blue-text.text-lighten-2 {
  color: #64B5F6 !important;
}
.blue.lighten-1 {
  background-color: #42A5F5 !important;
}
.blue-text.text-lighten-1 {
  color: #42A5F5 !important;
}

What is Next ?

Beside this conditional, there is also loop article, that you might need to read. Consider going back reading [ Less - Loop - Spacing Class ].

Thank you for reading.


Conclusion

These color classes is ready to serve.

lessjs: output

What do you think ?