Stylus gets @extend

@extend is a great little feature which originated in SASS, and now finds a home in Stylus. If you’ve ever written CSS like this, you know it can become quite the pain to maintain large lists of selectors:

.message,
.warning,
.error {
  font-size: 14px;
  padding: 5px 10px;
  border: 1px solid #eee;
  border-radius: 5px;
}

.warning {
  color: yellow;
}

.error {
  color: red;
}

@extend makes this process flow naturally, and even supports inheritance. In Stylus you may use @extend or @extends, I prefer the latter personally but that’s up to you! Using this feature you would define .message on it’s own, and simply __@extend_ it from within the other rulesets:

.message {
  font-size: 14px;
  padding: 5px 10px;
  border: 1px solid #eee;
  border-radius: 5px;
}

.warning {
  @extends .message;
  color: yellow;
}

.error {
  @extends .message;
  color: red;
}

Taking this even further with inheritance:

.fatal-error {
  @extends .error;
  font-weight: bold
}

Would yield the following CSS:

.message,
.warning,
.error,
.fatal-error {
  font-size: 14px;
  padding: 5px 10px;
  border: 1px solid #eee;
  border-radius: 5px;
}
.warning {
  color: #ff0;
}
.error,
.fatal-error {
  color: #f00;
}
.fatal-error {
  font-weight: bold;
}

Here’s another example:

form
  button
    padding: 10px
    border: 1px solid #eee
    border-radius: 5px

ul
  li a
    @extends form button

Yielding:

form button,
ul li a {
  padding: 10px;
  border: 1px solid #eee;
  border-radius: 5px;
}

And of course you may utilize the alternative Stylus syntax if you prefer:

.message
  font-size: 14px
  padding: 5px 10px
  border: 1px solid #eee
  border-radius: 5px

.warning
  @extends .message
  color: yellow

.error
  @extends .message
  color: red

.fatal-error
  @extends .error
  font-weight: bold

Grab Stylus 0.22.4 for these goodies!

Stylus 0.15.1 - new property reference & @keyframes fabrication features

Stylus 0.15.x adds a bunch of cool new functionality as well as some important bug fixes.

@keyframes fabrication

The new keyframes fabrication support automatically duplicates your @keyframes definitions to support vendor prefixes. Note that this is only a default, as shown below:

@-moz-keyframes foo {
  0% {
    color: #000;
  }

  100% {
    color: #fff;
  }
}
@-webkit-keyframes foo {
  0% {
    color: #000;
  }

  100% {
    color: #fff;
  }
}
@keyframes foo {
  0% {
    color: #000;
  }

  100% {
    color: #fff;
  }
}

one can tweak this functionality using the vendors list as shown in the following snippet which will only expand to the -webkit- vendor prefix.

vendors = moz official

@keyframes foo {
  from {
    color: black
  }
  to {
    color: white
  }
}

The vendors property may be utilized more in the future, and perhaps in frameworks like “nib” to unify configuration.

Property reference

The syntax @<property> has been introduced to reference values of existing properties. For example if you want to re-use values, you may typically do something like below instead of typing the value several times, also allowing you to use functions with w.

.button
  width: w = 50px
  height: w

The new property reference syntax allows you to treat properties as variables, which can also be accessed within mixins:

.button
  width: 50px
  height: @width

Comment compilation

When the compress option is true Stylus will not output single-line nor multi-line comments, which are commonly found used for licensing in stylesheet frameworks etc, so it’s useful to strip these out however you can now force output with the ! character /*!.

New built-in functions

The math functions cos() and sin() were added, as well as exposing PI.

Bug fixes

  • Fixed @import on windows

Stylus vs SASS vs LESS error reporting

I was curious what SASS did as far as error reporting goes, so I tried the following snippet with SASS, LESS as well as Stylus.

body {
  form input {
    background: foo[fail];
  }
}

LESS

The output from LESS was terrible:

  Syntax Error on line 4

note: that for LESS I had to tweak the input to “foo[fail’]’;” since it simply consumes the input above

SASS

The SASS output was not bad, it shows you a tiny chunk of the line for context:

Syntax error: Invalid CSS after "...background: foo": expected ";", was "[fail];"
        on line 4 of standard input
  Use --trace for backtrace.

note: —trace is for the ruby stack trace, not SASS

Stylus

Then we have Stylus, showing you ~8 lines of context (by default), and even a detailed stack trace including the call sites much like you would find in other languages.

    Error: /tmp/stylus/test.styl:4
       1| 
       2| body {
       3|   form input {
     > 4|     background: foo[fail];
       5|   }
       6| }
       7| 

    cannot perform foo[(fail)]
        at "form input" (/tmp/stylus/test.styl:4)
        at "body" (/tmp/stylus/test.styl:3)

Extend Sylus with a Nib

Today I am releasing Nib, an extensions library for Stylus, inspired by the SASS compass library. Nib is a very new library with a minimal feature set at the moment, however I need to get some of my semi-finished projects open-sourced to make way for bigger badder things, and I’m already finding this project quite useful so feedback and contributions are always welcome.

If you dont like to read, feel free to check out the short screencast.

Vendor Support

Nib currently supplies some cross-browser vendor support for properties, however this will surely expand and be refined as we go.

Enjoy The Little Things

Ever forget if nowrap is no-wrap, or whitespace over white-space? well I do, thanks to simple definitions like the following, these are completely interchangeable:

no-wrap = unquote('nowrap')

Augmented Border Radius

Border radius works as you might expected, however it is also augmented to support positioning. For example to round only the top of a box, use top 5px, or perhaps round the top and bottom differently with top 5px, buttom 10px. For example:

button {
  border-radius: top left 5px, bottom right 10px;
}

yields:

button {
  -moz-border-radius-topleft: 5px;
  -webkit-border-top-left-radius: 5px;
  border-top-left-radius: 5px;
  -moz-border-radius-bottomright: 10px;
  -webkit-border-bottom-right-radius: 10px;
  border-bottom-right-radius: 10px;
}

Position Properties

Nib has three position mixins or “custom properties”, fixed, absolute, and relative. These shorthands are helpful since you can define three properties in one concise, legible manner.

fixed: top left
fixed: top 5px left
fixed: top left 5px
absolute: top 5px left 5px
relative: left -5px

Expanding to:

position: fixed;
top: 0;
left: 0;

etc.

Gradients

Nib makes working with gradients easier than you could imagine. The following call to linear-gradient() duplicates the property to which it is assigned to (not bound only to “background”), and generates the appropriate webkit and mozilla output:

body
  background: linear-gradient(right bottom, white, 80% black)

yields:

body {
  background: -webkit-gradient(linear, right bottom, left top, color-stop(0, #fff), color-stop(0.8, #000));
  background: -moz-linear-gradient(right bottom, #fff 0%, #000 80%);
  background: linear-gradient(right bottom, #fff 0%, #000 80%);
}

You may pass as many color stops as you like, optionally providing a position before or after the color for each, whichever you prefer.

Gradient Image Generation

Another powerful feature of Nib, is the ability to utilize node-canvas when installed, and auto-generate a data URI representation of the gradient for further browser support. The image below displays google chrome’s render on the left, and node-canvas on the right.

gradients

The linear-gradient-image() function generates the data uri only:

body
  background: linear-gradient-image(50px top, white, black);

Alternatively we can pass the size to a regular call to linear-gradient() and the data uri will be created along with the other properties:

body
  background: linear-gradient(50px top, white, black);

Components

Nib currently ships with 5 or 6 sets of buttons, however I welcome the addition of other flexible components, and of course more buttons :). Below is an example of utilizing the “bold” button, assigning a glow which adjusts appropriately within the mixin.

.bold
  bold-button()

.bold-alternate
  bold-button(glow: #00ABFA)

glow button

Stylus 0.4.0 released

Stylus for those who are not familiar with it, is a dynamic language which compiles down to css, written with JavaScript for node.js.

CSS Syntax Support

Previously Stylus only allowed the use of our indented grammar, which may deter designers that are not comfortable learning or using a new syntax. For this reason and to aid in copy/pasting of css stylesheets I have added support for optional semi-colons, colons, commas, and braces, this means that most css is valid.

Example

The example below is completely valid, showing the use of mixins within both our indentation-style usage, and our css-style usage.

  vendor(prop, args)
    -webkit-{prop} args
    -moz-{prop} args
    {prop} args

  border-radius()
    vendor('border-radius', arguments)

  button
  a.button
  input[type=submit]
  input[type=button]
    border-radius 3px 5px
    color black
    &:hover
      background black
      color white

  button,
  a.button,
  input[type=submit],
  input[type=button] {
    border-radius: 3px 5px;
    color: black;
    &:hover {
      background: black;
      color: white;
    }
  }

yielding:

button,
a.button,
input[type=submit],
input[type=button] {
  color: #000;
  -webkit-border-radius: 3px 5px;
  -moz-border-radius: 3px 5px;
  border-radius: 3px 5px;
}
button:hover,
a.button:hover,
input[type=submit]:hover,
input[type=button]:hover {
  background: #000;
  color: #fff;
}
button,
a.button,
input[type=submit],
input[type=button] {
  color: #000;
  -webkit-border-radius: 3px 5px;
  -moz-border-radius: 3px 5px;
  border-radius: 3px 5px;
}
button:hover,
a.button:hover,
input[type=submit]:hover,
input[type=button]:hover {
  background: #000;
  color: #fff;
}

This of course applies to use within mixins as well, using the indentation-style we may define the mixin as:

  fade-to(to = .5)
    &:hover
      opacity to

  button
    fade-to(.1)

or with the css-style as:

  fade-to(to = .5)
    &:hover {
      opacity: to;
    }

  button {
    fade-to(.1);
  }

both yielding:

  button:hover {
    opacity: 0.1;
  }

Hope you enjoy the new feature, be sure to watch the project on GitHub to keep up to date with improvements.