guardian/frontend — pull request #1989 by kaelig (2013-10-14 17:18:58+00:00)
Title: Sass 3.3 (RC1)

Sass 3.3 is a major upgrade of Sass, and it brings support for Sourcemaps. The RC1 was just released and it's ready to use.

Source maps won't work for now for two reasons:
- we store the CSS in localStorage, meaning it's impossible for Chrome to know when a stylesheet has been updated
- still in dev: sourcemaps don't point to an existing file. eg: `/*# sourceMappingURL=football.min.css.map */` (the file has no hash)

@phamann's work (in an unreleased branch) will address those issues, enabling us to have a much faster front-end development process (native live reload in Chrome!)

# Sass Changelog

## 3.3.0

### Using `&` in SassScript

For a long time, Sass has supported a special
{file:SASS_REFERENCE.md#parent-selector "parent selector", `&`}, which is used
when nesting selectors to describe how a nested selector relates to the
selectors above it. Until now, this has only been usable in selectors, but now
it can be used in SassScript as well.

In a SassScript expression, `&` refers to the current parent selector. It's a
comma-separated list of space-separated lists. For example:

.foo.bar .baz.bang, .bip.qux {
$selector: &;
}

The value of `$selector` is now `((".foo.bar" ".baz.bang"), ".bip.qux")`. The
compound selectors are quoted here to indicate that they're strings, but in
reality they would be unquoted.

The SassScript `&` may be used in selectors using `#{}` interpolation. By
treating it as a SassScript value, you can do different things with it than you
can when treating it as a selector. When `&` is used as a selector, it can only
appear at the beginning of a compound selector, similarly to a type selector
like `a` or `h1`. When used with `#{}`, it can go anywhere. For example:

.badge {
@at-root #{&}-info { ... }
@at-root #{&}-header { ... }
}

Produces:

.badge-info { ... }
.badge-header { ... }

#### `@at-root`

What's that `@at-root` thing in the previous example? It's a way to tell Sass
that you don't want that selector to be nested. When you use `&` as a selector,
Sass can tell automatically that you don't want nesting, but when you use it
with `#{}` you have to be explicit. After all, you might have put it in a
variable, returned it from a function, or turned it into a string and reversed
the characters.

In addition to using `@at-root` on a single selector, you can also use it on a
whole block of them. For example:

.badge {
@at-root {
#{&}-info { ... }
#{&}-header { ... }
}
}

Also produces:

.badge-info { ... }
.badge-header { ... }

#### `@at-root (without: ...)` and `@at-root (with: ...)`

By default, `@at-root` just excludes selectors to allow `#{&}` to work
similarly to just including `&` in a selector. However, it's also
possible to use `@at-root` to move outside of nested directives such
as `@media` as well. For example:

@media print {
.page {
width: 8in;
@at-root (without: media) {
color: red;
}
}
}

produces:

@media print {
.page {
width: 8in;
}
}
.page {
color: red;
}

You can use `@at-root (without: ...)` to move outside of any
directive. You can also do it with multiple directives separated by a
space: `@at-root (without: media supports)` moves outside of both
`@media` and `@supports` queries.

There are two special values you can pass to `@at-root`. "rule" refers
to normal CSS rules; `@at-root (without: rule)` is the same as
`@at-root` with no query. `@at-root (without: all)` means that the
styles should be moved outside of *all* directives and CSS rules.

If you want to specify which directives or rules to include, rather
than listing which ones should be excluded, you can use `with` instead
of `without`. For example, `@at-root (with: rule)` will move outside
of all directives, but will preserve any CSS rules.

### Source Maps

Sass now has the ability to generate standard JSON [source maps][] of a format
that will soon be supported in most major browsers. These source maps tell the
browser how to find the Sass styles that caused each CSS style to be generated.
They're much more fine-grained than the old Sass-specific debug info that was
generated; rather than providing the source location of entire CSS rules at a
time, source maps provide the source location of each individual selector and
property.

Source maps can be generated by passing the `--sourcemap` flag to the `sass`
executable, by passing the {file:SASS_REFERENCE.md#sourcemap-option `:sourcemap`
option} to \{Sass::Plugin}, or by using the
\{Sass::Engine#render\_with\_sourcemap} method. By default, Sass assumes that
the source stylesheets will be made available on whatever server you're using,
and that their relative location will be the same as it is on the local
filesystem. If this isn't the case, you'll need to make a custom class that
extends \{Sass::Importers::Base} or \{Sass::Importers::Filesystem} and overrides
\{Sass::Importers::Base#public\_url `#public_url`}.

Thanks to Alexander Pavlov for implementing this.

[source maps]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?hl=en_US&pli=1&pli=1

### SassScript Maps

SassScript has a new data type: maps. These are associations from SassScript
values (often strings, but potentially any value) to other SassScript values.
They look like this:

$map: (key1: value1, key2: value2, key3: value3);

Unlike lists, maps must always be surrounded by parentheses. `()` is now an
empty map in addition to an empty list.

Maps will allow users to collect values into named groups and access those
groups dynamically. For example, you could use them to manage themes for your
stylesheet:

$themes: (
mist: (
header: #DCFAC0,
text: #00968B,
border: #85C79C
),
spring: (
header: #F4FAC7,
text: #C2454E,
border: #FFB158
),
// ...
);

@mixin themed-header($theme-name) {
h1 {
color: map-get(map-get($themes, $theme-name), header);
}
}

There are a variety of functions for working with maps:

* The {Sass::Script::Functions#map_get `map-get($map, $key)` function} returns
the value in the map associated with the given key. If no value is found, it
returns `null`.

* The {Sass::Script::Functions#map_merge `map-merge($map1, $map2)` function}
merges two maps together into a new map. If there are any conflicts, the
second map takes precedence, making this a good way to modify values in a map
as well.

* The {Sass::Script::Functions#map_keys `map-keys($map)` function} returns all
the keys in a map as a comma-separated list.

* The {Sass::Script::Functions#map_values `map-values($map)` function} returns
all the values in a map as a comma-separated list.

* The {Sass::Script::Functions#map_has_key `map-has-key($map, $key)` function}
returns whether or not a map contains a pair with the given key.

All the existing list functions also work on maps, treating them as lists of
pairs. For example, `nth((foo: 1, bar: 2), 1)` returns `foo 1`. Maps can also be
used with `@each`, using the new multiple assignment feature (see below):

@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {
#{$header} {
font-size: $size;
}
}

Produces:

h1 {
font-size: 2em;
}

h2 {
font-size: 1.5em;
}

h3 {
font-size: 1.2em;
}

#### Variable Keyword Arguments

Maps can be passed as variable arguments, just like lists. For example, if
`$map` is `(alpha: -10%, "blue": 30%)`, you can write `scale-color($color,
$map...)` and it will do the same thing as `scale-color($color, $alpha: -10%,
$blue: 30%)`. To pass a variable argument list and map at the same time, just do
the list first, then the map, as in `fn($list..., $map...)`.

You can also access the keywords passed to a function that accepts a variable
argument list using the new {Sass::Script::Functions#keywords `keywords($args)`
function}. For example:

@function create-map($args...) {
@return keywords($args);
}

create-map($foo: 10, $bar: 11); // returns (foo: 10, bar: 11)

#### Lists of Pairs as Maps

The new map functions work on lists of pairs as well, for the time being. This
feature exists to help libraries that previously used lists of pairs to simulate
maps. These libraries can now use map functions internally without introducing
backwards-incompatibility. For example:

$themes: (
mist (
header #DCFAC0,
text #00968B,
border #85C79C
),
spring (
header #F4FAC7,
text #C2454E,
border #FFB158
),
// ...
);

@mixin themed-header($theme-name) {
h1 {
color: map-get(map-get($themes, $theme-name), header);
}
}

Since it's just a migration feature, using lists of pairs in place of maps is
already deprecated. Library authors should encourage their users to use actual
maps instead.

### Smaller Improvements

* [listen](http://github.com/guard/listen) is now a standard Gem dependency.
It's no longer bundled with Sass.

* Sass now has numerous functions for working with strings:
\{Sass::Script::Functions#str_length `str-length`} will return the length of a
string; \{Sass::Script::Functions#str_insert `str-insert`} will insert one
string into another; \{Sass::Script::Functions#str_index `str-index`} will
return the index of a substring within another string;
\{Sass::Script::Functions#str_slice `str-slice`} will slice a substring from a
string; \{Sass::Script::Functions#to_upper_case `to-upper-case`} will
transform a string to upper case characters; and
\{Sass::Script::Functions#to_lower_case `to-lower-case`} will transform a
string to lower case characters.

* A \{Sass::Script::Functions#list_separator `list-separator`} function has been
added to determine what separator a list uses. Thanks to [Sam
Richard](https://github.com/Snugug).

* Custom Ruby functions can now access the global environment, which
allows them the same power as Sass-based functions with respect to
reading and setting variables defined elsewhere in the stylesheet.

* The `set-nth($list, $n, $value)` function lets you construct a new
list based on `$list`, with the nth element changed to the value
specified.

* Add "grey" and "transparent" as recognized SassScript colors. Thanks to [Rob
Wierzbowski](https://github.com/robwierzbowski).

* Add a function \{Sass::Script::Functions#unique\_id `unique-id()`} that will
return a CSS identifier that is unique within the scope of a single CSS file.

* Allow negative indices into lists when using `nth()`.

* You can now detect the presence of a Sass feature using the new function
`feature-exists($feature-name)`. There are no detectable features in this
release, this is provided so that subsequent releases can begin to
use it. Additionally, plugins can now expose their functionality
through `feature-exists` by calling `Sass.add_feature(feature_name)`. Features
exposed by plugins must begin with a dash to distinguish them from
official features.

* It is now possible to determine the existence of different Sass
constructs using these new functions:

* `variable-exists($name)` checks if a variable resolves in the
current scope.
* `global-variable-exists($name)` checks if a global variable of the
given name exists.
* `function-exists($name)` checks if a function exists.
* `mixin-exists($name)` checks if a mixin exists.

* You can call a function by name by passing the function name to the
call function. For example, `call(nth, a b c, 2)` returns `b`.

* Comments following selectors in the indented syntax will be correctly
converted using `sass-convert`.

* `@each` now supports "multiple assignment", which makes it easier to iterate
over lists of lists. If you write `@each $var1, $var2, $var3 in a b c, d e f,
g h i`, the elements of the sub-lists will be assigned individually to the
variables. `$var1`, `$var2`, and `$var3` will be `a`, `b` and `c`; then `d`,
`e`, and `f`; and then `g`, `h`, and `i`. For more information, see
{file:SASS_REFERENCE.md#each-multi-assign the `@each` reference}.

* There is a new {Sass::Script::Value::Helpers convenience API} for creating
Sass values from within ruby extensions.

* The `if()` function now only evaluates the argument corresponding to
the value of the first argument.

* Comma-separated lists may now have trailing commas (e.g. `1, 2,
3,`). This also allows you to use a trailing comma to distinguish a
list with a single element from that element itself -- for example,
`(1,)` is explicitly a list containing the value `1`.

* All directives that are nested in CSS rules or properties and that
contain more CSS rules or properties are now bubbled up through
their parent rules.

### Backwards Incompatibilities -- Must Read!

* Sass will now throw an error when `@extend` is used to extend a selector
outside the `@media` context of the extending selector. This means the
following will be an error:

@media screen {
.foo { @extend .bar; }
}
.bar { color: blue; }

* Sass will now throw an error when an `@extend` that has no effect is used. The
`!optional` flag may be used to avoid this behavior for a single `@extend`.

* Sass will now throw an error when it encounters a single `@import` statement
that tries to import more than one file. For example, if you have `@import
"screen"` and both `screen.scss` and `_screen.scss` exist, a warning will be
printed.

* `grey` and `transparent` are no longer interpreted as strings; they're now
interpreted as colors, as per the CSS spec.

* The automatic placement of the current working directory onto the Sass
load path is now deprecated as this causes unpredictable build
processes. If you need the current working directory to be available,
set `SASSPATH=.` in your shell's environment.

* `Sass::Compiler.on_updating_stylesheet` has been removed.

* `Sass::Plugin.options=` has been removed.

* `Sass::Script::Number::PRECISION` has been removed.

* Many classes in the \{Sass::Script} have been rearranged. All the value
classes have been moved into \{Sass::Script::Value} (e.g.
\{Sass::Script::Value::Color}, \{Sass::Script::Value::String}, etc). Their
base class is now \{Sass::Script::Value::Base} instead of
`Sass::Script::Literal`. All the parse tree classes have been moved into
\{Sass::Script::Tree} (e.g. \{Sass::Script::Tree::Node},
\{Sass::Script::Tree::Operation}, etc).

The old names will continue to work for the next couple releases, but they
will be removed eventually. Any code using them should upgrade to the new
names.

* As part of a migration to cleaner variable semantics, assigning to
global variables in a local context by default is deprecated. If
there's a global variable named `$color` and you write `$color:
blue` within a CSS rule, Sass will now print a warning; in the
future, it will create a new local variable named `$color`. You may
now explicitly assign to global variables using the `!global` flag;
for example, `$color: blue !global` will always assign to the global
`$color` variable.

![ ](http://s2.developerslife.ru/public/images/gifs/2c9b41fd-b407-416c-83c6-eb405491a4f7.gif)