Sass Reference
Article
By Hugo Giraudel

At-root

By Hugo Giraudel

The @at-root directive was introduced in Ruby Sass 3.3 in order to be able to emit a style block at root of the document, rather than being nested beneath its parent selectors.

Obviously, this directive is not a killer feature from Sass and is mostly used by Sass frameworks and libraries developers. Also, with Sass being more and more clever at each new release, it slowly reduces the number of usecases for this secondary feature.

For instance, in Sass 3.3, it was very useful to get a CSS animation declaration outside of the scope it was in. Take the following code snippet as an example:

1="{" 2="//" 3="..." 4="@keyframes" 5="animation" 6="{" 7="to" 8="{" 9="color:" 10="red;" 11="}" 12="}" 13="}" 14="[/code"]

<p>When compiled using Ruby Sass 3.3.14, this code sample yields:</p>

[code language='sass']
@keyframes animation {
  .foo to {
    color: red;
  }
}

As you can see, the generated CSS is invalid because the parent selector (.foo) gets prepended to the first keyframe. To fix this code, we can make a good use of the @at-root directive:

.foo {
    // ...

    @at-root {
        @keyframes animation {
            to { color: red; }
        }
    }
}

Now, Sass 3.4 plays it much better by automatically porting @keyframes declarations to the root of the document, as they belong as per CSS specifications; no need for @at-root anymore for this case.

Including/Excluding contexts

An interesting and quite unknown feature from the @at-root directive is the ability to include or exclude contexts when moving a code block to the root of the document. For instance, when using this directive within a @media context, it is possible to port a chunk of code to the root level, outside of the @media block.

When not specifying any with or without, @at-root is a shorthand for @at-root (with: all), meaning it keeps all contexts when porting code to the root level. When wanting to get rid of one or several contextes (@media and @supports), we can use (without: <context>) where <context> can be a space-separated list of contexts such as media supports.

// This will produce:
// `@media print { .bar { color: red; } }`
// ...because `@at-root` conserves contexts by default
@media print {
    @at-root {
        .bar {
            color: red;
        }
    }
}

// This will produce:
// `.bar { color: red; }`
@media print {
    @at-root (without: media) {
        .bar {
            color: red;
        }
    }
}

Example

I must say it is quite hard to find actual solid use cases for the @at-root directive. Either Sass is clever enough so we don’t have to use it, or the cognitive overhead of a solution involving @at-root is usually not worth the troubles.

Actually there is one simple yet not that uncommon use case for this feature: when you want to qualify a class from within its ruleset. Let’s say you style buttons with .button but want to draw an exception when buttons are links (a elements). You might be tempted to write something like:

.button {
    // Styles for buttons

    a& {
        // Styles for buttons when they are actually links
    }
}

Unfortunately, this doesn’t work and make Sass throw an error:

Invalid CSS after “a”: expected “{“, was “&”
“&” may only be used at the beginning of a compound selector.

To circumvent the issue, we have to interpolate the reference selector and exclude it with @at-root:

.button {
    // Styles for buttons

    @at-root a#{&} {
        // Styles for buttons when they are actually links
    }
}

Note that this may be fixed in a further version of Sass.

Also, Daniel Guillan uses it in his library Modernizr Mixin to dynamically generate Sass placeholders at root level in order to extend them. This technique allows him to use his helper mixins both inside and outside of a CSS selectors:

.foo {
    @include yep('opacity', 'csstransforms') {
        // Do something when opacity and CSS transforms are supported
    }
}

@include nope('cssanimations') {
    .foo {
        // Do something when CSS animations are not supported
    }
}

Engine compatibility

The @at-root directive is supported starting Sass 3.3, meaning Sass 3.2 has no support for it. On top of that, LibSass still does not support this feature properly.

  • Glenn Verschooren

    You can also use @at-root to generate a BEM structure

    box {
    @at-root {
    .#{&} {
    // some styles
    }

    .#{&}__element {
    // box element
    }

    .#{&}--modifier {
    // box modifier
    }
    }
    }

    • You do not need to do that. Sass does it automatically.

      .block {
      &__element {
      // Generates `.block__element`
      }

      &--modifier {
      // Generates `.block--modifier`
      }
      }/

      • Stefano Vollono

        Hi Hugo,
        if i have a class in body element?

        .block {
        &__element {
        color: red;

        // this could be correct?
        @at-root .bodyClass & {color: green}
        }

  • LeusMaximus

    Is it possible add to Sass something like this?
    http://blog.legomushroom.com/2014/06/nesting-tree-traversal-in-stylus/