This week I started to build a Less version for the Ionic framework. The Ionic framework, or shortly Ionic is a beautiful, open source front-end SDK for developing hybrid mobile apps with HTML5.
Why should you need a Less version when there’s already a SASS version? Well, I know that Less vs SASS had been discussed a thousands times. Personally i like Less because of its syntax stays as close as possible to the declarative nature of CSS. But i also really believe that Less is more suitable than SASS for CSS framework like Ionic because of its lazy loading nature. Lazy loading and apply the last declaration wins rule, which means that you can easily override any variable by putting the definition afterwards.
The example from the official Less documentation shows you what the preceding exactly means:
// library
@base-color: green;
@dark-color: darken(@base-color, 10%);// use of library
@import “library.less”;
@base-color: red;
Secondly having a Less version will enable me to write a Less plugin for Ionic. This plugin imports the library before your custom code, without the need to explicit import it in your code. After installing the plugin you can simply compile your code with the following command:
lessc apllication.less application.css –ionic
Read now the Formula which describe how to deploy Ionic apps with Less: Customize your Ionic app’s theme with Less. This Formula uses the Less autoprefix plugin and the Less ionic plugin.
Migrate Ionic from SASS to Less
I decide to do the migration in 3 steps:
- First rewrite the basic system in Less; reset, scaffolding, type and grid (also ionicons cause they’ve got a Less version already)
- Include the autoprefix plugin in the build chain
- Rewrite other code, including components
About the autoprefixer
I found that many the purpose of many of the current mixins in Ionic is only adding CSS vendor prefixers. The / a autoprefix postprocessor can add these vendor prefixers automatically. Since version 2 of Less you can also use the Less autoprefix plugin of this task. Alternatively you can use gulp-autoprefix in the build chain too. Also notice there is no reason to not use the autoprefixer with SASS too.
How does this works?
The SASS code contains the following mixin:
@mixin align-items($value: stretch) {
@if $value == flex-start {
-webkit-box-align: start;
-ms-flex-align: start;
} @else if $value == flex-end {
-webkit-box-align: end;
-ms-flex-align: end;
} @else {
-webkit-box-align: $value;
-ms-flex-align: $value;
}
-webkit-align-items: $value;
-moz-align-items: $value;
align-items: $value;
}
To do the same in Less, you will need some kind of helper mixin, as can be seen in the following code:
.ms-align-items(@value: stretch) when (@value = flex-start) {
-webkit-box-align: start;
-ms-flex-align: start;
}
.ms-align-items(@value) when (@value = flex-end) {
-webkit-box-align: end;
-ms-flex-align: end;
}
.ms-align-items(@value) when (default()) {
-webkit-box-align: @value;
-ms-flex-align: @value;
}.align-items(@value) {
.ms-align-items(@value);
-webkit-align-items: @value;
-moz-align-items: @value;
align-items: @value;
}
When using the mixins above:
div.flex-end {
@include align-items(flex-end);
}
or
div.flex-end {
.align-items(flex-end);
}
outputs:
div.flex-end {
-webkit-box-align: end;
-ms-flex-align: end;
-webkit-align-items: flex-end;
-moz-align-items: flex-end;
align-items: flex-end; }
When using the autoprefixer you Less (or SASS) code will look like that shown below to do the same:
div.flex-end {
align-items: flex-end;
}
The following command:
echo “div.flex-end { align-items: flex-end; }” | lessc – –autoprefix
outputs:
div.flex-end {
-webkit-box-align: end;
-webkit-align-items: flex-end;
-ms-flex-align: end;
align-items: flex-end;
}
The preceding look well, but the -moz-align-items: flex-end;
declaration is missing. This can be fixed by brute adding prefixes for even the most old browsers by running:
echo “div.flex-end { align-items: flex-end; }” | lessc – –autoprefix=”last 20 versions”
The above indeed adds the -moz-align-items: flex-end;
declaration, but it seems nicer to set the supported browser more specific. I could not find which browsers Ionic should support. But i found that it support only browsers with flexbox support. So for now i will use the following command:
echo “div.flex-end { align-items: flex-end; }” | lessc – –autoprefix=”Android >= 2.1,BlackBerry >= 7,Chrome >= 20,Firefox >= 21,Explorer >= 10,iOS >= 3.2,Opera > 12,Safari > 6,OperaMobile >= 12.1,ChromeAndroid >= 40,FirefoxAndroid >= 30,ExplorerMobile >= 10″
Now the above commands outputs as desired:
div.flex-end {
-webkit-box-align: end;
-webkit-align-items: flex-end;
-moz-box-align: end;
-ms-flex-align: end;
align-items: flex-end;
}
When applying all the above the Less gulp task should look like that shown beneath:
gulp.task(‘less’, function(done) {
var LessPluginAutoprefix = require(‘less-plugin-autoprefix’),
autoprefix = new LessPluginAutoprefix({ browsers: [“Android >= 2.1″,”BlackBerry >= 7″,”Chrome >= 20″,”Firefox >= 21″,”Explorer >= 10″,”iOS >= 3.2″,”Opera > 12″,”Safari > 6″,”OperaMobile >= 12.1″,”ChromeAndroid >= 40″,”FirefoxAndroid >= 30″,”ExplorerMobile >= 10”] });gulp.src(‘less/ionic.less’)
.pipe(header(banner))
.pipe(less({
plugins: [autoprefix],
onError: function(err) {
//If we’re watching, don’t exit on error
if (IS_WATCH) {
console.log(gutil.colors.red(err));
} else {
done(err);
}
}
}))
.pipe(concat(‘ionic.css’))
.pipe(gulp.dest(buildConfig.dist + ‘/css’))
.pipe(gulpif(IS_RELEASE_BUILD, minifyCss()))
.pipe(rename({ extname: ‘.min.css’ }))
.pipe(gulp.dest(buildConfig.dist + ‘/css’))
.on(‘end’, done);
});
Should both SASS and Less code maintained?
As has been mentiond rightly at https://github.com/driftyco/ionic/issues/2252 maintaining two version does not seem teh right way to go. Possible fixes: Bootstrap SASS has been automaticaly created from the Less version and @kristoferjoseph from flexboxgrid.com suggests to build a CLI /JavaScript process that ouputs code for different preprocessors.
—
update
Autoprefixing:
The .touch-callout(none);
mixin add only the -webkit-touch-callout: @value;
. The autoprefixer does not efficient solve the preceding. When we code touch-callout: none;
the -webkit-
is not add (and the unprefixed property is not removed). On the other hand it is not sure if the right prefixes are addded in future, when we code -webkit-touch-callout: @value;
. The .touch-callout(none);
mixins in not removed for now.
I also found a difference for the order
property.
When coding order: 3
the original mixin outputs:
-webkit-box-ordinal-group: 3;
-webkit-order: 3;
-moz-box-ordinal-group: 3;
-ms-flex-order: 3;
order: 3;
The autoprefixer outputs in this situation:
-webkit-box-ordinal-group: 4;
-webkit-order: 3;
-moz-box-ordinal-group: 4;
-ms-flex-order: 3;
order: 3;
Although i could not find any documentation that suggest to increase the value of the *-box-ordinal-group
, the incrementation could make sense cause the initial value of *-box-ordinal-group
property is 1
whilst the order property
has got an initial value of 0
.
The -webkit-font-smoothing
property is only be used by Webkit browsers. Because of -webkit-font-smoothing
has no official syntax, this property is also not supported by the autoprefixer. There is no reason to expect that other browser will start using this property in future. Also the original mixin seems to be incorrect because of the font-smoothing
property does not exist:
.font-smoothing(@font-smoothing) {
-webkit-font-smoothing: @font-smoothing;
font-smoothing: @font-smoothing;
}
The mixin has been removed from the Less code in favor of coding -webkit-font-smoothing
directly. The same seem true for the .touch-callout(none);
mixin hindsight. Also the .touch-callout(none);
mixin has been removed.