box-shadow を使って複数の border を付ける方法

CSS を使って 1 つの要素に複数の border を付ける上手い方法 [^1] はないものかと調べていたところ 
[CSSで複数のボーダーを付ける方法 | Ri-mode Rainbow | No:1134](http://ri-mode.com/rainbow/2013/05/08/css_multiple_borders/)
という素晴らしい記事を見つけたので実際に試してみました。

[^1]: 複数の div を入れ子にして各 div に border を設定する方法は、メンテナンスの観点から論外とします

こんな感じの枠が作れます。(HTML 内の記述は div 1 つだけです)

![border03.png](https://qiita-image-store.s3.amazonaws.com/0/24393/d6c29daf-fecc-b291-1287-8be4b3bfc8e6.png)

このテクニックは CSS3 で導入された `box-shadow` プロパティを使って擬似的に border を実現するというものです。box-shadow には複数の値を指定できるという仕様を応用します。

確認したブラウザは Google Chrome (42.0), Firefox (35.0), Opera (27.0), IE 9 ～ IE 11 です。

## box-shadow で枠を付ける方法

以下のように `box-shadow : 0 0 0 <幅> <色>;` というフォーマットで実現することができます。

```css:sample
div {
    box-shadow : 0 0 0 10px blue;
}
```

box-shadow 自体の詳しい説明は割愛します。この設定がどういう意味を持っているのか、なぜこれで枠が実現できるのかといった詳しい理由については [冒頭に挙げたブログ記事](http://ri-mode.com/rainbow/2013/05/08/css_multiple_borders/) を参照してください。

通常の方法と box-shadow を使った方法を比較してみます。

### 通常の border

```html:sample01.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <title>sample01</title>
        <style>
            body {
                margin : 0;
            }
            #box {
                border : 10px solid blue; /* 通常の border */
                width : 100px;
                height : 50px;
                margin : 50px;
                padding : 25px;
                text-align : center;
            }
        </style>
    </head>
    <body>
        <div id="box">TEST</div>
    </body>
</html>
```

(出力結果)
![border01.png](https://qiita-image-store.s3.amazonaws.com/0/24393/96d5294b-01ed-9a31-e3a1-c07d0078cf48.png)

### box-shadow を使った border

```html:sample02.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <title>sample02</title>
        <style>
            body {
                margin : 0;
            }
            #box {
                box-shadow : 0 0 0 10px blue; /* 擬似的な border */
                width : 100px;
                height : 50px;
                margin : 50px;
                padding : 25px;
                text-align : center;
            }
        </style>
    </head>
    <body>
        <div id="box">TEST</div>
    </body>
</html>
```

(出力結果)
![border02.png](https://qiita-image-store.s3.amazonaws.com/0/24393/bf5e04ec-4568-4f91-f4ba-37204a138dd6.png)

ご覧の通り box-shadow を使って div 要素に全く同じ見た目の青い枠を付けることが出来ました！

### 注意事項 (サイズ・幅の算出方法の違いについて)

box-shadow で枠を表現する際に注意すべきポイントがあります。この方法はエフェクトを使って擬似的に border を表現しているだけなので、要素のサイズや余白などの計算方法が通常の border と異なります。 
さきほど挙げた 2 つのスクリーンショットを見比べると、左・上方向の余白のサイズが微妙に違うことが分かるでしょうか？

分かりやすいサンプルとして、以下のようにボックスを 2 つ上下に並べた場合にどのように表示されているか確認してみましょう。
まず、通常の border を使った場合

```html:sample03.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <title>sample03</title>
        <style>
            body {
                margin : 0;
            }
            div {
                border : 25px solid #cccccc; /* 通常の border */
                background-color : #ffffcc;
                height : 50px;
                width : 100px;
                margin : 50px;
            }
        </style>
    </head>
    <body>
        <div>Box 01</div>
        <div>Box 02</div>
    </body>
</html>
```

この HTML は以下のようにレンダリングされます。

![size01.png](https://qiita-image-store.s3.amazonaws.com/0/24393/ed17bf22-a71a-8abe-fe5b-cc992ab434ad.png)

黄色いボックスの周りに 25px の border が付き、さらにその周囲に 50px の余白が出来ます。
通常 padding や border は要素のサイズに含まれないため [^2] グレーの枠の端から端までが margin として計算されていることが分かります。

一方、box-shadow を擬似的な border として利用した場合

```html:sample04.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <title>sample04</title>
        <style>
            body {
                margin : 0;
            }
            div {
                box-shadow : 0 0 0 25px #cccccc; /* 擬似的な border */
                background-color : #ffffcc;
                height : 50px;
                width : 100px;
                margin : 50px;
            }
        </style>
    </head>
    <body>
        <div>Box 01</div>
        <div>Box 02</div>
    </body>
</html>
```

このページは以下のようにレンダリングされます。

![size02.png](https://qiita-image-store.s3.amazonaws.com/0/24393/0d0ce7c8-d3d7-f6ed-7b4e-5f930e410feb.png)

余白の設定は上と同じ 50px ですが、こちらは上下の黄色のボックスが接しているように見えます。
box-shadow によって付けられた枠が無視されて、黄色いボックスの端から端までが margin として計算されていることが分かります。

このように、通常の border と同じ感覚で扱ってしまうと意図しないズレが発生することがあるので注意しましょう。

[^2]: 例外として、ある要素の `box-sizing` プロパティに `border-box` が指定されている場合はその要素の padding と border までを含めた長さがその要素のサイズとして計算されます。IE の互換モード (Quirks Mode) におけるボックスサイズの計算方法も同様です。

## 複数の枠を付けるサンプル

それでは実際に複数の枠を付けてみます。上で挙げた box-shadow の書式 `0 0 0 <幅> <色>` を枠の内側から順にコンマで繋げて書くだけで OK です。
例えば、内側から順に青・緑・オレンジ・赤の枠を 10px の幅で付けたい場合は以下のように表現します。幅のサイズには **要素の端からの距離** を指定してください。

```css:sample
div {
    box-shadow :
        0 0 0 10px blue,
        0 0 0 20px green,
        0 0 0 30px orange,
        0 0 0 40px red;
}
```

このスタイルを利用して簡単な HTML を作ってみます。

```html:sample05.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <title>sample05</title>
        <style>
            body {
                margin : 0;
            }
            #box {
                box-shadow :
                    0 0 0 10px blue,
                    0 0 0 20px green,
                    0 0 0 30px orange,
                    0 0 0 40px red;
                width : 100px;
                height : 20px;
                line-height : 20px;
                margin : 50px;
                padding : 20px;
                text-align : center;
            }
        </style>
    </head>
    <body>
        <div id="box">TEST</div>
    </body>
</html>
```

(出力結果)
![border03.png](https://qiita-image-store.s3.amazonaws.com/0/24393/d6c29daf-fecc-b291-1287-8be4b3bfc8e6.png)

こんな感じで、狙い通りに複数の枠を付けることに成功しました！

この手法では従来の border と違って点線や二重線などの特殊な枠は作れませんが、敢えて第 3 パラメータ（ぼかし距離）を指定することでふわっとした枠を作るなど、この方法でしか出来ない様々な応用が考えられます。
このテクニックを使ってどんな実践的デザインが出来るか、次回ご紹介したいと思います。

## 注釈