Introduction
Roole is a language that compiles to CSS.
It drew many inspirations from other CSS preprocessing languages like Sass, LESS and Stylus.
The most unique feature of Roole is that it has vendor prefixing built-in, so the language stays dead simple yet being able to prefix some extremely complex rules transparently. Since Roole is also a superset of CSS, you can use it directly as a CSS prefixer.
Roole is implemented in JavaScript, so it can be run both on the server side (via node.js) or in a browser (run unit tests to check if Roole works in your browser).
Overview
Roole is a superset of CSS:
body { margin: 0 }
body {
margin: 0;
}
Store repeating values in variables:
$position = left;
#sidebar {
float: $position;
margin-$position: 20px;
}
#sidebar {
float: left;
margin-left: 20px;
}
Conditonally generate rules:
$support-old-ie = false;
li {
@if $support-old-ie {
display: inline;
}
float: left;
margin-left: 10px;
}
li {
float: left;
margin-left: 10px;
}
Quickly generate many rules:
@for $i in 1..3 {
.span-$i {
width: $i * 60px;
}
}
.span-1 {
width: 60px;
}
.span-2 {
width: 120px;
}
.span-3 {
width: 180px;
}
Define your own way of generating rules:
$button = @function $color, $bg-color {
display: inline-block;
color: $color;
background-color: $bg-color;
};
.submit {
@mixin $button(black, white);
}
.reset {
@mixin $button(red, white);
}
.submit {
display: inline-block;
color: black;
background-color: white;
}
.reset {
display: inline-block;
color: red;
background-color: white;
}
Or simply extend already defined rules:
.button {
display: inline-block;
color: black;
background-color: white;
}
.submit {
@extend .button;
}
.reset {
@extend .button;
color: red;
}
.button,
.submit,
.reset {
display: inline-block;
color: black;
background-color: white;
}
.reset {
color: red;
}
And forget about prefixing:
@keyframes become-round {
from {
border-radius: 0;
}
to {
border-radius: 50%;
}
}
@-webkit-keyframes become-round {
from {
-webkit-border-radius: 0;
border-radius: 0;
}
to {
-webkit-border-radius: 50%;
border-radius: 50%;
}
}
@-moz-keyframes become-round {
from {
-moz-border-radius: 0;
border-radius: 0;
}
to {
-moz-border-radius: 50%;
border-radius: 50%;
}
}
@-o-keyframes become-round {
from {
border-radius: 0;
}
to {
border-radius: 50%;
}
}
@keyframes become-round {
from {
border-radius: 0;
}
to {
border-radius: 50%;
}
}
Installation
Node.js
Run command:
npm install roole -g
Browser
Insert the downloaded file into HTML:
<script src="/path/to/roole.js"></script>
Usage
Command line
Compile a single file:
roole /path/to/style.roo
For more usage on the roole command, please run roole -h.
Browser
Link to an external file:
<link rel="stylesheet/roole" href="styles.roo">
Or embed code directly:
<style type="text/roole">
// put code here
</style>
JavaScript API
roole.compile(input, options, callback)
input- the source code stringoptions- an optional hash object which supports the following options:indent(default:"\t") - indentation string to use in CSSprecision(default:3) - Max number of decimal digits to use in CSSprefix(default:"webkit moz ms o") - space-separated vendor names to use for prefixingskipPrefixed(default:false) - Not generate prefixed rule that already exists
callback(error, css)- this function will be called when CSS is generated:error-nullif there was no error when generating CSS, otherwise an error objectcss- the generated CSS string
Language
Rule Set
Rule sets can be nested, and their selectors will be joined together:
#header {
.logo {
float: left;
}
}
#header .logo {
float: left;
}
ul {
overflow: hidden;
> li {
float: left;
}
}
ul {
overflow: hidden;
}
ul > li {
float: left;
}
#main, #sidebar {
h1, h2 {
color: #333;
}
}
#main h1,
#main h2,
#sidebar h1,
#sidebar h2 {
color: #333;
}
Use the & selector to reference the outer rule set's selector:
a {
&:hover {
text-decoration: underline;
}
}
a:hover {
text-decoration: underline;
}
img {
a & {
border: none;
}
}
a img {
border: none;
}
The & selector can also be nested:
a {
&:hover {
text-decoration: underline;
.button & {
text-decoration: none;
}
}
}
a:hover {
text-decoration: underline;
}
.button a:hover {
text-decoration: none;
}
@media
@media can be nested into rule sets:
#sidebar {
@media print {
display: none;
}
}
@media print {
#sidebar {
display: none;
}
}
#container {
@media print {
.sidebar {
display: none;
}
}
}
@media print {
#container .sidebar {
display: none;
}
}
Or within one another, and their media queries will be joined together:
@media screen {
a {
color: blue;
@media (monochrome) {
color: black;
}
}
}
@media screen {
a {
color: blue;
}
}
@media screen and (monochrome) {
a {
color: black;
}
}
Comment
Both single-line commnets // and multi-line commnets /* */ are supported:
/*
* Box module
*
* Display a nice box
*/
.box {
float: left;
margin-left: 20px;
// Fix IE6
display: inline;
}
/*
* Box module
*
* Display a nice box
*/
.box {
float: left;
margin-left: 20px;
display: inline;
}
Currently only the first top-level multi-line comment will be preserved in the generated CSS, all other comments are discarded.
Variable
Variables are case-sensitive. Their names start with $ and are defined using assignments:
$margin = 20px 0;
$MARGIN = 30px 0;
p {
margin: $margin;
}
p {
margin: 20px 0;
}
If variables are assigned using ?=, the assigments will only success if the variables is currently undefined:
$margin = 20px 0;
$margin ?= 30px 0;
p {
margin: $margin;
}
p {
margin: 20px 0;
}
$margin ?= 0 20px;
p {
margin: $margin;
}
p {
margin: 0 20px;
}
Variables are allowed where values like numbers, strings, identifiers, etc are allowed:
$tag = body;
$tag {
margin: 0;
}
body {
margin: 0;
}
$attribute = type;
$value = button;
input[$attribute=$value] {
border: none;
}
input[type=button] {
border: none;
}
$property = margin;
$value = 20px;
p {
$property: $value;
}
p {
margin: 20px;
}
$feature = max-width;
$value = 1024px;
@media ($feature: $value) {
#main {
width: 960px;
}
}
@media (max-width: 1024px) {
#main {
width: 960px;
}
}
When being assigned with an string value, variables can also be used as selectors:
$selector = '#sidebar a';
$selector {
color: green;
}
#sidebar a {
color: green;
}
$tab = '.tabs .tab';
#sidebar $tab {
padding: 5px;
}
#sidebar .tabs .tab {
padding: 5px;
}
This also works for media queries:
$query = '(max-width: 1024px)';
@media $query {
body {
width: 960px;
}
}
@media (max-width: 1024px) {
body {
width: 960px;
}
}
$lt-desktop = '(max-width: 979px)';
$gt-phone = '(min-width: 768px)';
@media $gt-phone and $lt-desktop {
body {
width: 960px;
}
}
@media (min-width: 768px) and (max-width: 979px) {
body {
width: 960px;
}
}
Variables can also be used in interpolations (see the next section).
Interpolation
Variables can be interpolated into doubled-quoted strings:
$number = 12;
.heading::before {
content: "Chapter $number: ";
}
.heading::before {
content: "Chapter 12: ";
}
But not single-quoted strings:
.heading::before {
content: 'Chapter $num: ';
}
.heading::before {
content: 'Chapter $num: ';
}
Variables can also be interpolated into identifiers:
$name = star;
.icon-$name {
width: 20px;
height: 20px;
}
.icon-star {
width: 20px;
height: 20px;
}
$position = left;
.sidebar {
padding-$position: 20px;
border-$position: 1px solid;
}
.sidebar {
padding-left: 20px;
border-left: 1px solid;
}
Use \$ to escape variables inside strings:
.heading::before {
content: "Chapter \$number: ";
}
.heading::before {
content: "Chapter \$number: ";
}
Wrap the variable in curly braces {} to seperate characters come after it, which would otherwise be part of its name:
$chapter = 4;
.figcaption::before {
content: "Figure {$chapter}-12: ";
}
.figcaption::before {
content: "Figure 4-12: ";
}
$position = left;
.sidebar {
border-{$position}-width: 1px;
}
.sidebar {
border-left-width: 1px;
}
Use \{ to escape it:
$chapter = 4;
.figcaption::before {
content: "Figure \{$chapter}-12: ";
}
.figcaption::before {
content: "Figure \{4}-12: ";
}
If the curly brace does not form an interpolation there is no need to escape it:
.figcaption::before {
content: "Figure {\$chapter}-12: ";
}
.figcaption::before {
content: "Figure {\$chapter}-12: ";
}
.title::before {
content: "latex \\hat{x}";
}
.title::before {
content: "latex \\hat{x}";
}
Operation
Arithmetic operators +, -, *, / and parentheses () are supported:
$total = 250px;
$padding = 20px;
$border = 1px;
#sidebar {
width: $total - ($padding + $border) * 2;
padding: 0 $padding;
border-width: $border;
}
#sidebar {
width: 208px;
padding: 0 20px;
border-width: 1px;
}
At lease one space should exist around /, otherwise it is generated literally:
body {
font: 14px/1.25 sans-serif;
}
body {
font: 14px/1.25 sans-serif;
}
@media (device-aspect-ratio: 16/9) {
body {
background: url(bg-16-9.png);
}
}
@media (device-aspect-ratio: 16/9) {
body {
background: url(bg-16-9.png);
}
}
At lease one space should exist on the right side of +(-), or no space exists on both side, otherwise unary +(-) is applied:
#box {
margin: 40px -20px;
}
#box {
margin: 40px -20px;
}
#box {
margin: 40px +20px;
}
#box {
margin: 40px 20px;
}
Arithmetic operations can be combined with assignments:
$text = 'Hello, ';
.guest::before {
$text += 'Guest';
content: $text;
}
.guest::before {
content: 'Hello, Guest';
}
Comparison operation are also supported, which comes in handy when specifying @if conditions (see the next section).
@if
@if allows rules inside it to be conditionally generated:
$support-old-ie = false;
li {
@if $support-old-ie {
display: inline;
}
float: left;
margin-left: 10px;
}
li {
float: left;
margin-left: 10px;
}
Sample truthy values: true, 12, 0.5em, '0'. Sample falsey values: false, 0, 0px, "".
@if can be followed by any number of @else if and optionally one @else:
$color = black;
body {
@if $color is white {
background: #fff;
} @else if $color is black {
background: #000;
} @else if $color is gray {
background: #999;
} @else {
background: url(bg.png);
}
}
body {
background: #000;
}
This example also demonstrates the use of is operator.
Like is, which tests equalitys, isnt tests inequality:
$size = large;
.button {
@if $size isnt small {
border: 1px solid;
}
}
.button {
border: 1px solid;
}
and expects values on its both sides to be truthy:
$size = large;
$type = split;
.button {
@if $size is large and $type is split {
padding: 10px;
}
}
.button {
padding: 10px;
}
or expects values on its either sides to be truthy:
$size = large;
.button {
@if $size is medium or $size is large {
border: 1px solid;
}
}
.button {
border: 1px solid;
}
<, <=, > and >= compare numeric values:
$width = 100px;
.button {
@if $width < 100px {
border: none;
} @else if $width >= 100px and $width < 200px {
border: 1px solid;
}
}
.button {
border: 1px solid;
}
Sample numberic values: 1.2, 2em, 50%.
@for
@for allows rules in it to be generated multiple times:
@for $i in 1..3 {
.span-$i {
width: $i * 60px;
}
}
.span-1 {
width: 60px;
}
.span-2 {
width: 120px;
}
.span-3 {
width: 180px;
}
Values like 1..3 are ranges, and are inclusive. Use ... to denote an exclusive range:
@for $i in 1...3 {
.span-$i {
width: $i * 60px;
}
}
.span-1 {
width: 60px;
}
.span-2 {
width: 120px;
}
Ranges can be in reversed order:
@for $i in 3..1 {
.span-$i {
width: $i * 60px;
}
}
.span-3 {
width: 180px;
}
.span-2 {
width: 120px;
}
.span-1 {
width: 60px;
}
@for $i in 3...1 {
.span-$i {
width: $i * 60px;
}
}
.span-3 {
width: 180px;
}
.span-2 {
width: 120px;
}
Ranges are essentially lists, so regular lists works as well:
@for $icon in arrow star heart {
.icon-$icon {
background: url("$icon.png");
}
}
.icon-arrow {
background: url("arrow.png");
}
.icon-star {
background: url("star.png");
}
.icon-heart {
background: url("heart.png");
}
Lists are values separated by spaces, commas ,, or slashs /.
To specify a step other than 1, use by:
@for $i by 2 in 1..5 {
.span-$i {
width: $i * 60px;
}
}
.span-1 {
width: 60px;
}
.span-3 {
width: 180px;
}
.span-5 {
width: 300px;
}
If step is a negative number, the order of iteration is reversed:
@for $i by -1 in 1..2 {
.span-$i {
width: $i * 60px;
}
}
.span-2 {
width: 120px;
}
.span-1 {
width: 60px;
}
@for $i by -1 in 2..1 {
.span-$i {
width: $i * 60px;
}
}
.span-1 {
width: 60px;
}
.span-2 {
width: 120px;
}
To access indices, specify one more variable:
@for $icon, $i in arrow star heart {
.icon-$icon {
background-position: 0 $i * 20px;
}
}
.icon-arrow {
background-position: 0 0px;
}
.icon-star {
background-position: 0 20px;
}
.icon-heart {
background-position: 0 40px;
}
@function
You can store blocks of rules in @function, and mix them in other places with @mixin:
$clearfix = @function {
*zoom: 1;
&:before,
&:after {
content: " ";
display: table;
}
&:after {
clear: both;
}
};
ul {
@mixin $clearfix();
}
ul {
*zoom: 1;
}
ul:before,
ul:after {
content: " ";
display: table;
}
ul:after {
clear: both;
}
(Hat tip to Nicolas Gallagher, for this example uses his micro clearfix)
Or you can ask it to perform some calculations, and return the value with @return:
$width = @function {
$side-bar = 250px;
$main = 710px;
@return $side-bar + $main;
};
body {
width: $width();
}
body {
width: 960px;
}
@function can have parameters, which can also have a default value:
$button = @function $color, $bg-color, $size = large {
color: $color;
background-color: $bg-color;
@if $size is small {
font-size: 12px;
} @else if $size is large {
font-size: 14px;
}
};
#submit {
@mixin $button(#000, #fff);
}
#submit {
color: #000;
background-color: #fff;
font-size: 14px;
}
Arguments passed to a function can also be accessed dynamically using $arguments:
$social-icons = @function {
@for $icon in $arguments {
.icon-$icon {
background: url("$icon.png");
}
}
};
#social {
@mixin $social-icons(twitter, facebook);
}
#social .icon-twitter {
background: url("twitter.png");
}
#social .icon-facebook {
background: url("facebook.png");
}
Use rest parameter to capture multiple arguments:
$social-icons = @function $size, ...$icons {
@for $icon in $icons {
.icon-$icon {
background: url("$size/$icon.png");
}
}
};
#social {
@mixin $social-icons(large, twitter, facebook);
}
#social .icon-twitter {
background: url("large/twitter.png");
}
#social .icon-facebook {
background: url("large/facebook.png");
}
Unassigned parameters have value null:
$button = @function $color, $bg-color, $size {
color: $color;
background-color: $bg-color;
@if $size is null {
font-size: 12px;
} @else {
font-size: 14px;
}
};
#submit {
@mixin $button(#000, #fff);
}
#submit {
color: #000;
background-color: #fff;
font-size: 12px;
}
@extend
@extend extends other rule sets of the matching selectors:
.button {
display: inline-block;
border: 1px solid;
}
.large-button {
@extend .button;
display: block;
}
.button,
.large-button {
display: inline-block;
border: 1px solid;
}
.large-button {
display: block;
}
@extend works recursively:
.button {
display: inline-block;
border: 1px solid;
}
.large-button {
@extend .button;
display: block;
}
#submit {
@extend .large-button;
margin: 0 20px;
}
.button,
.large-button,
#submit {
display: inline-block;
border: 1px solid;
}
.large-button,
#submit {
display: block;
}
#submit {
margin: 0 20px;
}
@extend can be specified multiple times:
.button {
display: inline-block;
border: 1px solid;
}
.large-button {
@extend .button;
display: block;
}
.dangerous-button {
@extend .button;
color: #fff;
background: red;
}
#reset {
@extend .large-button;
@extend .dangerous-button;
margin: 0 20px;
}
.button,
.large-button,
.dangerous-button,
#reset,
#reset {
display: inline-block;
border: 1px solid;
}
.large-button,
#reset {
display: block;
}
.dangerous-button,
#reset {
color: #fff;
background: red;
}
#reset {
margin: 0 20px;
}
Or simply use a list of selectors:
.button {
display: inline-block;
border: 1px solid;
}
.large-button {
@extend .button;
display: block;
}
.dangerous-button {
@extend .button;
color: #fff;
background: red;
}
#reset {
@extend .large-button, .dangerous-button;
margin: 0 20px;
}
.button,
.large-button,
.dangerous-button,
#reset,
#reset {
display: inline-block;
border: 1px solid;
}
.large-button,
#reset {
display: block;
}
.dangerous-button,
#reset {
color: #fff;
background: red;
}
#reset {
margin: 0 20px;
}
Complex selectors also work as intended:
.button .icon {
font-family: icon-font;
}
.button .edit-icon {
@extend .button .icon;
content: 'i';
}
.button .icon,
.button .edit-icon {
font-family: icon-font;
}
.button .edit-icon {
content: 'i';
}
Note that selectors are matched exactly, so .icon will not match .button .icon:
.icon {
font-family: icon-font;
}
.button .icon {
font-family: button-font;
}
.button .edit-icon {
@extend .icon;
content: 'i';
}
.icon,
.button .edit-icon {
font-family: icon-font;
}
.button .icon {
font-family: button-font;
}
.button .edit-icon {
content: 'i';
}
And @extend will not extend rule sets that come after it:
.button {
display: inline-block;
}
#submit {
@extend .button;
}
.button {
display: block;
}
.button,
#submit {
display: inline-block;
}
.button {
display: block;
}
When using @extend under a @media, it will only match rule set under @media with the same media query:
.button {
display: block;
}
@media (min-width: 512px) {
.button {
display: inline-block;
}
}
@media (min-width: 1024px) {
.button {
display: inline-block;
border: 1px solid;
}
}
@media (min-width: 512px) {
#submit {
@extend .button;
}
}
.button {
display: block;
}
@media (min-width: 512px) {
.button,
#submit {
display: inline-block;
}
}
@media (min-width: 1024px) {
.button {
display: inline-block;
border: 1px solid;
}
}
@void
Rule sets inside @void are removed from the CSS output, unless they are extended by a rule set not inside a @void, but original selectors are always removed:
@void {
.button {
display: inline-block;
}
.tabs {
.tab {
@extend .button;
float: left;
}
}
}
#submit {
@extend .button;
}
#submit {
display: inline-block;
}
@import
@import imports rules from other files:
.tabs {
.tab {
float: left;
}
}
@import './tabs';
.tabs .tab {
float: left;
}
.roo will be added if the file's name doesn't end with an extension.
Use relative paths (i.e., paths start with ./ or ../) to import files, future version will use paths like 'tabs' to import libraries.
Files are not imported if their paths are sepecified using url(), starting with a protocol like http://, or followed by a media query:
@import url(./tabs);
@import url("./tabs");
@import "http://example.com/tabs";
@import './tabs' screen;
@import url(./tabs);
@import url("./tabs");
@import "http://example.com/tabs";
@import './tabs' screen;
Variables will be allowed in paths in a future version.
Files are only imported once:
body {
margin: 0;
}
@import './reset';
.tabs {
.tab {
float: left;
}
}
@import './reset';
.button {
display: inline-block;
}
@import './reset';
@import './tabs';
@import './button';
body {
margin: 0;
}
.tabs .tab {
float: left;
}
.button {
display: inline-block;
}
@import can be nested inside other rules:
.sidebar {
float: left;
margin-left: 20px;
}
.sidebar {
display: inline;
}
@import './sidebar';
$support-old-ie = false;
@if $support-old-ie {
@import './sidebar-old-ie';
}
.sidebar {
float: left;
margin-left: 20px;
}
.button {
display: inline-block;
}
.tabs .tab {
float: left;
}
@void {
@import './framework';
}
#submit {
@extend .button;
}
#submit {
display: inline-block;
}
Variable scope in the imported file is explained in the next section.
Scope
Variables have to be defined before they can be used. Once defined, they are only available within the boundary of their respective rule block, which defines their scope:
$menu = @function {
$width = 200px;
width: $width;
};
.menu {
@mixin $menu();
// $width is undefined here
}
.menu {
width: 200px;
}
Changing variables in an inner scope has no effect in outer scopes:
$width = 200px;
.mini-menu {
$width = 100px;
width: $width;
}
.menu {
width: $width;
}
.mini-menu {
width: 100px;
}
.menu {
width: 200px;
}
Variables defined in an imported file are exposed to the importing file:
$color = #000;
$bg-color = #fff;
@import './vars';
#main {
color: $color;
background: $bg-color;
}
#main {
color: #000;
background: #fff;
}
The imported file also has access to variables defined the importing file:
#main {
color: $color;
background: $bg-color;
}
$color = #000;
$bg-color = #fff;
@import './base';
#main {
color: #000;
background: #fff;
}
@block
@block simply introduces a new scope, which can be used to prevent the imported file from polluting variables, and easily use default values in them:
$width = 200px;
.foo-module {
width: $width;
}
$width ?= 100px;
.bar-module {
width: $width;
}
@block {
@import './foo-framework';
}
@block {
@import './bar-framework';
}
.foo-module {
width: 200px;
}
.bar-module {
width: 100px;
}
@module
@module prepends a name to all class selectors inside it:
@module foo {
.button {
display: inline-block;
}
.tabs {
.tab {
float: left;
&.active {
border-bottom: none;
}
}
}
}
.foo-button {
display: inline-block;
}
.foo-tabs .foo-tab {
float: left;
}
.foo-tabs .foo-tab.foo-active {
border-bottom: none;
}
It can be used to prevent class name collisions between your project and frameworks.
By default it uses - as a separator, you can specifiy a different one using with:
@module foo with '--' {
.button {
display: inline-block;
}
.tabs {
.tab {
float: left;
&.active {
border-bottom: none;
}
}
}
}
.foo--button {
display: inline-block;
}
.foo--tabs .foo--tab {
float: left;
}
.foo--tabs .foo--tab.foo--active {
border-bottom: none;
}
@module can be nested:
@module foo {
@module bar {
.button {
display: inline-block;
}
}
}
.foo-bar-button {
display: inline-block;
}
Prefix
Roole automatically prefixes rules:
#box {
box-shadow: 0 1px 3px #000;
}
#box {
-webkit-box-shadow: 0 1px 3px #000;
-moz-box-shadow: 0 1px 3px #000;
box-shadow: 0 1px 3px #000;
}
Start position in linear-gradient() is automatically translated:
#box {
background: linear-gradient(to bottom right, #fff, #000);
}
#box {
background: -webkit-linear-gradient(top left, #fff, #000);
background: -moz-linear-gradient(top left, #fff, #000);
background: -o-linear-gradient(top left, #fff, #000);
background: linear-gradient(to bottom right, #fff, #000);
}
Currently only keywords are translated, angle values will be translated in a future version
-webkit-gradient() will be added in a future version
When properties are nested inside other rules which needs to be prefixed, Roole handles it correctly:
@keyframes become-round {
from {
border-radius: 0;
}
to {
border-radius: 50%;
}
}
@-webkit-keyframes become-round {
from {
-webkit-border-radius: 0;
border-radius: 0;
}
to {
-webkit-border-radius: 50%;
border-radius: 50%;
}
}
@-moz-keyframes become-round {
from {
-moz-border-radius: 0;
border-radius: 0;
}
to {
-moz-border-radius: 50%;
border-radius: 50%;
}
}
@-o-keyframes become-round {
from {
border-radius: 0;
}
to {
border-radius: 50%;
}
}
@keyframes become-round {
from {
border-radius: 0;
}
to {
border-radius: 50%;
}
}
Support
You can leave comments or open a new issue in the issue tracker. Before opening a new one, use the search function to make sure the issue is not already reported.
You can also follow me @curvedmark on twitter.
Change Log
0.4.1
- Fix function called within mixin retuning incorrect value
0.4.0
- Rename
@mixinto@function - Allow
@functionto return value with@return - Support
$argumentsvariable inside@function - Add rest parameter syntax
- Remove
@extend-allsyntax - Add
@modulesyntax - Add assignment operators (e.g,
+=,*=, etc) - The prefix option now is a space-separated string
- Support
@pagesyntax - Allow identifier to directly follow & selector (e.g.,
$-foo {})
0.3.1
- Fix bugs relating to selectors
0.3.0
- Change syntax to be a superset of CSS
- Make variables case-sensitive
- Add
@extend-allsyntax - Support
@font-facesyntax - Support
@charsetsyntax
0.2.1
- Quit at the first importing error
0.2.0
- Auto-compile code in browsers
- Allow the CLI to watch files
- Add travis-ci support
0.1.2
- Add generated parser to published npm module
0.1.1
- Add MIT lincese file
- Add missing dependency
0.1.0
- Initial release
