It's Not Always a Pain in the Sass

WordCamp Denmark 2014

by Scott Basgaard / @scottbasgaard

An introduction to Sass, Grunt, and how these tools can help speed up and streamline your WordPress development workflow.

Who is Scott Basgaard?

  • Lives in Norway
  • Originally from NJ
  • Technical Project Lead at Human Made
  • Loves all things WordPress and organized the first WordCamp Norway in 2012.
  • Passionate about helping others and is the man behind a 24-hour-long WordPress event known as WordSesh.
  • Previously worked at WebDevStudios and WooThemes

New Dad :)

Grunt & Sass

What is Grunt?

Grunt

A Command-line JavaScript Task Runner

Why use it?

Grunt Equals Less Work!

Minification, compilation, unit testing, linting etc. on the fly.

Grunt can do A TON of cool things!

But first, let's talk about Sass…

What is Sass?

Lets you do things that don't exist in CSS

  • Variables
  • Nesting
  • Mixins
  • Inheritance
  • Etc.

What about LESS?

LESS is awesome, too!

WordPress 3.8

CORE ADOPTS SASS

Two Syntaxes

foo.sass

```css nav ul margin: 0 padding: 0 list-style: none li display: inline-block a display: block padding: 6px 12px text-decoration: none ```

White space delimited

foo.scss

```css nav { ul { margin: 0; padding: 0; list-style: none; } li { display: inline-block; } a { display: block; padding: 6px 12px; text-decoration: none; } } ```

Similar to CSS Syntax. I like this the best.

Compiled FOO.CSS

```css nav ul { margin: 0; padding: 0; list-style: none; } nav li { display: inline-block; } nav a { display: block; padding: 6px 12px; text-decoration: none; } ```

How Do I Get It?

The "Pain"

Sass requires Ruby

Grunt requires Node

There's got to be an easier way…

Vagrant

MAMP

WAMP

LAMP

XAMP

DOWNLOAD VAGRANT

vagrantup.com

Requires VirtualBox or VMware

Create a one file describing what needs to be installed.

`$ vagrant up`

Salty WordPress

VVV

So what's Grunt for again?

  • Look for changes to HTML, JS, and Sass
  • Compile our Sass
  • Minify and concatenate our JS
- theme-name - - assets - - - fonts - - - images - - - javascripts - - functions.php - - index.php - - style.css
- theme-name - - assets - - - fonts - - - images - - - javascripts - - - - build - - - - source - Custom JS - - - - vendor - e.g. jQuery Plugins - - - styles - - - - build - - - - source - Custom Sass - - - - vendor - Third Party - - Gruntfile.js - - package.json - - lib - Custom Classes / Functions - - templates - Templates Files - - - partials - (e.g. Header, Footer) - - functions.php - Bootload lib - - index.php - - style.css

package.json

Gruntfile.js

package.json

Defines project and dependencies.

package.json

``` { "name": "Pain In The Sass", "version": "1.0.0", "description": "Theme for My Talk at WordCamp Denmark 2014", "main": "Gruntfile.js", "dependencies": { "grunt": "~0.4.0", "grunt-contrib-compass": "~0.1.3", "grunt-contrib-jshint": "~0.2.0", "grunt-contrib-uglify": "~0.1.2", "grunt-contrib-watch": "~0.3.1" } } ```

Gruntfile.js

List of tasks to do.

Gruntfile.js

``` 'use strict'; module.exports = function(grunt) { grunt.initConfig({ // let us know if our JS is sound jshint: { options: { "bitwise": true, "browser": true, "curly": true, "eqeqeq": true, "eqnull": true, "es5": true, "esnext": true, "immed": true, "jquery": true, "latedef": true, "newcap": true, "noarg": true, "node": true, "strict": false, "trailing": true, "undef": true, "globals": { "jQuery": true, "alert": true } }, all: [ 'Gruntfile.js', 'javascripts/source/*.js' ] }, // concatenation and minification all in one uglify: { dist: { files: { 'javascripts/build/vendor.min.js': [ 'javascripts/vendor/plugin1/jquery.plugin.js', 'javascripts/vendor/plugin2/js/plugin/plugin.js' ], 'javascripts/build/script.min.js': [ 'javascripts/source/script.js' ] } } }, // style (Sass) compilation via Compass compass: { dist: { options: { sassDir: 'styles/source', cssDir: 'styles/build', imagesDir: 'images', images: 'images', javascriptsDir: 'javascripts/build', fontsDir: 'fonts', environment: 'production', outputStyle: 'expanded', relativeAssets: true, noLineComments: true, force: true } } }, // watch our project for changes watch: { compass: { files: [ 'styles/source/*', 'styles/source/**/*', 'styles/vendor/*', 'styles/vendor/**/*' ], tasks: ['compass'] }, js: { files: [ '<%= jshint.all %>' ], tasks: ['jshint', 'uglify'] } } }); // load tasks grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-compass'); grunt.loadNpmTasks('grunt-contrib-watch'); // register task grunt.registerTask('default', [ 'jshint', 'compass', 'uglify', 'watch' ]); }; ```

Note: Paths are relative to Gruntfile.js.

2 Steps

2 Steps

``` $ npm install $ grunt ```

Sass is Sassy

  • Variables
  • Nesting
  • Partials & Imports
  • Import
  • Mixins
  • Inheritance
  • Math

Variables

```css $font-stack: Helvetica, sans-serif; $primary-color: #333; body { font: 100% $font-stack; color: $primary-color; } ```

variables.scss

Variables

```css body { font: 100% Helvetica, sans-serif; color: #333; } ```

variables.css

Nesting

```css nav { ul { margin: 0; padding: 0; list-style: none; } li { display: inline-block; } a { display: block; padding: 6px 12px; text-decoration: none; } } ```

nesting.scss

Nesting

```css nav ul { margin: 0; padding: 0; list-style: none; } nav li { display: inline-block; } nav a { display: block; padding: 6px 12px; text-decoration: none; } ```

nesting.css

Partials & Imports

```css html, body, ul, ol { margin: 0; padding: 0; } ```

_reset.scss

Partials & Imports

```css @import 'reset'; body { font-size: 100% Helvetica, sans-serif; background-color: #efefef; } ```

base.scss

Partials & Imports

```css html, body, ul, ol { margin: 0; padding: 0; } body { background-color: #efefef; font-size: 100% Helvetica, sans-serif; } ```

base.css

Mixins

```css @mixin border-radius($radius) { -webkit-border-radius: $radius; -moz-border-radius: $radius; -ms-border-radius: $radius; border-radius: $radius; } .box { @include border-radius(10px); } ```

mixins.scss

Mixins

```css .box { -webkit-border-radius: 10px; -moz-border-radius: 10px; -ms-border-radius: 10px; border-radius: 10px; } ```

mixins.css

Inheritance

```css .message { border: 1px solid #ccc; padding: 10px; color: #333; } .success { @extend .message; border-color: green; } .error { @extend .message; border-color: red; } .warning { @extend .message; border-color: yellow; } ```

inheritance.scss

Inheritance

```css .message, .success, .error, .warning { border: 1px solid #cccccc; padding: 10px; color: #333; } .success { border-color: green; } .error { border-color: red; } .warning { border-color: yellow; } ```

inheritance.css

Math

+, -, *, /, and %

```css .container { width: 100%; } article[role="main"] { float: left; width: 600px / 960px * 100%; } aside[role="complimentary"] { float: right; width: 300px / 960px * 100%; } ```

math.scss

Math

```css .container { width: 100%; } article[role="main"] { float: left; width: 62.5%; } aside[role="complimentary"] { float: right; width: 31.25%; } ```

math.css

Tips & Tricks

Proper Use of Mixins

```css /* No */ @mixin default-container { background-color: #fff; min-height: 20px; min-width: 20px; text-align: center; } #container-b { @include default-container; #container-a { @include default-container; } } ```

Proper Use of Mixins

```css /* Yes */ @mixin default-container { background-color: #fff; min-height: 20px; min-width: 20px; text-align: center; } #container-a, #container-b { @include default-container; } #container-a { height: 120px; width: 120px; margin: 20px; } #container-b { height: 240px; width: 240px; } ```

Sass is still CSS

```css /* No */ #container { .first, .second { padding: 5px; } .first { border: 2px solid red; margin-bottom: 5px; a { color: red; } } .second { border: 2px solid blue; a { color: red; } } } ```

Sass is still CSS

```css /* Yes */ #container { .first, .second { padding: 5px; } .first { border: 2px solid red; margin-bottom: 5px; } .second { border: 2px solid blue; } a { color: red; } } ```

Use Good Practice

```css /* No */ .social-icon { width: 40px; height: 40px; background-size: contain; &.facebook{ background-image: url('imgs/facebook.png'); } &.twitter { background-image: url('imgs/twitter.png'); } &.tumblr { background-image: url('imgs/tumblr.png'); } &.pinterest { background-image: url('imgs/pinterest.png'); } } ```

Use Good Practice

```css /* Yes */ .social-icon { $icons: facebook twitter tumblr pinterest; $size: 40px; width: $size; height: $size; background-size: contain; @each $icon in $icons { &.#{$icon} { background-image: url('imgs/#{$icon}.png'); } } } ```

Mixins Tip

```css /* No */ #wrapper { @include clearfix; .inner { @include clearfix; } } #other { @include clearfix; } ```

Mixins Tip

```css /* Yes */ .clearfix { @include clearfix; } ```

Mixins Tip

Avoid Deep Nesting & Bloat

Separate Useful Files

Avoid Duplicate Imports

Meaningful Variable Names

Live Examples

Resources/Credits

Thank you!

@scottbasgaard

Questions?