Blog

Sass(Scss) Memo: 出力形式 compressed

Sassにはいくつかの出力形式がありますが、今回はcompressedについてです。
どのように出力されるのか見ていきます。

よくありそうなスタイルの記述

Scss
element+element {
    margin: 0.5em 0;
    padding: 0px;;;
    border: none;
    background-color: rgb(0,0,0);
    color: #ffffff; ;;

}
selector {
; // 空の宣言ブロック
}
CSS
element + element{margin:0.5em 0;padding:0px;border:none;background-color:black;color:#ffffff}
  • 余計な改行や空白は削除されます。
    例外として、セレクタの結合子(+ や >)の前後には空白が挿入されます。
  • 最後のプロパティのセミコロン(;)や余分なセミコロンが削除されます。
  • カラーコードは6桁のまま出力されます。3桁にはなりません。
    ただし、Mixinを通したものはどの出力形式でも条件によって出力される値が変わります。
  • rgbは可能なものはカラーコードに変換されます。
  • 0pxは0にはならず、そのまま0pxで出力されます。
  • noneは0にならず、そのままnoneで出力されます。
  • 0.5emは.5emにはならず、そのまま0.5emで出力されます。
  • 空の宣言ブロックは出力されません。

CSS/Sassコメント

Scss
selector-1 {
    margin: 0;
}
/*
 * 複数行CSSコメント
 * 複数行CSSコメント
 */
selector-2 {
    /* 1行CSSコメント */
    // Sassコメント
    padding: 0;
}
/*!
 * !付きの複数行CSSコメント
 * !付きの複数行CSSコメント
 */
selector-3 {
    /*! !付きの1行CSSコメント */
    color: red;
}
body /*! */ element {
    width: 100px;
}
CSS
selector-1{margin:0}selector-2{padding:0}/*
 * !付きの複数行CSSコメント
 * !付きの複数行CSSコメント
 */selector-3{/* !付きの1行CSSコメント */;color:red}body element{width:100px}
  • CSSコメントは削除されます(Sassコメントはどのコンパイル形式でも削除されます)。
  • 「/*!」から始まるCSSコメントはそのまま残ります。ただし、セレクタ部分に入れた場合は削除されます。

コメント(「/*!」から始めたコメントも)が削除されるので、次に挙げるハックは使用できません。

使用できないコメントを利用したハック

Scss
html[xmlns] > /**/body elements { property: value; }
html > /**/ body elements { property: value; }
/* \*//*/ selector { property: value; } /**/
/* \*/ selector { property: value; } /**/
など

コメントの削除には例外があります。プロパティの後ろにいれたコメントは削除されないので、次に挙げるハックは使用することができます。

使用できるコメントを利用したハック

Scss
selector {
    color/**/: red; // IE7,8,9と他のモダンブラウザ
    color/*\**/: green\9; // IE7,8,9
}
CSS
selector{color/**/:red;color/*\**/:green\9}

ほかのハックはどうでしょうか。気になりますね。。

IE6 スターハック

Scss
* html element { // IE6
    color: red;
}
CSS
* html element{color:red}

問題なし。

IE6 アンダースコアハック

Scss
selector {
    _color: red; // IE6
}
CSS
selector{_color:red}

問題なし。

IE7 スターハック

Scss
*:first-child + html element { // IE7
    color: red;
}
CSS
*:first-child + html element{color:red}

問題なし。

IE6,7 アスタリスクハック

Scss
selector {
    *color: red; // IE6,7
}
CSS
selector{*color:red}

問題なし。

IE6,7 スラッシュハック

Scss
selector {
    /color: red; // IE6,7
}

エラーになります。
変数を使いましょう。でも一番早いのは / の代わりに * を使うことです。

Scss
$slash: "/";

selector {
    #{$slash}color: red; // IE6,7
}
CSS
selector{/color:red}

これで問題なし。

IE \9ハック

Scss
selector {
    color: red\9; // IE6,7,8,9 (10も??)
}
CSS
selector{color:red\9}

問題なし。

IE \0/ハック

Scss
selector {
    font-family: Meiryo, sans-serif\0/; // IE6,7,8,9 (10も??)
}

エラーになります。
変数を使いましょう。

Scss
$hack-ie6to9: "\0/";

selector {
    font-family: Meiryo, sans-serif#{$hack-ie6to9}; // IE6,7,8,9 (10も??)
}
CSS
selector{font-family:Meiryo,sans-serif\0/}

これで問題なし。

セレクタハック 1

Scss
html > body element { // IE7と他のモダンブラウザ
    color: red;
}
CSS
html > body element{color:red}

問題なし。

セレクタハック 2

Scss
html + body element { // IE7と他のモダンブラウザ
    color: red;
}
CSS
html + body element{color:red}

問題なし。

Firefox moz-any-linkハック

Scss
element, x:-moz-any-link {
    color: red;
}
CSS
element,x:-moz-any-link{color:red}

問題なし。

WebKit mediaqueryハック

Scss
@media screen and (-webkit-min-device-pixel-ratio: 0) {
    selector {
      color: red;
    }
}
CSS
@media screen and (-webkit-min-device-pixel-ratio: 0){selector{color:red}}

問題なし。

@importを使ったハック

もう使うことはなさそうですが、これらのハックも使用できません。

@import'style.css'; → @import url(style.css); に整形される。
@import style.css;  → エラーになる。
など

結論

いくつかのハックが使用できなくなりますが、それ以外は特に問題なさそうです。

ただ、GitやSVNなどのバージョン管理ツールでのCSSの差分確認は難しくなると思いますのでご注意を。

font-familyの指定はウェイトなしのアルファベット表記のみでほぼよさそう

下記のコードはフォントの指定によく使われていると思います。

font-family: 'ヒラギノ角ゴ Pro W3','Hiragino Kaku Gothic Pro','メイリオ',Meiryo,'MS Pゴシック','MS PGothic',sans-serif;

同じフォントを日本語表記とアルファベット表記の両方で指定しているのは、各ブラウザの解釈や挙動が異なるなどの理由からです。ただ、最近のブラウザはどうなんだろうかとちょっと気になったので調べました。

結果としては下記の表になりました。詳細はデモページを見てください。

  • Parallels上のWin7: IE6-8(IE6,7はIETester), Chrome15, Firefox8, Safari5, Opera11
  • Mac OS X 10.6.8: Chrome15, Firefox8, Safari5, Opera11
  • 文字コード: UTF-8
指定方法 IE Chrome Safari Firefox Opera
Win WinMac WinMac WinMac WinMac
日本語表記 × × ×
日本語表記(ウェイトあり) - -× -× -△(*1) -×
アルファベット表記 △(*2)
アルファベット表記(ウェイトあり) - -× -× -△(*1) -
ポストスクリプト × ×× ×× ×× ××
ポストスクリプト(ウェイトあり) - - - -× -×
  • *1: ウェイトは無視され、ウェイトなしのものが適用される。
  • *2: ウェイトありで指定しないと適用されないものもある。

まとめ

ウェイトなしのアルファベット表記だけで多くのブラウザに対応できそうです。
問題が出るかもしれないのはMac Operaだけです。

記事の冒頭で挙げたコードは下記のようにすることができると思います。

font-family: 'Hiragino Kaku Gothic Pro',Meiryo,'MS PGothic',sans-serif;

Mac Operaですが、メイリオ、MS Pゴシックが入ってなければ、sans-serifが最後にあるのでヒラギノ角ゴ Pro W3が適用されます。もしくは、VerdanaやTahomaなどを指定すれば日本語部分はヒラギノになります...。

もしかすると今回の検証だけでは足りないかもしれませんが時間がないので今回はこのへんで。

iOS5なのにposition:fixed;が効かない時に確認すること

なんだかんだでFirefoxメインで制作しているわけですが、とあるページでposition:fixed;が効いてないみたいだよ?と@e_luckさんに教えてもらいまして、、、でちょっと調べてみて気づいたことです。

HTML5 Rocks - Improving the Performance of your HTML5 Appの「The magic CSS bullet」で紹介されていた下記のコードは、指定するとGPUアクセラレーションが有効になるのでパフォーマンス向上の恩恵を受けることができるかもしれない、といったような事が言われています(アニメーションの描画がスムーズになる?)。

GPUアクセラレーションを有効にするスタイル
-webkit-transform: translateZ(0);

ということで、これをbodyタグに指定してたんですが、、position:fixed;にならない原因はこのスタイルでした。これについてはW3CのWDに書いてありました。

The object acts as though position: relative has been specified, but also acts as a containing block for fixed positioned descendants.

CSS 3D Transforms Module Level 3

どおりでfixedを指定していた子孫要素が全部relativeの振る舞いになっちゃってたわけですね。

次のデモページでは親要素に-webkit-transform:translateZ(0);の指定がある場合とない場合の子要素の挙動を確認できます。WebKit系のブラウザでみてください。

デモページをみる

新しいプロパティを使うときは仕様書をちゃんと読んだほうが良さそうですね...。

Sass(Scss) Memo: @mixin

今回は@mixinについてです。
Mixinはスタイル(規則集合や宣言)を定義し、それを再利用することができます。
定義したMixinは@includeでインクルードして使用します。また、Mixinには引数を渡すことができます。

Mixinの利用例

clearfix用のMixinを例に説明します。

まずはMixinを定義します。

Scss
@mixin clearfix {
    *zoom: 1;
    //
    &:after {
        content: "";
        display: block;
        clear: both;
    }
}

これをインクルードして利用します。

Scss
.clearfix {
    @include clearfix;
}

これをコンパイルすると下記のようになります。

CSS
.clearfix {
  *zoom: 1;
}
.clearfix:after {
  content: "";
  display: block;
  clear: both;
}

簡単だし、コード管理も楽ですね。

ただ、別のセレクタにも同じコードを適用する場合、@extendを利用することも検討してください。

Scss
.clearfix {
    @include clearfix;
}
.header {
    @extend .clearfix;
}

これをコンパイルすると下記のようになります。

CSS
.clearfix, .header {
  *zoom: 1;
}
.clearfix:after, .header:after {
  content: "";
  display: block;
  clear: both;
}

セレクタをグループ化して出力されるので、セレクタごとにコードが展開される@mixinよりも全体のコード量を減らすことができます。

引数について

引数はMixinの中で変数として使うことができ、CSSのプロパティやプロパティの値、@ifの条件式などに利用することができます。

下記は引数を伴うMixinの例です。

Scss
@mixin sample-mixin($var1, $var2: margin, $var3: false) {
    width: $var1;
    #{$var2}: 0;
    @if $var3 {
        overflow: hidden;
    }
}
  • 引数を複数指定する場合は,(カンマ)で区切ります。
  • 初期値のない引数は初期値がある引数よりも必ず先に書かなければエラーになります($var1: false, $var2だとエラー)。
  • 初期値が指定されていない引数はインクルード時に値を指定しないとエラーになります。

インクルード時の引数の記述例

前述のsample-mixinを例にします。

Mixinの引数の順番通りに指定する

単純にMixinの引数の順番通りに値を指定します。
引数のキーワード($var1や$var2など)を記述する必要がないのでタイプ数が少なくてすみます。
ただ、コードを後で見返したときにMixinによっては引数の値が何なのかが分かりづらいかもしれません。

Scss
.selector {
    @include sample-mixin(100px, padding, true);
}

特定の引数だけ指定する

$var2は初期値を利用して、$var3の値だけ指定します。

Scss
.selector {
    @include sample-mixin(100px, $var3: true);
}

キーワードを使い、順不同で指定する

$var3を記述した後に$var2を記述します。
引数のキーワードを記述すると引数の順番が関係なくなるようです。ただ、コードの見通しが悪くなりそうなので使わないほうが良さそうですが...。

Scss
.selector {
    @include sample-mixin(100px, $var3: true, $var2: padding);

    // 下記のように$var1を最後に記述してもエラーにはなりません。
    @include sample-mixin($var3: true, $var2: padding, $var1: 100px);
}

Mixinを使えば作業をかなり効率化することができますが、複雑すぎると運用・管理のコストを増やしてしまう可能性があるのでバランスを考慮して利用したいですね。

Sass(Scss) Memo: @extend

今回は@extendについてです。
別のセレクタの全てのスタイルを継承することができます。

Scss
.borderRed {
    border: 1px solid red;
}
.selectorA {
    @extend .borderRed; // ここで.borderRedを継承指定
    padding: 2px;
}
出力結果のCSS
.borderRed, .selectorA { /* ここに .selectorA が追加されます */
  border: 1px solid red;
}

.selectorA {
  padding: 2px;
}

@extendに使用できるセレクタ

1つの要素のみに関連するセレクタであればほとんど使用できます。 要素セレクタやid/classセレクタや擬似クラス、属性セレクタなども使用できますし、各セレクタを連結させたもの(div#selectorA.selectorBdiv.selectorA:first-childなど)も使用できます。

ただ、いまのところ(Sassバージョン3.1.1)は子孫/子/兄弟セレクタ(E FE > FE ~ F)と隣接セレクタ(E + F)は使用できません。
全称セレクタ(*)も使用できますが、使う場面はなさそうですね。

あと、@extend .selectorA, .selectorB;みたいに複数一括指定することはできないので分けて書きましょう。

Scss
.selectorA {
    @extend div;
    @extend #id;
    @extend .class;
    @extend div.class; // 要素も指定しているので、p.classがあっても継承しません
    @extend #id.class;
    @extend .class-1.class-2;
    @extend .selector:hover;
    @extend .selector[attr];
    @extend *; // これは使うことなさそうだけど...

    // ここから下はエラーになります
    @extend div .class; // E F
    @extend div > .class; // E > F
    @extend div + .class; // E + F
    @extend div ~ .class; // E ~ F
}

@extendをうまく使えば、セレクタのグループ化によってCSSのファイルサイズを減量したり、スプライト用の画像の読み込み指定をまとめてhttpリクエストを減らすこともできるのでとても便利です。

が、、気をつけなければいけないことがあります。

気をつけること 1

継承するのは@extendで指定したセレクタ自身のスタイルだけではなく、その子要素などのスタイルも継承するということです。

Scss
.selectorA {
    padding: 20px;
    border: 1px solid #333;
    //
    h2 {
        font-weight: bold;
        font-size: 20px;
    }
    p {
        color: #666;
    }
}
body.firefox .selectorA {
    padding: 10px;
}
.selectorB {
    @extend .selectorA; // ここで.selectorAを継承指定
    background: #eee;
    //
    h2 {
        font-size: 14px;
    }
}
CSS
.selectorA, .selectorB {
  padding: 20px;
  border: 1px solid #333;
}
.selectorA h2, .selectorB h2 { /* ここにも追加されました */
  font-size: 20px;
}
.selectorA p, .selectorB p { /* ここにも追加されました */
  color: #666;
}

body.firefox .selectorA, body.firefox .selectorB { /* ここにも追加されました */
  color: #666;
}

.selectorB {
  background: #eee;
}

.selectorB h2 {
  font-size: 14px;
}

こういう問題があるかもしれません。

  • .selectorA h2は太字にしたいけど、.selectorB h2はスタイルリセット時にfont-weight: normal;にしてるからそのままでよかったのに...。
  • .selectorB pのテキストカラーはbodyで適用されてる色でよかったのに...。
  • body.firefox .selectorBはスタイル変える必要がないのに...。
  • 後日、.selectorA h2のテキストカラーを変更することになったのでスタイル追加したら、当然 .selectorB h2にも適用された...。
    複数人で制作していると、別のメンバーがつくったセレクタを継承してたらいつの間にか結構変わってたとかセレクタ自体が削除されてた(エラー出ないので気づかない)なんてこともあるかも。

気をつけること 2

@extendで指定したセレクタを含むセレクタも継承することです。

たとえば下記のような場合、要素指定なしの.selectorAのスタイルだけを継承したいとします。

Scss
.selectorA {
    text-decoration: none;
}
a.selectorA {
    text-decoration: underline;
}
.selectorB {
    @extend .selectorA; // ここで.selectorAを継承指定
}

ところが、期待通りの出力結果になりませんでした。

CSS
.selectorA, .selectorB {
  text-decoration: none;
}

a.selectorA, a.selectorB { /* こちらにも追加されている */
  text-decoration: underline;
}

さらに属性セレクタに関してはバグ(?)があるようです。

.selectorBで属性セレクタ.selectorA@extendします。

Scss
a.selectorA {
    text-decoration: none;
    //
    &[href] {
        text-decoration: underline;
    }
}
.selectorB {
    @extend .selectorA;
}

期待する出力結果は下記になります。

CSS
a.selectorA, a.selectorB {
  text-decoration: none;
}

a.selectorA[href], a.selectorB[href] {
  text-decoration: underline;
}

実際の出力結果では、意図しないセレクタが出力されます。

CSS
a.selectorA, a.selectorB {
  text-decoration: none;
}

a.selectorA[href], a[href].selectorB { /* これはどういうことですか... */
  text-decoration: underline;
}

複雑なセレクタに使うとややこしいですね。。
@extendを使用するルールは決めておいたほうが良いと思います。

@extendの使用ルール案
  • @extendで使用するセレクタはスタイルや要素が、ある程度固定されたものにする。
    おなじみのclearfixやスプライト用の指定とか。
  • @extend専用のセレクタ(.extend-fooとか)を用意して利用する。
    このセレクタに対するあらゆる変更は、それを利用している他のセレクタにも影響することが分かるので、変更は注意しておこなうだろうし、変更前に影響範囲を確認するというフローをつくることもできると思います。