2016年11月1日火曜日

z-indexとか何とかとか

あなたはhtml/cssにおける要素の重なり方を知っていますか?
z-index
を指定しなければ先に書いたほうが下層で後に書けば上層に来る?

というわけで、実は割りと複雑な要素の重なりについて書きます。そこまで意識しなくとも支障がでることは滅多にないと思いますが、知っておいて損はないはずです。
ちなみに、昨今CSS3なるものが流行していますが、この記事はW3CCSS2.1勧告(と付録)を元に書いています。
基本的な内容であり、目新しいことは特になにもありません。

スタックコンテキストとスタックレベル

用語と概念から。
ある意味、ここが一番よくわかりませんが、ここがわからないと何もわかりません。

スタックコンテキスト(スタッキング・コンテキスト)

簡単に言えばauto以外のz-indexと位置(static以外のposition)を指定した要素によって生成される階層構造を形成する固まりです。ただし、ルート要素(html)はルートスタックコンテキストを整形するので、例えz-indexを一つも指定しなくとも、スタックコンテキストは存在することになり、各ボックスは何らかのスタックコンテキストに属することになります。

スタックコンテキストはその内部に追加のスタックコンテキストを含むことができ、z-indexに整数値を指定すると自身のスタックレベルを0としたローカルスタックコンテキストが生成されます。しかし、スタックコンテキストは各階層における構造によるものなので、あるボックスが同時に複数のスタックコンテキストに股がって属したり、他のスタックコンテキスト内のボックスが任意のボックスの間にくることはできません。

スタックレベル

ボックスは同一スタックコンテキスト内でのz軸の位置となるスタックレベルを持ちます。まぁ、z-indexで指定する数字です。

同じスタックコンテキストに同じスタックレベルを持つボックスがある場合、構造内での順番に従って背面から前面へとボックスが積み重ねられます。

基本的な重なり

基本的に以下のレイヤー順番で重なります(スタックコンテキスト内での子孫の塗装順序)。W3C勧告に倣い、先に書いたものが下層、後に書いたものが上層になります。

1. スタックコンテキストが整形する要素の背景とボーダー

2. 負のスタックレベルをもつ子スタックコンテキスト

3. インラインレベル以外の位置(position)を指定していない要素

4. 位置(position)を指定していないフロートしている要素とその子孫

5. インラインレベルの位置(position)を指定していない要素(但しインラインテーブル・インラインブロックを含む)

6. z-indexauto又は0の要素とその子孫

7. 正のスタックレベルを持つ子スタックコンテキストとその子孫

以上の基準に基づきながら、同レイヤーに存在する・同スタックレベルを持つ場合は後から書いたものが上層に、先に書いたものが下層に配置されます。

z-index

注:z-indexはブラウザによるバグが少なくなく以下に書くように動作しない場合があります。

z-indexは垂直位置を指定する

z-indexはボックスの重なりの順序を指定するプロパティであるとよく言われていますが、垂直(z軸の)位置を指定するプロパティと考えた方がよりわかりやすいのではないでしょうか。x軸・y軸の位置をtop(bottom)left(right)で指定するのと同様にz軸での位置をz-indexで指定するのです(スタックコンテキスト内で)。

よって、x軸・y軸の位置を指定するときと同様にpositonstatic以外(relativeabsolutefixed)を指定しなければいけません。

z-indexにはpositionが必要positionあるところにz-indexあり

z-indexを指定するには position: static; 以外の指定が必要といいましたが、逆に、position: static; 以外を指定すれば自動的にz-indexが指定されていることにもなります。ある要素に、あなたがposition: static; 以外を指定すると、その要素は自動的にz-indexの初期値である auto が指定されたことになります。

普段意識せずに使っているかもしれませんが、positionを指定した要素がz-indexを指定せずとも以降の要素の下に隠れないのは、これによるものです。

先ほどのリストを思い出して下さい。

z-indexauto又は0の要素とその子孫(つまり、positionを指定した要素)」は「正のスタックレベルを持つ子スタックコンテキストとその子孫」を除き、その他の要素より上層のレイヤーとなります。これにより、absolutefixedでのナビゲーション固定などをz-indexを指定せずとも自然に実現できているわけです。


実際のコードとサンプル

z-index: auto; z-index: 0; の違いとスタックコンテキスト

z-index: auto; を指定したボックスのスタックレベルは0であり、上記のようにスタックコンテキストを作成したかのように要素を扱いますが、実際に新しいスタックコンテキストを作成しているわけではなく、親スタックコンテキストの一部と考慮されるます。対して、z-index: 0;はスタックコンテキストを生成します。


どちらも色の薄い方に z-index: -1; を指定しています。
実際のコードとサンプル

z-index: 0; を指定した要素はその指定した要素の背景が最下層となりますが、z-index: auto; を指定した要素はそうではありません。先ほどのリストを見ると、スタックコンテキストの最下層はスタックコンテキストが整形する要素の背景であることから、z-index: auto;を指定した要素は実際には新しいスタックコンテキストを作成しているわけではないことがわかります。

尚、これは::before::afterといった擬似要素にスタックレベルを指定した場合でも同様の動きをします。擬似要素の元になる要素がスタックコンテキストを生成した場合、::before::afterに関わらずそのスタックコンテキストに属することになります。よって、CSS3によって流行りとなった紙が捲れたような影を付ける場合などは、元の要素にz-indexを指定して新たなスタックコンテキストができてしまうと、幾ら擬似要素にマイナスのスタックレベルを指定しようとも影が元要素の背景より上に出てしまいますので注意が必要です。


実際のコードとサンプル

新たなスタックコンテキストが出来る出来ないという結果を見てもう一つわかることは、このスタックコンテキストというものがあることにより、z-indexを指定したからといえ完全に好きな階層に置けるわけではないということです。スタックレベルは同じスタックコンテキスト内での順序を決めるものであるため、そのスタックコンテキスト外では機能しません。

例え先祖要素であってもより近い先祖要素にz-indexを指定した場合、その要素の背景より下層に配置することはできませんし、もちろん、先述した通り他のスタックコンテキスト内の任意の層に配置することも出来ません

逆に言えば、余計なところにスタックコンテキストを生成しなければよいわけですから、不必要にz-indexを指定するのを避ければよいということにもなります。

ちなみに、有名なバグとしてIE7ではz-index: auto; でもスタックコンテキストを生成します。割りと困ります。

z-index以外のプロパティのスタックコンテキストの導入 opacity

z-index以外でもスタックコンテキストを導入することが認められており、実際にz-index以外でもスタックコンテキストを生成するプロパティは既に存在します。

例えば、CSS3で加えられた透明度を操作するopacityはスタックコンテキストを生成します。つまり、z-indexを指定しなくとも、opacity1未満を指定(基準値の 1 ではスタックコンテキストが出来ない)した場合、その要素をスタックレベル0としたスタックコンテキストが生成されてしまうので注意が必要です。


どちらも色の薄い方に z-index: -1; を指定しています。
実際のコードとサンプル

z-index以外の重なり

先ほどのリストで示した通りなのですが、一応サンプルを用意しました。


span
float p の順に記述。pのみ color: white;
実際のコードとサンプル

p よりも span floatさせた要素の方が先に記述されていますが、リストの通り、spanfloatさせた要素の背景の方が後に記述された p の背景より上層にきています。
ここで難しいことは、位置(position)指定されていない要素の階層はそれ自体(主に背景とボーダー)のものであり、子孫要素や内部のテキスト等はまた異なるレイヤーに置かれるます。よって、上記の図でもわかるとおり、背景はリストに従い span のものが上層にきていますが、内部のテキストは後から記述された p の内部のものが上層にきています。

 

Sassの基本的な記述方法

記法の前に

Sass記法の前に、SassCSSの「完全な上位互換」と言えるので、CSSのルールはすべてそのまま利用することが出来ます。
その為、cssファイルをそのまま拡張子だけ .scssに変更して変換(コンパイル)し直すみたいな事も可能です。(圧縮できる程度のメリットしか有りませんが)

軽く余談ですが、このCSSとの完全な互換性が従来のSassには無かったのですが、現行のバージョンではそれが出来るので、余計な事を覚える必要がなく、Sassの機能を今までのCSSの知識にプラスアルファとして使えるんです。
これに寄ってハードルが下がり、学習コストも最低限で済むので非常に意味の有る事かなーと。

ただ、一部のCSSハックに関しては変換した際にエラーになってしまうので、その場合は別の方法で書く必要があります。
細かい部分まで把握できていませんが、引っかかる可能性が高いハックとしては次のような「/」を使ったIE6/7用のハックを使うとエラーになります。

view plaincopy to clipboardprint?

1.  .item {  

2.      /zoom1;  

3.  }  

/」が使えないので、この場合は「*」にすれば問題有りません。

view plaincopy to clipboardprint?

1.  .item {  

2.      *zoom1;  

3.  }  

いきなりちょっと不便っぽい部分が出てきましたが、Sassの素敵なトコの1つとしてエラーがちゃんと表示されることです。
その為、ScoutLogを見ると下記のように表示されます。

>>> Change detected to: style.scss
error style.scss (Line 12: Invalid CSS after ".item {": expected "}", was "/zoom: 1;")
overwrite style.css 

英語が出来なくても『style.scss 12行目でエラーが出てる!何か /zoom: 1; InvalidCSSっぽい!』って感じで分かるので、変にハマって時間を取られません。

/」以外にも単純な文法違反でもちゃんとエラーが返ります。
例えば、、、

view plaincopy to clipboardprint?

1.  .item {  

2.      width350px;  

3.  }s  

何か、} の後に s が残ってる!!!(きっと、Ctrl+SしたつもりでCtrlが押せてなかったんですね)とかでもちゃんとエラーになるので、ケアレスミスが原因で時間を取られてしまう事が減ると思います。
記事書いてて思いましたけど、これって結構ステキですね。

互換性の話だったのに、気付けばエラー処理の話になってしまいましたが、こんな感じで今のCSS知識はそのまま使うことが出来ます。


Sassの基本的な記法

ルールのネスト - Nested Rules

何はともあれコレだけはマスターしておきたい記法です。
基本的な機能の中でもダントツで利用するのがネストで、入れ子でスタイルを書いていく事が可能です。

いつものCSS

view plaincopy to clipboardprint?

1.  #main {  

2.      width600px;  

3.  }  

4.  #main p {  

5.      margin-bottom1.5em;  

6.  }  

いつもはこんな感じですよね?
これを、Sass記法のネストを使うと、、、

Sass

view plaincopy to clipboardprint?

1.  #main {  

2.      width600px;  

3.      p {  

4.          margin-bottom1.5em;  

5.      }  

6.  }  

と、書くことが可能です。
上記は一番シンプルな例ですが、多少入れ子が多い例も上げてみます。

いつものCSS

view plaincopy to clipboardprint?

1.  #header {  

2.      width940px;  

3.  }  

4.  #header h1 {  

5.      floatleft;  

6.  }  

7.  #header ul#nav {  

8.      height50px;  

9.  }  

10. #header ul#nav li {  

11.     floatleft;  

12. }  

13. #header ul#nav li a {  

14.     floatleft;  

15.     width80px;  

16. }  

ヘッダーっぽい部分を例にしてみました。
これを、Sass記法のネストを使うと、、、

Sass

view plaincopy to clipboardprint?

1.  #header {  

2.      width940px;  

3.      h1 {  

4.          floatleft;  

5.      }  

6.      ul#nav {  

7.          height50px;  

8.          li {  

9.              floatleft;  

10.             a {  

11.                 floatleft;  

12.                 width80px;  

13.             }  

14.         }  

15.     }  

16. }  

こんな風に書くことが出来ます。
これを保存して変換するといつものCSSと同じ感じにしてくれます。

ちなみに、インデントは無くても大丈夫です。この辺りは可読性が良いと思う方法で書いてもらえれば。

これだけでもコピペから解放され、単純に記述量も減っているのが分かると思いますが、このネストのメリットはメンテナンス性の部分でもかなり力を発揮します。

例えば、上記の #header #globalHeader に変更する必要が出たとします。
この際に、いつものCSSでは #header から始まる全てを変更なり置換なりをしないと行けませんが、ネストを利用して書いている場合、最初の #header を変更するだけでOKです。

これにより、IDやクラス名が違う別の案件へのコピペも楽になり、再利用性が向上しますよね。

ネストには、プロパティやmedia queriesでも使うこと出来たりするんですが、その辺りの詳細は後のエントリーで書く予定です(ちゃんと書けよ自分)。

& 親セレクタの参照 - Referencing Parent Selectors

セレクタに「&」を使うことでネストしている際の親セレクタの参照が可能です。

Sass

view plaincopy to clipboardprint?

1.  a {  

2.      font-weightbold;  

3.      &:hover {  

4.          colorred;  

5.      }  

6.  }  

& が親セレクタを参照するので、&:hover a:hover としたのと同じ扱いですね。
これを、変換すると次のようになります。

変換されたCSS

view plaincopy to clipboardprint?

1.  a {  

2.      font-weightbold;  

3.  }  

4.  a:hover {  

5.      colorred;  

6.  }  

変換されたCSSは、実際に変換されたCSSとは違う可能性が有ります(これ以降のも同様に)。

この例だと殆ど利点が分かりませんが、これの最たる例がCSSシグネチャとして利用したい場合かと思います。

例えば、トップページだけ色が変わってる場合なんかに、body要素に指定したclassIDでスタイルを振り分けることが有ると思います。

いつものCSS

view plaincopy to clipboardprint?

1.  #sub {  

2.      floatright;  

3.      width200px;  

4.      background#ccc;  

5.  }  

6.  .body-top #sub {  

7.      background#fff;  

8.  }  

いつものだと、こんな風にしますよね。
Sass
のネストを使って書いていると、部分的にスタイルを振り分けるのにネストされた中だと親は参照できないので、そう言った場合に「&」を利用します。

Sass

view plaincopy to clipboardprint?

1.  #sub {  

2.      floatright;  

3.      width200px;  

4.      background#ccc;  

5.      .body-top & {  

6.          background#fff;  

7.      }  

8.  }  

こんな感じで、&を使うことでネストされた状態でも親セレクタを参照できます。
この程度の例なら、ネストしないで外に書けば良いんですが、ネストされた状態が長くなると、記述する位置がだいぶ変わってしまい分かりにくくなるので、そんな場合に使っていけます。

コメント - Comments: /* */ and //

CSSのコメントは /* */ で囲みますが、Sassだと通常のコメントに加え、// による1行コメントが可能です。

Sass

view plaincopy to clipboardprint?

1.  //メインコンテンツ  

2.  #main {  

3.      width600px;  

4.      // p要素  

5.      p {  

6.          margin-bottom1.5em;  

7.      }  

8.      // ul要素  

9.      ul {  

10.         margin0 0 1.5em 20px;  

11.         li {  

12.             margin-bottom5px;  

13.         }  

14.     }  

15. }  

JSとかちょっとでも触れてれば、1行コメントは馴染みが有ると思うので詳しい説明は不要かと思いますが、変換した際に1行コメントに関しては削除され、いつものCSSによるコメントは変換しても基本的に残ります。

変数 - Variables

変数と聞くと、軽くプログラムアレルギーが出る人も居るかも知れませんが、ちょっと使うだけならとても便利な機能なのでこれもぜひ覚えましょう。

変数は、任意の名前を付けて、任意の場所で呼び出したりすることが出来る機能です。

Sass

view plaincopy to clipboardprint?

1.  $yokohaba: 250px;  

こんな風に変数を定義しておけば、任意の場所で変数の値を呼び出すことが出来ます。
変数の名前は$から始め、その後は、いつものCSSと似た感じで「:」で区切ってから値を指定します。

Sass

view plaincopy to clipboardprint?

1.  #sub {  

2.      width: $yokohaba;  

3.      ul.bnrList {  

4.          width: $yokohaba;  

5.      }  

6.  }  

定義した変数を、値の部分に入れることで呼び出されます。
これを変換すると次のようになります。

変換されたCSS

view plaincopy to clipboardprint?

1.  #sub {  

2.      width250px;  

3.  }  

4.  #sub ul.bnrList {  

5.      width250px;  

6.  }  

変数を上手く利用することで、重複したスタイルを何度も書く必要が無く、値とかも間違えたりしなくなります。

ちなみに、変数はファイルの最初の方にまとめておいたり別ファイルに記述しておくと見通しが良くメンテナンス性が向上するかと思います。

他の例として、基本のフォントカラーやリンクカラーを予め指定しておけば良い感じです。また、変数の値にはマルチバイト文字も使えるのでfont-familyの指定とかも可能です。

Sass

view plaincopy to clipboardprint?

1.  // ベースフォント  

2.  $base_font: "メイリオ""Meiryo"verdana"ヒラギノ角ゴ Pro W3""Hiragino Kaku Gothic Pro", Osaka, "MS Pゴシック""MS PGothic", Sans-Serif;  

3.     

4.  // ベーステキストカラー  

5.  $font_color: #333;  

6.     

7.  // ベースリンクカラー  

8.  $link_color: #06f;  

9.  $link_color_hover: #00f;  

10.    

11.    

12. body {  

13.     color: $font_color;  

14.     font-family: $base_font;  

15. }  

16. #main {  

17.     a:link {  

18.         color: $link_color;  

19.     }  

20.     a:hover {  

21.         color: $link_color_hover;  

22.     }  

23. }  

これを、変換すると次のような感じになります。

変換されたCSS

view plaincopy to clipboardprint?

1.  body {  

2.      color#333333;  

3.      font-family"メイリオ""Meiryo"verdana"ヒラギノ角ゴ Pro W3""Hiragino Kaku Gothic Pro", Osaka, "MS Pゴシック""MS PGothic", Sans-Serif;  

4.  }  

5.  #main a:link {  

6.      color#0066ff;  

7.  }  

8.  #main a:hover {  

9.      colorblue;  

10. }  

この例だけだと、記述する量が増えてるのでアレですが、#main以外にも#footerで同じ色を使いたいとかそんな時に値を覚えてる必要が無いので、微妙に色を間違えるとかが無くなって統一性が出しやすくなったりします。

この他にも変数には、便利な使い方が多くありますが、その辺りの詳細は後のエントリーで書く予定です(ちゃんと書けよ自分)。

演算

Sassでは、足したり引いたりって言う演算が出来ます。
この演算がかなり頭が良いので非常に素晴らしいのですが、複雑なことは抜きにしてココではカンタンで実用性が高そうな例を。

Sass

view plaincopy to clipboardprint?

1.  .item {  

2.      width300px - 14px;  

3.      padding7px;  

4.  }  

こんな風に書けます。これを変換すると、、、

変換されたCSS

view plaincopy to clipboardprint?

1.  .item {  

2.      width286px;  

3.      padding7px;  

4.  }  

こうなるんです!もう電卓から解放される!!
ボクみたいに暗算が苦手な人からすると、これヤバイ。
box-sizing: border-box;
が使えれば、paddingの計算とか不要ですけど、あのブラウザに対応させるには、こんなプロパティは使えないですからね...

ちなみに、単位は省略してもよしなにやってくれるので「14」と書いても同じ結果が返ってきます。
単位の省略に加えこんな風にすることも。

Sass

view plaincopy to clipboardprint?

1.  .item {  

2.      width300px - (7 * 2) - 2;  

3.      padding7px;  

4.      border1px solid #666;  

5.  }  

お前どんだけ計算したくないんだよ!って感じもしますが、感覚的にはあくまでも横幅は300pxなんだよ!!と思うわけです。ボクは。
これを変換すると期待した通り次のようになります。

変換されたCSS

view plaincopy to clipboardprint?

1.  .item {  

2.      width284px;  

3.      padding7px;  

4.      border1px solid #666;  

5.  }  

ミックスイン - Mixins

ミックスインは、プロパティやセレクタをまとめてワンセットにしておく事が可能で、それらをカンタンにインクルード(読み込む)する事が出来ます。
変数の機能を強化したと言うか便利にしたようなイメージです。

Sass

view plaincopy to clipboardprint?

1.  @mixin inline_block {  

2.      display: inline-block;  

3.      *displayinline;  

4.      *zoom1;  

5.  }  

@mixinの後に半角スペース、任意の名前って感じで定義します。

この例では、display: inline-block; IE でも使う場合に、 *display: inline; *zoom: 1; を指定する必要があります。
これを毎回書くと面倒なので、ミックスインを使って予め定義しておくことで、次のように1行書くだけでインクルード出来ます。

Sass

view plaincopy to clipboardprint?

1.  .sampleBox {  

2.      @include inline_block;  

3.      background#eee;  

4.  }  

@includeの後に半角スペース、ミックスインで定義した名前って感じで記述します。
当然、他の宣言と一緒に書くことが可能です。
これを変換すると、、、

変換されたCSS

view plaincopy to clipboardprint?

1.  .sampleBox {  

2.      display: inline-block;  

3.      *displayinline;  

4.      *zoom1;  

5.      background#eee;  

6.  }  

無事にインクルードされ、展開されました。
こんな感じで、何度も使うようなスタイルや長めの宣言を予め定義してインクルード出来るので、かなりの工数削減になると思います。
特にCSS3使ってて、ベンダープレフィックスが多い場合とか。

このミックスインもかなり強力な機能なので、がっつりやり出すとかなり覚えたりやることが多いですが、いきなり複雑な事はせずに、この宣言何度も書いてるなーと思ったらミックスインで定義しておく位が最初は良いのかなと。

ちなみに、この inline-block程度ならミックスインではなく次に説明する セレクタを継承出来る @extend を使った方が良いかもです。

セレクタの継承 - Selector Inheritance@extend

@extend は、一度使ったセレクタを継承して使うことが出来ます。
ミックスインと似てる部分も多いのですが、似て非なるものって感じで用途がだいぶ変わってきます。
文だと伝わる気がしないので、例を見てください。

Sass

view plaincopy to clipboardprint?

1.  .imgR {  

2.      floatright;  

3.      margin-left10px;  

4.      margin-bottom1.5em;  

5.  }  

これだけ見ても、いつものCSSと何ら変わりは有りませんが、この一度使ったセレクタを使うことが出来ます。

Sass

view plaincopy to clipboardprint?

1.  .photo {  

2.      @extend .imgR;  

3.  }  

と書くことで .imgR で指定した宣言が .photo でも継承されます。
これを変換すると、、、

変換されたCSS

view plaincopy to clipboardprint?

1.  .imgR, .photo {  

2.      floatright;  

3.      margin-left10px;  

4.      margin-bottom1.5em;  

5.  }  

こんな感じで変換されるので、clearfix等のように一度使ったセレクタを再利用したい場合に便利な機能です。

ちなみに、clearfix @extend で利用する場合はこんな感じです。

Sass

view plaincopy to clipboardprint?

1.  .clearfix {  

2.      *zoom1;  

3.      &:after {  

4.          content".";  

5.          displayblock;  

6.          clearboth;  

7.          height0;  

8.          visibilityhidden;  

9.      }  

10. }  

11.    

12. ul.menu {  

13.     @extend .clearfix;  

14.     background#ccc;  

15.     li {  

16.         floatleft;  

17.         width100px;  

18.     }  

19. }  

変換されたCSS

view plaincopy to clipboardprint?

1.  .clearfix, ul.menu {  

2.      *zoom1;  

3.  }  

4.  .clearfix:after, ul.menu:after {  

5.      content".";  

6.      displayblock;  

7.      clearboth;  

8.      height0;  

9.      visibilityhidden;  

10. }  

11.    

12. ul.menu {  

13.     background#ccc;  

14. }  

15. ul.menu li {  

16.     floatleft;  

17.     width100px;  

18. }  

clearfixから話を戻して。
また次のように、スタイルを上書きしたい場合も有ると思います。

Sass

view plaincopy to clipboardprint?

1.  .imgR {  

2.      floatright;  

3.      margin-left10px;  

4.      margin-bottom1.5em;  

5.  }  

6.  .photo {  

7.      @extend .imgR;  

8.      margin-left5px;  

9.  }  

この様な場合は、次のようにちゃんと分けて変換してくれます。

変換されたCSS

view plaincopy to clipboardprint?

1.  .imgR, .photo {  

2.      floatright;  

3.      margin-left10px;  

4.      margin-bottom1.5em;  

5.  }  

6.  .photo {  

7.      margin-left5px;  

8.  }  

部分的に、スタイルを上書きしたい場合も問題ないですね。
こんな感じで便利な @extend ですが、現状だと単独のセレクタしか使うことしか出来ません。
よく分からない説明ですが、次の場合はエラーになっちゃいます。

Sass(エラー)

view plaincopy to clipboardprint?

1.  .item .imgR {  

2.      floatright;  

3.      margin-left10px;  

4.      margin-bottom1.5em;  

5.  }  

6.  .photo {  

7.      @extend .item .imgR;  

8.  }  

子孫セレクタとかセレクタを組み合わせるとダメって事ですね。
これは、将来的に対応されるのかな。

@extend は使い方に寄ってはミックスインでも何ら問題が無いのですが、大きな違いとして、ミックスインはインクルードする度に展開され、@extend はグループ化されて変換されます。
なので、例えばclearfixをミックスインで定義して、複数ヶ所でインクルードするとその数だけ宣言が増えていきますが、@extend だとグループ化されるのでより合理的なCSSになります。