Drupal Syndicate
Using SASS + Compass with (or without) Drupal
by Matt Stills
WHAT DOES THIS HAVE TO DO WITH DRUPAL, EXACTLY?
- The answer is: nothing, really.
- You can use SASS (and Compass) on any website. It has nothing to do with Drupal.
- On the Drupal side (in PHP), you'll still be working with compiled CSS files:
- drupal_add_css()
- '#attached' => array('css' => $css)
- stylesheets[]
- ..."The Drupal Way(s)" still apply
In retrospect, this might not have been the best inaugural topic for our Drupal Syndicate reboot. We'll vote on the next topic after this.
SO, WHY SHOULD I CARE?
- You're a web developer.
- Web developers generally have to write CSS.
- If you don't, you may want to leave now.
- Web developers generally have a love/hate relationship with CSS.
- If you don't, you may want to leave now.
- Assertion #1: SASS makes CSS approximately 1 billion times better.
- Assertion #2: SASS+Compass makes CSS approximately 1.5 billion times better.
- Proof will be established during this presentation.
SASS: Syntactically Awesome stylesheets
What is SASS?
- Stylesheet "scripting" language
- If you've ever used LESS, SASS is the exact same concept
- Compiles to *.css
- Provides many basic (and some advanced) elements of programming which CSS itself sorely lacks:
- variables (!)
- file includes (aka partials) (!!)
- ability to perform basic arithmetic (!!!)
- nested selectors and properties (!!!!)
- functions and mixins (MINDBLOWN)
- ...plus, there's lots of helpful built-in functions. So much so that I don't think we've needed to write any of our own.
SASS: FEATURES OVERVIEW
variables
Even something as simple as the ability to set variables allows for a number of possibilities in CSS:
// file -> my-first-variable.scss
$color-red: #993434; // hex color is a valid data type
$color-blue: #307aea;
$cdn-prefix: '/dr/ncaa/ncaa7/sites/default/files'; // so is string
body {
// interpolation syntax -
background-image: url('#{$cdn-prefix}/my-image.png');
background-color: $color-red;
color: $color-blue;
}
// compiles to -> my-first-variable.css body { background-image: url('/dr/ncaa/ncaa7/sites/default/files/my-image.png');
background-color: #993434; color: #307aea; }
Bonus: SASS also provides the classic "// comment" style which I've used above. This style will be stripped from the compiled *.css. Regular "/* comment */" CSS comments will be left in place.
File includes (PARTIALS)
SASS provides the ability to include files much like other scripting languages. This is mostly helpful for organizing and re-using SASS. For example, you could:
- Split large stylesheets into smaller, possibly re-usable components (called partials in SASS) that are never directly compiled by themselves.
- Have a common variables partial that gets imported into other stylesheets as needed
A gotcha here is that the keyword for including a file is actually "@import" Semantically, this makes sense, but is extra confusing coming from PHP due to the existence of another keyword "@include" which is how mixins get used or "called" in SASS. More on those later.
Nesting
- Nesting is another feature of SASS that finally allows for organization of related style rules.
- It is important to understand the CSS selectors produced when using nesting
-
Recommended not to nest more than 3 levels deep although there is no technical limit
// file -> linescore.scss
.linescore {
background-color: purple;
h2 {
color: pink;
}
a {
text-decoration: none;
&:hover {
text-decoration: underline
}
}
}
// compiles to -> linescore.css .linescore { background-color: purple; }
.linescore h2 { color: pink; } .linescore a { text-decoration: none; } .linescore a:hover { text-decoration: underline; }
MIXINS - DEFINING
Mixins are meant to be small, re-usable chunks of style rules. (But they don't have to be small.) They may also accept arguments so you can parameterize them as needed:
@mixin ncaa-nowrap($overflow: 'ellipsis') {
white-space: nowrap;
overflow: hidden;
text-overflow: $overflow;
}
Here we've defined a mixin to prevent copy from wrapping to 2-lines - a common scenario when working with an editorial staff.
The $overflow parameter allows the caller to specify how they want text overflow to be treated. By default, "ellipsis" value will be used (note a default value is not required on SASS mixins).
MIXINS - USING
Mixins are usable basically wherever you want. In fact, you can nest mixins into mixins if you're into that sort of thing. Let's use the mixin we crafted to control text wrapping within a <p>:
// file -> article.scss
// To use a mixin in this file, you must either @import another
// *.scss file which defines it OR define it here in this file.
// Just pretend the mixin resides in 'partials/_toolkit.scss'
@import 'partials/toolkit'
p.flow {
@include ncaa-nowrap();
&.clip { @include ncaa-nowrap('clip'); }
}
// compiles to -> article.css p.flow { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
p.flow.clip {
white-space: nowrap;
overflow: hidden;
text-overflow: clip; }
FUNCTIONS
- Functions in SASS are basically what you'd expect.
- They might seem kinda similar to mixins (which are more like macros) at first.
- The difference is that mixins provide one or more style rules and their values.
- Functions are used to compute just a value to be used in a single style rule.
- You can use functions in mixins (but probably not vice-versa AFAIK).
Here is a quick example that uses some of SASS' built-in color manipulation functions:
$color-heading: #4c821c;
.main-headline {
color: lighten($color-heading, 15%);
.sub {
color: darken($color-heading, 15%);
}
}
(The variable "$color-heading" will be automatically lightened or darkened by 15% from (from #4c821c -> #65ac25 or #335813, respectively) in the compiled output. This is very useful for setting hover-state colors, for example.)
SASS & OUR DEVELOPMENT WORKFLOW
DEALING WITH THE COMPILE STEP
- All of the cool functionality that SASS brings to the table comes at a cost: the compilation step.
- As you work on *.scss files, before you can see your changes in browser, you'll have to compile all the files you changed.
- Luckily, SASS compilation is quite speedy.
- Furthermore, there are plenty of tools out there to help:
- SASS' own command line utility can compile (obviously) but it can also watch files or folders for changes
- Codekit is a (non-free) GUI for OS X that can watch folders and files and automatically compile them with SASS (among many other things unrelated to SASS)
- Grunt has SASS plugins which can be configured to watch folders etc.
NO COMPILED *.CSS IN SOURCE CONTROL
- We don't keep *.css files in source control for custom modules. Hence, our .gitignore files have entries like "css/" and "*.css"
- Why? Because they aren't needed!
- All of our CSS gets compiled at build time from the SASS (ie. right after drush make has run and created "the world").
- We only care to track changes in the SASS, not the compiled output.
- You can write plain CSS into a *.scss file and the compiler will basically leave it alone.
- This makes it easy to transition an existing CSS codebase to SASS piecemeal:
- $ mv some-random.css some-random.scss # rename it!
- $ sass some-random.scss some-random.css # compile it!
PRODUCTION VS. DEVELOPMENT
- The SASS compiler has different "output style" options.
- This allows us to compile SASS->CSS differently in different environments.
- Eg. In production, we use an output style which minifies the compiled CSS and strips comments.
- There is also a "development-friendly" output style with helpful comments that act as a trace to the origin of the compiled rules (if you use a lot of partials and mixins from other files this is invaluable for debugging):
/* line 1, ../compass/arcade.scss */ .ncaa-arcade { position: relative; padding: 12px 0;
}
GRUNT
- We leverage Grunt to automate the task of compiling SASS files into CSS.
- Typically, Grunt tasks are designed to aggregate
- For example, if a directory contained 3 *.scss files that needed to be compiled, Grunt tasks tend to want to compile them into 1 target *.css file.
- This doesn't jive with Drupal's module-centric development (where you want to have some stylesheets in your theme, others in your modules, etc.)
- What we really want is for Grunt to discover all of our (eg. "sass/one.scss" and "sass/two.scss) SASS files, and compile them as individual CSS files named the same way but compiled to a different folder -> ("css/one.css" and "css/two.css")
- That way, drupal_add_js("/path/to/css/one.css"); would still work and we can use Grunt to automate the compilation.
GRUNT, CONT'D
- It is actually possible to configure the contrib Grunt plugin for compiling SASS to do this.
- But it took some doing.
- This gets into some advanced Grunt configuration stuff but I wanted to include it in the deck just because it is a really critical part of our build process.
-
Even if it doesn't make 100% sense, just try to remember that it is possible and you can always reach out to us for more information so you don't have to re-solve the problem!
GRUNT, CONT'D
(This shows the config for the grunt-contrib-sass plugin to compile *.css -> *.scss files individually but to a different directory)

COMPASS

WHAT IS COMPASS?
- Compass is a SASS framework.
- All plain SASS is valid-Compass SASS but not vice-versa.
- It uses a custom compiler but the same file extension (*.scss)
- The Compass compiler can compile plain SASS that doesn't use any Compass elements but not vice-versa.
- A rough and lazy analogy: Compass:SASS as jQuery:JavaScript
- Our codebase is a mix of plain SASS and Compass SASS *.scss files.
- Each module must choose to be one or the other.
- The rationale is we didn't want to force people to use Compass because it is overkill for many modules. (Or so we thought... turns out >50% of our modules use Compass.)
- We use a simple directory structure/naming convention to differentiate (./compass/*.scss or ./sass/*.scss). More on this later.
COMPASS: FEATURES OVERVIEW
MIXINS
At its core, Compass is mostly just a collection of mixins. We primarily use their CSS3 mixin collection to support legacy browsers without having to manually type out all the vendor-specific rules all the time etc.

FUNCTIONS
In addition to mixins, Compass provides a number of useful functions that complement what SASS comes with. Compass generally refers to these as "helpers" within their documentation.
- sqrt()
- pi()
- sin()
- cos()
- tan()
- more advanced color manipulation funcs like shade() and tint()
- ... and more that struck me as too esoteric but I guess someone must use them
RESET
This is pretty self-explanatory. Like most CSS authoring frameworks, Compass includes a nice reset stylesheet. Paired with SASS file include (@import) ability, it is easy to use wherever you need it.
For example, we @import Compass' reset file into our theme's base stylesheet so that the rules apply to every page on the site.
AUTOMATED SPRITING
-
Compass includes several utilities that are used outside of the *.scss file
- By far the most useful/interesting is "automated spriting"
- No more creating (or worse: updating) sprite maps by hand!
- Never calculate another "background-position" [x,y] coord or sprite cut size
- Simply:
- Drop individual cuts into a folder within your module.
- Use the Compass sprite utility to automatically generate the sprite map image + a partial with mixins to provide CSS rules.
- Makes maintaining and altering existing sprite maps infinitely quicker.
- If there is time: live_data (REAL WORLD PROD example)
COMPASS: WORKFLOW INTEGRATION
DIRECTORY STRUCTURE
- As I mentioned before, we use a simple directory structure to differentiate between modules that use Compass and modules that don't (ie. those that just use plain SASS)
- In our world, a module must choose to either be a Compass module or a SASS module. This is to aid build automation but it also keeps things simpler for developers. It gives them an at-a-glance way of seeing what the module uses for its stylesheets.
- Our build tools know how to differentiate between the two, and will use the appropriate compiler.
- (Yes, the Compass compiler could technically do everything but, again, it is kind of slow so we like to use the SASS compiler wherever possible.)
GRUNT
- Grunt does the rest of the work.
- Compass isn't out-of-the-box compatible with Drupal's modular project structure.
- Compass was designed with the intent that there would be basically one Compass "project" that provides all the CSS for a given website or application.
- We needed to allow for each of our custom modules to be its own Compass project.
- Re-compiling dozens of Compass projects with the out-of-the-box tools is a slow, inefficient process.
- This could have been achieved with some tedious shell scripting, OR...
GRUNT, cont'd
- We could just write a Grunt plugin and take advantage of its super robust globbing engine.
-
https://github.com/mattacular/grunt-compass-compiler
- Wait... holy crap, it is on Github!?
- Yeah, I wrote it on my own time.
- For free.
- You're welcome.
- The Grunt configuration for this task is quite simple:
-
- $ grunt compass build
- This command tells Grunt to go through all of "sites/all" and compile every Compass project that it finds.
- Usually takes about 10 seconds to compile all the *.css for the entire site.
THE ENd.
Questions?
Drupal Syndicate: SASS/Compass
By Matt Stills
Drupal Syndicate: SASS/Compass
Drupal Syndicate presentation on SASS/Compass
- 289