レスポンシブや反転、余白の変更、垂直方向の変更ができるMediaオブジェクト

OOCSSでよく取り上げられるMediaオブジェクトに4つのModifierを指定できるように作ってみました。以下の4つのパターンを組み合わせて指定できます。もちろんネストにも対応しています。

* `padding`プロパティで3段階の余白指定
* `vertical-align`プロパティで3段階の垂直位置指定
* `direction`プロパティで要素の反転
* `display: block;`でブレイクポイント以下で横並びから縦並びに変更

CodePenにデモを用意しています。

<p data-height="266" data-theme-id="4660" data-slug-hash="QybeoN" data-default-tab="result" data-user="gaku" class='codepen'>See the Pen <a href='http://codepen.io/gaku/pen/QybeoN/'>Media object</a> by ManabuYasuda (<a href='http://codepen.io/gaku'>@gaku</a>) on <a href='http://codepen.io'>CodePen</a>.</p>
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

## マークアップ
マークアップは以下のように記述します。BEMっぽくはなっていますが、マークアップをシンプルにするために`Element`を`Block__Element--Modifier`にはしていません。`[]`はModifierであることを示しています。

```html
<div class="c-media [c-media--small c-media--middle c-media--rev c-media--stack-md]">
  <div class="c-media__item">
    <img>
  </div>
  <div class="c-media__item">
    <p></p>
  </div>
</div>
```

ネストする場合は以下のように`.c-media__item`の中に`.c-media`を入れ子にします。

```html
<div class="c-media">
  <div class="c-media__item">
    <img src="">
  </div>
  <div class="c-media__item">
    <p></p>
    <!-- ここからがネスト -->
    <div class="c-media">
      <div class="c-media__item">
        <img src="">
      </div>
      <div class="c-media__item">
        <p></p>
      </div>
    </div>
    <!-- ネストの終了 -->
  </div>
</div>
```

## Sassコード
4つのModifierを指定するのでソースコードがやや複雑になっています。Sassが分かる人はCSSよりも把握しやすいと思います。

```scss
$media-gutter: 1em !default;
$media-gutter-small: ($my-media-gutter / 2) !default;
$media-gutter-large: ($my-media-gutter * 2) !default;

.c-media {
    display: table;
    width: 100%;
    margin: 0;
    padding: 0;
}

.c-media__item {
    display: table-cell;
    margin: 0;
    padding: 0;
    vertical-align: top;
    &:not(:first-child) {
        padding-left: $my-media-gutter;
    }
    & > :first-child {
        margin-top: 0;
    }
    & > :last-child {
        margin-bottom: 0;
    }
}

.c-media__item > img {
    display: block;
}

.c-media--middle {
    > .c-media__item {
        vertical-align: middle;
    }
}

.c-media--bottom {
    > .c-media__item {
        vertical-align: bottom;
    }
}

.c-media--rev {
    text-align: left;
    direction: rtl;
    > .c-media__item {
        text-align: left;
        direction: ltr;
    }
    > .c-media__item:not(:first-child) {
        padding-right: $my-media-gutter;
        padding-left: 0;
    }
}

.c-media--small {
    > .c-media__item:not(:first-child) {
        padding-left: $my-media-gutter-small;
    }
    &.c-media--rev > .c-media__item:not(:first-child) {
        padding-right: $my-media-gutter-small;
        padding-left: 0;
    }
}

.c-media--large {
    > .c-media__item:not(:first-child) {
        padding-left: $my-media-gutter-large;
    }
    &.c-media--rev > .c-media__item:not(:first-child) {
        padding-right: $my-media-gutter-large;
        padding-left: 0;
    }
}

@media screen and (max-width: 399px) {
    .c-media--stack-sm {
        > .c-media__item {
            display: block;
        }
        > .c-media__item:not(:first-child) {
            padding: $my-media-gutter 0 0;
        }
        > .c-media__item > img {
            margin: auto;
        }
        &.c-media--small > .c-media__item:not(:first-child) {
            padding: $my-media-gutter-small 0 0;
        }
        &.c-media--large > .c-media__item:not(:first-child) {
            padding: $my-media-gutter-large 0 0;
        }
    }
}

@media screen and (max-width: 767px) {
    .c-media--stack-md {
        > .c-media__item {
            display: block;
        }
        > .c-media__item:not(:first-child) {
            padding: $my-media-gutter 0 0;
        }
        > .c-media__item > img {
            margin: auto;
        }
        &.c-media--small > .c-media__item:not(:first-child) {
            padding: $my-media-gutter-small 0 0;
        }
        &.c-media--large > .c-media__item:not(:first-child) {
            padding: $my-media-gutter-large 0 0;
        }
    }
}
```

## CSSコード
Sassコードをコンパイルすると以下のようになります。

```c
.c-media {
    display: table;
    width: 100%;
    margin: 0;
    padding: 0;
}

.c-media__item {
    display: table-cell;
    margin: 0;
    padding: 0;
    vertical-align: top;
}
.c-media__item:not(:first-child) {
    padding-left: 1em;
}
.c-media__item > :first-child {
    margin-top: 0;
}
.c-media__item > :last-child {
    margin-bottom: 0;
}

.c-media__item > img {
    display: block;
}

.c-media--middle > .c-media__item {
    vertical-align: middle;
}

.c-media--bottom > .c-media__item {
    vertical-align: bottom;
}

.c-media--rev {
    text-align: left;
    direction: rtl;
}
.c-media--rev > .c-media__item {
    text-align: left;
    direction: ltr;
}
.c-media--rev > .c-media__item:not(:first-child) {
    padding-right: 1em;
    padding-left: 0;
}

.c-media--small > .c-media__item:not(:first-child) {
    padding-left: 0.5em;
}

.c-media--small.c-media--rev > .c-media__item:not(:first-child) {
    padding-right: 0.5em;
    padding-left: 0;
}

.c-media--large > .c-media__item:not(:first-child) {
    padding-left: 2em;
}

.c-media--large.c-media--rev > .c-media__item:not(:first-child) {
    padding-right: 2em;
    padding-left: 0;
}

@media screen and (max-width: 399px) {
    .c-media--stack-sm > .c-media__item {
        display: block;
    }
    .c-media--stack-sm > .c-media__item:not(:first-child) {
        padding: 1em 0 0;
    }
    .c-media--stack-sm > .c-media__item > img {
        margin: auto;
    }
    .c-media--stack-sm.c-media--small > .c-media__item:not(:first-child) {
        padding: 0.5em 0 0;
    }
    .c-media--stack-sm.c-media--large > .c-media__item:not(:first-child) {
        padding: 2em 0 0;
    }
}

@media screen and (max-width: 767px) {
    .c-media--stack-md > .c-media__item {
        display: block;
    }
    .c-media--stack-md > .c-media__item:not(:first-child) {
        padding: 1em 0 0;
    }
    .c-media--stack-md > .c-media__item > img {
        margin: auto;
    }
    .c-media--stack-md.c-media--small > .c-media__item:not(:first-child) {
        padding: 0.5em 0 0;
    }
    .c-media--stack-md.c-media--large > .c-media__item:not(:first-child) {
        padding: 2em 0 0;
    }
}
```

## コードの解説

### `table-cell`を使用すると`vertical-align`プロパティが指定できる
Mediaオブジェクトは`float`プロパティを使ったものがよく使われていましたが、それでは垂直方向の変更ができません。`vertical-align`プロパティを指定できるのはインラインレベルとテーブルセル要素です。

 > 適用対象	インラインレベルとテーブルセル要素. It also applies to ::first-letter and ::first-line.
>
> <a href="https://developer.mozilla.org/ja/docs/Web/CSS/vertical-align" target="_blank">vertical-align - CSS | MDN</a>

今回は`top`をデフォルトにしています。

```css
.c-media {
    display: table;
    width: 100%;
    margin: 0;
    padding: 0;
}

.c-media__item {
    display: table-cell;
    margin: 0;
    padding: 0;
    vertical-align: top;
}

.c-media--middle > .c-media__item {
    vertical-align: middle;
}

.c-media--bottom > .c-media__item {
    vertical-align: bottom;
}
```

### 要素を反転させる`direction`プロパティ
`float`プロパティを使用しないと要素の反転ができないかというと、そうではありません。`direction`プロパティを使用すればテーブルでも反転することができます。

<a href="https://developer.mozilla.org/ja/docs/Web/CSS/direction" target="_blank">direction - CSS | MDN</a>

`direction`プロパティはヘブライ語やアラビア語などの右から左へ読む言語で使用されるものです。親要素に`direction: rtl;`を指定することで反転、子要素に`direction: ltr;`を指定することで元の状態に戻します。

```css
.c-media--rev {
    text-align: left;
    direction: rtl;
}
.c-media--rev > .c-media__item {
    text-align: left;
    direction: ltr;
}
```

これは`inline-block`にも使用できるので覚えておくとレイアウトの幅が広がります。

### `:not(:first-child)`で余白指定をシンプルにする
余白の指定には否定擬似クラスの`:not()`を使用しています。Mediaオブジェクトは`__image`と`__body`のようなクラスを指定することが多いですが、否定擬似クラスを使用すると、同じクラスでマークアップすることができます。

```css
.c-media__item:not(:first-child) {
    padding-left: 1em;
}

.c-media--small > .c-media__item:not(:first-child) {
    padding-left: 0.5em;
}

.c-media--large > .c-media__item:not(:first-child) {
    padding-left: 2em;
}
```

<a href="https://developer.mozilla.org/ja/docs/Web/CSS/%3Anot" target="_blank">:not() - CSS | MDN</a>

`:not()`はIE9から使えます。もしIE8でも使いたい場合は以下のように隣接セレクタを使います（詳細度も同じです）。

```css
.c-media__item + .c-media__item {}
```

<a href="https://developer.mozilla.org/ja/docs/Web/CSS/Adjacent_sibling_selectors" target="_blank">隣接セレクタ - CSS | MDN</a>

### あるブレイクポイント以下で`block`にする
画像とテキストが横並びになるMediaオブジェクトはスマホなどの横幅が狭いデバイスでは縦に並べたい場合もあると思います。メディアクエリを使用してブレイクポイント以下になったときに`display: table-cell`を`block`にすることで縦に配置します。

```css
@media screen and (max-width: 767px) {
    .c-media--stack-md > .c-media__item {
        display: block;
    }
    .c-media--stack-md > .c-media__item:not(:first-child) {
        padding: 1em 0 0;
    }
    .c-media--stack-md > .c-media__item > img {
        margin: auto;
    }
    .c-media--stack-md.c-media--small > .c-media__item:not(:first-child) {
        padding: 0.5em 0 0;
    }
    .c-media--stack-md.c-media--large > .c-media__item:not(:first-child) {
        padding: 2em 0 0;
    }
}
```

`padding`プロパティは縦方向にだけ余白を指定するように変更しています。`img`要素に`margin: auto;`を指定しているのはバランス的にセンタリングした方がいいのではということで指定しています。