Forget Lesshat – More-or-less is smarter and faster


Forget Lesshat – More-or-less is smarter and faster

More-or-less – the intelligent mixin-library

Screen Shot 2014-04-06 at 20.04.38
Documentation Source

Less hassle more fun

Preprocessors have become a standard. SASS/SCSS, Less or Stylus, no matter which one you use, they all make your life easier.

I have been a SASS/SCSS user over the last years. The main reason was that it offered a lot more features. I currently work for a company that mainly uses Less, so I had to find the good parts about it.

CSS3 mixins

Nowadays I tend to use autoprefixer instead of using mixins to do the job. But I guess there are still a lot of people that use CSS3 mixins. Even I can’t always work with autoprefixer. When working with Less I started using Less Hat a while ago but soon realized that it was not all that good. It requires some ugly syntax for some mixins and isn’t really fast. When I write code I want the preprocessor to do it’s job really fast but sometimes it takes a few seconds (worst case I had was 6 seconds). So I started to write my own mixin-library More-or-less.

So I didn’t just write standard mixins, no I gave it a thought and tried to optimise the way they work.
Many of you might have seen mixins like this one:

.border-radius(@values) {
    -webkit-border-radius: @values;
    -moz-border-radius: @values;
    -o-border-radius: @values;
    border-radius: @values;
}

So I thought, this could be done better and came up with a mixin that looked like this:

.border-radius(@values) {
    @vendorPrefixes: -webkit-, -moz-, '';
    @prop: border-radius;
    .prefix();
}

Now that looked a lot better. But how does it work? And what is this .prefix(); mixin doing?

The library is built on a few smart functions that help to write these smart mixins.

Functions

I started to write a few mixins that were actually functions. Some are .for(), .inArray(), .if()
These mixins are mainly used for other mixins to work.
The .for() mixin allows to create loops.
The .inArray() mixin compares a value to a list/array.
The .if() mixin let’s you write if-then-else statements without guards.

So where you would normally write:

& when (lightness(@color) > 50%) {
    background: #234;
}
& when not (lightness(@color) > 50%) {
    background: #fde;
}

I write:

.if(lightness(@color) gt 50%, {
    .-then(){
        background: #234;
    }
    .-else(){
        background: #fde;
    }
});

While it basically does the same thing it just looks a little more intuitive. Syntactic sugar FTW.

But what about loops?
Instead of writing

.item-1 {
    left: 0;
}
.item-2 {
    left: 50px;
}
.item-3 {
    left: 100px;
}
.item-4 {
    left: 150px;
}

I write:

.for(4);.-each(@i) {
    .item-@{i} {
        left:((@i - 1) * 50px)
    }
}

Hm.. so this looks good… right?

More-or-less offers a few of these helpers and they come in handy.
See how I build the .prefix() mixin

@import '../../vars/_prefix';
@import '../../../fn/_inArray';
@import '../../../fn/_scope';
@import '../../../fn/_if';
.prefix(...) {
    @needPrefix: transition-property, transition;
    .for(@vendorPrefixes); .-each(@i){
        .inArray(@needPrefix, @prop);
        .if(@-, {
            .-then(){
                @transform: replace('@{values}', 'transform', '@{i}transform');
                @box-shadow: replace('@{transform}', 'box-shadow', '@{i}box-shadow');
                @end: e(@box-shadow);
                @return: @end;
            }
            .-else() {
                @return: @values;
            }
        });
        .scope({
            .inArray(@prefixes, @i);
            .if(@-,{
                .-then(){
                    @eProp: e('@{i}@{prop}');
                    @{eProp}: @return;
                }
                .-else(){
                    @{prop}: @values;
                }
            });
        });
    }
}

As you can see, I am using a few of the helpers to generate the code for most of the CSS3 mixins.
If you look at the beginning you can see that I am importing all dependencies in the top of the file.
This way I can be sure that even if I only need one mixin every dependency is available.
This also makes More-or-less fully modular.

I have been using this technique in other projects aswell.

So what if I decide to switch to autoprefixer?
No problem. …There is a global variable @prefixes. SO when I set it to @prefixes: 'none' I won’t get any vendor-prefixed properties and can run the autoprefixer to do the job. This way it is really easy to maintain or modify the code as desired.

You can look at the Hompage http://more-or-less.org. It is written with the library and the @prefixes variable os set to 'none'. from a grunt-task. Then autoprefixer is used to create the vendor-prefixes.

More-or-less is opensource and I am always looking forward to feedback or contributions. While the library is still young I have released 6 versions so far. I compiled a HUGE file with Less Hat and More-or-less.
Less Hat: ~30 seconds
More-or-less: ~8 seconds
This shows that More-or-less is almost 4 times as fast when writing big files. Of course this is an illogical usecase, because it has about 1000 lines of CSS3 mixins. But if used on a standard file More-or-less is still twice as fast as Less Hat

But how is this possible?
Less Hat uses inline-javascript to generate the vendor-prefixes while More-or-less uses Less‘s built in functions and the helpers.

So maybe you decide to give it a try in your next project or fork it and start contributing.

It’s time for a change….

Follow me

Gregor Adams

web developer at Codefights
Hi, I am a web-programmer and frontend-developer from Germany.
I specialize in CSS, HTML and JavaScript. As I am a huge fan and also a student of open-source, I try to help other programmers (especially newcomers) as much as I can. I am also very happy for "true" criticism. So feel free to ask any question you like and I will try to give the best possible answer.
Follow me

Hi, I am a web-programmer and frontend-developer from Germany. I specialize in CSS, HTML and JavaScript. As I am a huge fan and also a student of open-source, I try to help other programmers (especially newcomers) as much as I can. I am also very happy for "true" criticism. So feel free to ask any question you like and I will try to give the best possible answer.