Sassメモ

Sassメモ

まあ今更こんな記事ってな感じですが。Sassを使い始めて約1年、自分のブログに記事を書いておくのもひとつの記録かなと思い、メモがてらに記事にしてみました。
【追記】2013/12/26 @functionを追記しました。2013/12/24 @eachを追記しました。

ネスト

Sass

main {
    margin-bottom: 3em;
    p {
        line-height: 1.4;
        margin-bottom: 1em;
    }
}

CSS

main {
  margin-bottom: 3em;
}
main p {
  line-height: 1.4;
  margin-bottom: 1em;
}

Sassの最もベーシックな書き方だと思います。ネストで書くことによって見通しの良いCSSが書けますが、ネストのしすぎに注意。かえって見辛いコードになる可能性もあります。

親セレクタ参照

Sass

a {
    text-decoration: none;
    &:hover {
        text-decoration: underline;
    }
}
.box {
    margin-bottom: 2em;
    background-color: #999;
    .top & {
        background-color: #eee;
    }
    &.info {
        background-color: #fff;
    }
}

CSS

a {
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
.box {
  margin-bottom: 2em;
  background-color: #999;
}
.top .box {
  background-color: #eee;
}
.box.info {
  background-color: #fff;
}

擬似セレクタの場合は&:hoverのように書きます。親セレクタを参照したい場合は.top &のように「参照する親セレクタ &」と書きます。&の後にセレクタを続ければ連結セレクタで出力します。

変数と演算

Sass

$base-margin: 3em;
div {
    margin-bottom: $base-margin;
    p {
        line-height: 1.4;
        margin-bottom: $base-margin / 2;
    }
}

CSS

div {
  margin-bottom: 3em;
}
div p {
  line-height: 1.4;
  margin-bottom: 1.5em;
}

ベースとなるカラーコードやmarginの値を変数に入れると便利です。変数は演算もできます。
小数点以下の処理を丸めたい場合はround()で四捨五入、ceil()で切り上げ、floor()で切り捨てとなります。

Sass

$base-margin: 5em;
div.round {
    margin-bottom: round($base-margin / 2);
}
div.ceil {
    margin-bottom: ceil($base-margin / 2);
}
div.floor {
    margin-bottom: floor($base-margin / 2);
}

CSS

div.round {
  margin-bottom: 3em;
}
div.ceil {
  margin-bottom: 3em;
}
div.floor {
  margin-bottom: 2em;
}

平澤さんのブログに非常にわかりやすい記事があります。変数と演算で効率的に

mixn

Sass

@mixin clearfix {
    *zoom: 1; // lte ie7
    &:after {
        content: '';
        display: block;
        clear: both;
    }
}
.cf {
    @include clearfix;
}

CSS

.cf {
  *zoom: 1;
}
.cf:after {
  content: '';
  display: block;
  clear: both;
}

@mixinは複数のプロパティをまとめて1つのプロパティのように扱います。@mixin mixin名 { prop: value; } を セレクタ { @include mixin名; } で呼び出します。
@mixinはパラメータを設定して柔軟に呼び出す事ができます。その場合は、@mixin mixin名(パラメータ) { prop: パラメータ; } をセレクタ { @include mixin名(パラメータ); } で呼び出します。
Compassなんかを使ってる場合、下のサンプルコードのようにCSS Sprite用のmixinとか作っておくと便利。

Sass

//スプライト画像の作成
$s1: sprite-map("/img/sprite/*.png", $spacing:20px, $layout: vertical);
//$name スプライト前の画像名
//$sorutes sprite-mapを指定
@mixin sprite-background($name, $sprites) {
  background: sprite-url($s1)no-repeat;
  background-position: sprite-position($sprites, $name);
  width: image-width(sprite-file($sprites, $name));
  height: image-height(sprite-file($sprites, $name));
}
//mixin呼び出し
.icon-config {
    @include sprite-background("Evernote", $s1);
}

CSS

.icon-Evernote {
  background: url(/img/sprite-scc43fbc7aa.png) no-repeat;
  background-position: 0 -52px;
  width: 32px;
  height: 32px;
}

/img/spreite/にpng画像を放り込んでおいてスプライト用のmixinを呼び出せば自動でスプライト画像を書き出してくれるのでもうCompass抜きでCSSスプライトは考えられません...

extend

Sass

.box-main {
    margin: 1em;
    padding: 0.5em 1em;
    border: 1px solid #999;
    background-color: #ddd;
}
.box-sub {
    @extend .box-main;
    background-color: #eee;
}

CSS

.box-main, .box-sub {
  margin: 1em;
  padding: 0.5em 1em;
  border: 1px solid #999;
  background-color: #ddd;
}
.box-sub {
  background-color: #eee;
}

セレクタを継承してまとめて記述してくれます。けれども後述のプレースホルダを使った方が余計な記述が減る場合も。

プレースホルダ

Sass

//このセレクタは@extendで呼び出されない限り出力されない
%box-style {
    margin: 1em;
    padding: 0.5em 1em;
    border: 1px solid #999;
}
.box-main {
    @extend %box-style;
    background-color: #ddd;
}
.box-sub {
    @extend %box-style;
    background-color: #999;
}

CSS

.box-main, .box-sub {
  margin: 1em;
  padding: 0.5em 1em;
  border: 1px solid #999;
  background-color: #ddd;
}
.box-sub {
  background-color: #999;
}

プレースホルダは%セレクタ名で記述します。@extend %セレクタ名で呼び出されない限り、CSSは出力されません。
先述のextendはあくまでも継承ですので、継承元のセレクタに継承したくないプロパティがあっても当然のように継承してしまうので、余計なCSSが増える可能性があります。
ですのでプレースホルダで共通のプロパティを用意して、それぞれのセレクタで個別のプロパティを書いたほうが効率が良いです。

@if @else

Sass

$ie7: false;
@if($ie7) {
    //$ie7: trueの場合
    .box {
        display: inline-block;
        *display: inline;
        *zoom: 1;
    }
} @else {
    .box {
        display: inline-block;
    }
}

CSS

.box {
  display: inline-block;
}

Sassはプログラミング言語でよく使われる条件分岐であるif〜elseを利用できます。サンプルコードでは変数$ie7でIE7をサポートするかしないか(true/false)で.boxのプロパティを変えています。
if関数に関しては昨年のAdvent CalendarのSassのif関数が参考になります。

@for

Sass

//スプライト画像の作成
$s1: sprite-map("/img/sprite/*.png", $spacing:20px, $layout: vertical);
//$name スプライト前の画像名
//$sorutes sprite-mapを指定
@mixin sprite-background($name, $sprites) {
  background: sprite-url($s1)no-repeat;
  background-position: sprite-position($sprites, $name);
  width: image-width(sprite-file($sprites, $name));
  height: image-height(sprite-file($sprites, $name));
}
//アイコン名を配列にセット
$socialList : Evernote, Facebook, Flickr, Github;
//length(配列名)で配列の長さを取得 nth(配列名, n)で配列名のn番目の要素を取得
@for $i from 1 through length($socialList){
  .icon-#{nth($socialList, $i)} {
    @include sprite-background(#{nth($socialList, $i)}, $s1);
  }
}

CSS

.icon-Evernote {
  background: url(/img/sprite-scc43fbc7aa.png) no-repeat;
  background-position: 0 -52px;
  width: 32px;
  height: 32px;
}
.icon-Facebook {
  background: url(/img/sprite-scc43fbc7aa.png) no-repeat;
  background-position: 0 -156px;
  width: 32px;
  height: 32px;
}
.icon-Flickr {
  background: url(/img/sprite-scc43fbc7aa.png) no-repeat;
  background-position: 0 -104px;
  width: 32px;
  height: 32px;
}
.icon-Github {
  background: url(/img/sprite-scc43fbc7aa.png) no-repeat;
  background-position: 0 0;
  width: 32px;
  height: 32px;
}

if文同様、for文もプログラミング言語でよく使われます。サンプルコードでは先述のCSSスプライト用mixinを呼び出す箇所でfor文を使えば非常に短いコードで書けます。
サンプルコードではwidth, height, backgroundが重複していますが、mixinを分けてプレースホルダでincludeして...と言った書き方にすればさらにきれいなCSSを出力できます。 配列と値の取得に関してはSass(Scss)配列と値の取得が参考になります。

@each

Sass

//スプライト画像の作成
$s1: sprite-map("/img/sprite/*.png", $spacing:20px, $layout: vertical);
//$name スプライト前の画像名
//$sorutes sprite-mapを指定
@mixin sprite-background($name, $sprites) {
  background: sprite-url($s1)no-repeat;
  background-position: sprite-position($sprites, $name);
  width: image-width(sprite-file($sprites, $name));
  height: image-height(sprite-file($sprites, $name));
}
@each $socialList in Evernote, Facebook, Flickr, Github {
  .icon-#{$socialList} {
    @include sprite-background($socialList, $s1);
  }
}

CSS

.icon-Evernote {
  background: url(/img/sprite-scc43fbc7aa.png) no-repeat;
  background-position: 0 -52px;
  width: 32px;
  height: 32px;
}
.icon-Facebook {
  background: url(/img/sprite-scc43fbc7aa.png) no-repeat;
  background-position: 0 -156px;
  width: 32px;
  height: 32px;
}
.icon-Flickr {
  background: url(/img/sprite-scc43fbc7aa.png) no-repeat;
  background-position: 0 -104px;
  width: 32px;
  height: 32px;
}
.icon-Github {
  background: url(/img/sprite-scc43fbc7aa.png) no-repeat;
  background-position: 0 0;
  width: 32px;
  height: 32px;
}

ループ処理はfor文以外にeach文も使えます。先述のfor文をeachに置き換えるとかなりわかりやすいコードで書けます。 for文では繰り返す数を指定しなければならないのですが、each文は配列の値分だけ繰り返してくれるので、length()やnth()と言ったfunctionを使わないですみます。
ベンダープリフィックスを付加するmixinのサンプルでeach文はよく見かけますね。不要なベンダープリフィックスはmixinの方で削除すればいいのでとても便利です。

Sass

$prefixPattern: '-webkit-', '-moz-', '-ms-', '-o-', '';
@mixin prefix($prop, $value) {
  @each $prefix in $prefixPattern {
    #{$prefix}#{$prop}: $value;
  }
}
.round-corner {
  @include prefix(border-radius, 5px);
}
.transition {
  @include prefix(transition, 0.5s);
}

CSS

.round-corner {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  -ms-border-radius: 5px;
  -o-border-radius: 5px;
  border-radius: 5px;
}
.transition {
  -webkit-transition: 0.5s;
  -moz-transition: 0.5s;
  -ms-transition: 0.5s;
  -o-transition: 0.5s;
  transition: 0.5s;
}

@function

Sass

$base-margin: 3em;
@function mg-half() {
  @return $base-margin / 2;
}
@function mg-thrid() {
  @return $base-margin / 3;
}
@function mg-quater() {
  @return $base-margin / 4;
}
section {
  margin-bottom: $base-margin;
  h1 {
    margin-bottom: mg-thrid();
  }
  p {
    margin-bottom: mg-half();
  }
  dl {
    margin: 0 mg-thrid() mg-half();
    dt {
      margin-bottom: mg-quater();
    }
    dd {
      margin-bottom: mg-thrid();
    }
    dd + dd {
      margin-bottom: mg-quater();
    }
  }
}

CSS

section {
  margin-bottom: 3em;
}
section h1 {
  margin-bottom: 1em;
}
section p {
  margin-bottom: 1.5em;
}
section dl {
  margin: 0 1em 1.5em;
}
section dl dt {
  margin-bottom: 0.75em;
}
section dl dd {
  margin-bottom: 1em;
}
section dl dd + dd {
  margin-bottom: 0.75em;
}

@functionは独自のファンクション(関数)を作る事ができます。@function内で実行された内容を@returnで返します。
サンプルコードではベースとなるmarginに対して2分の1、3分の1、4分の1の独自ファンクションを作り、それぞれのセレクタ内で呼び出してます。
@functionは@mixinほど使用頻度は少ないと思われますが、あれやりたい...なんて思った時に@functionでできる場合もあるので頭の片隅に入れておくと良いと思います。