CSSにおけるシャドウ効果の影

## 対象読者
- 「このシャドウはCSSで再現可能かな？」とお悩みのデザイナーの方
- 「このシャドウどうやってCSSで再現しよう、、」とお悩みのフロントエンドの方

### 注意
- Sass芸(Sassの[@for](https://sass-lang.com/documentation/at-rules/control/for)等を利用して、複雑な形を再現したりする行為)ではなく、無理なく使える範囲を狙います
- IEには疲れたのでこの記事中では存在しないことにします
- 作例は[Pug](https://pugjs.org/api/getting-started.html) + [Stylus](http://stylus-lang.com/)で作成しています

## htmlの要素にシャドウをつけたいという欲求
に対する一番シンプルな答えは、CSSの[box-shadow](https://developer.mozilla.org/ja/docs/Web/CSS/box-shadow)プロパティですね。
これを利用することで、`要素のフレームの周囲にシャドウ効果を追加`できます。

ただ、これはCSSの中でも扱いが難しいプロパティかと思います。
苦い顔をしてしまう方も多いのではないでしょうか？

## box-shadowの憂鬱

というのも、[Adobe XD](https://helpx.adobe.com/jp/xd/how-to/what-is-xd.html)など、デザインツールは矩形でなくとも様々な効果を設定することができるためです。

一方のCSSは[ボックスモデル](https://www.w3.org/TR/CSS21/box.html)に基づき、矩形の領域をベースにスタイリングを行いますから、デザインの再現に無理が発生してきます。

その際、box-shadowは、同じくボックスモデルの周囲を装飾する[border](https://developer.mozilla.org/ja/docs/Web/CSS/border)と比べ、左右上下での調整が効きにくく、綺麗に表現するには工夫が必要な場面が出てきがちです。

CSSにおけるシャドウ効果は一筋縄ではいかない、、、そう、`影`があるということです。
※個人の感想です

この記事では、そんなシャドウ効果の一端をご紹介します。

## Lv.1 矩形の要素
`box-shadow`を設定するのみです。

<p class="codepen" data-height="250" data-theme-id="default" data-default-tab="css,result" data-user="haradabox" data-slug-hash="XWJpERR" style="height: 250px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Lv1.box-shadow">
  <span>See the Pen <a href="https://codepen.io/haradabox/pen/XWJpERR">
  Lv1.box-shadow</a> by haradabox (<a href="https://codepen.io/haradabox">@haradabox</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

問題ないですね。

## Lv.2 凸形の要素（タブ風UI）
タブ選択で表示するコンテンツの切り替えができるUIを考えます。
選択しているタブと、コンテンツの周囲をシャドウで囲いたい、、、という欲求です。

ここからややこしくなってきます。

まず下記をご覧ください。

### 無邪気に
<p class="codepen" data-height="400" data-theme-id="default" data-default-tab="css,result" data-user="haradabox" data-slug-hash="bGNgKqK" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Lv2.box-shadow--faild">
  <span>See the Pen <a href="https://codepen.io/haradabox/pen/bGNgKqK">
  Lv2.box-shadow--faild</a> by haradabox (<a href="https://codepen.io/haradabox">@haradabox</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

タブ要素、コンテンツ要素共に周囲にシャドウを付けていますが、タブ要素にコンテンツ要素のシャドウが被ってしまっています。

これではいけませんね。

### 少し考えて
擬似要素とz-indexを利用し、被っているシャドウを消しました。

```stylus
.box
  position: relative;
  z-index: 0;
  &__tab-item    
    &.--current
      position: relative;
      box-shadow: 0 1px 3px 0 #333;
      background-color: #fff;
      // コンテンツのシャドウを隠す
      &::after {
        position: absolute;
        bottom: 0;
        left: 0;
        height: 3px;
        width: 100%;
        content: "";
        background-color: #fff;
        z-index: 3;
      }
  &__body
    position: relative;
    z-index: 2;
    background-color: #fff;
    box-shadow: 0 1px 3px 0 #333;
```
<p class="codepen" data-height="400" data-theme-id="default" data-default-tab="css,result" data-user="haradabox" data-slug-hash="XWJpEaR" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Lv2.box-shadow">
  <span>See the Pen <a href="https://codepen.io/haradabox/pen/XWJpEaR">
  Lv2.box-shadow</a> by haradabox (<a href="https://codepen.io/haradabox">@haradabox</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

よろしそうですね。

## Lv.3 凹形の要素（チケット風UI）
お次は凹形の要素です。
チケットのような、切り欠きのある形を再現します。

### 惜しい
<p class="codepen" data-height="320" data-theme-id="default" data-default-tab="css,result" data-user="haradabox" data-slug-hash="YzPZPvP" style="height: 320px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Lv3.box-shadow--failed">
  <span>See the Pen <a href="https://codepen.io/haradabox/pen/YzPZPvP">
  Lv3.box-shadow--failed</a> by haradabox (<a href="https://codepen.io/haradabox">@haradabox</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

丸い切り欠きの表現に、before擬似要素 + borderを使いました。
それっぽくはありますが、もうちょいいけそうですね？

### それなりに
before擬似要素ではinsetのbox-shadowで円形のシャドウを作成します。
そして、背景色のbox-shadowを加えた、after擬似要素で、不要な箇所をマスクします。

<p class="codepen" data-height="320" data-theme-id="default" data-default-tab="css,result" data-user="haradabox" data-slug-hash="JjoEvXw" style="height: 320px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Lv3.box-shadow">
  <span>See the Pen <a href="https://codepen.io/haradabox/pen/JjoEvXw">
  Lv3.box-shadow</a> by haradabox (<a href="https://codepen.io/haradabox">@haradabox</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

よりそれっぽくなりました。

## Lv.4 ギザギザ形の要素（アイコンと一体に）
Sass芸縛りプレイなので、rotateとskewを駆使して、、、という案は無視します。
となれば[SVG](https://developer.mozilla.org/ja/docs/Web/SVG)なんですが、、、

### どうやってシャドウをつければいいのだっけ？
drop-shadowはボックスモデルに対して影がつくので、、、

<p class="codepen" data-height="320" data-theme-id="default" data-default-tab="html,result" data-user="haradabox" data-slug-hash="abzWGpq" style="height: 320px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Lv4.box-shadow--svg-fail">
  <span>See the Pen <a href="https://codepen.io/haradabox/pen/abzWGpq">
  Lv4.box-shadow--svg-fail</a> by haradabox (<a href="https://codepen.io/haradabox">@haradabox</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

まあこうなりますよね。

### filterを使う
SVGには強い味方、[filter: drop-shadow](https://developer.mozilla.org/ja/docs/Web/CSS/filter-function/drop-shadow)があります。

表示したいSVGを前面に、シャドウ用のSVGを背面に配置し、シャドウで囲みたい部分（コンテンツなど）を挟み込みます。
また、filter: drop-shadowのプロパティはbox-shadowとは異なるため、数値は微調整します。

```stylus
.box
  position: relative;
  z-index: 0; // シャドウが親要素の後ろに入り込まないように付与
  &__inner
    position: relative;
    border-radius: 8px;
    padding: 50px;
    background: #fff;
    box-shadow: 0 1px 3px 0 #333;
    z-index: auto; // 指定なしでもOKですが、回り込ませる対象としてわかりやすいかなーということで付与
  &__icon
    width: 100px;
    position: absolute;
    top: 0;
    left: 0;
    transform: translate(-30px, -30px);
    &.--shadow
      z-index: -1; // シャドウ用のSVG画像を.box__innerの後ろに回り込ませる
.svg-icon.--shadow
  filter: drop-shadow( 0 1px 1px #444); // filterでSVG画像に沿ったシャドウを付与
```

<p class="codepen" data-height="320" data-theme-id="default" data-default-tab="html,result" data-user="haradabox" data-slug-hash="wvBJBNe" style="height: 320px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Lv4.box-shadow--svg">
  <span>See the Pen <a href="https://codepen.io/haradabox/pen/wvBJBNe">
  Lv4.box-shadow--svg</a> by haradabox (<a href="https://codepen.io/haradabox">@haradabox</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

私は満足です。

## おまけ: ロングシャドウ
完璧ではないのですが、お蔵にするには勿体無いので、記載しておきます。

### それがこちらです
```stylus
.box
  position: relative;
  z-index: 0;
  &__inner
    position: relative;
    background: #fff;
    z-index: auto;
    &::before
      content: "";
      position: absolute;
      top: -1px;
      left: 0;
      width: 300px;
      height: 100%;
      transform: skew(0, 45deg);
      transform-origin: 0 0;
      background: linear-gradient(90deg, rgba(200,200,200,1) 0%, rgba(255,255,255,1) 100%);
      z-index: -1;
    &::after
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 300px;
      box-sizing: border-box;
      transform: skew(45deg);
      transform-origin: 0 0;
      background: linear-gradient(180deg, rgba(200,200,200,1) 0%, rgba(255,255,255,1) 100%);
      z-index: -1;
```
<p class="codepen" data-height="600" data-theme-id="default" data-default-tab="css,result" data-user="haradabox" data-slug-hash="VwYpvaQ" style="height: 600px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="extra.long-shadow">
  <span>See the Pen <a href="https://codepen.io/haradabox/pen/VwYpvaQ">
  extra.long-shadow</a> by haradabox (<a href="https://codepen.io/haradabox">@haradabox</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
before擬似要素/after擬似要素でシャドウを作成します。
それぞれがシャドウを付与したい要素のheight/widthに依存し、skewで歪めることで汎用性のあるシャドウにしています。

グラデーションの角度調整（グラデーションの終端が要素の形になってしまう)が課題ですね。

## まとめ
シャドウ効果はこんな感じで工夫が求められることが多いです。
しかしながら、裏を返せば工夫の発揮しどころ、実装を楽しめるポイントでもあります。

「他にもこんなシャドウ再現できまっせ」、ありましたら是非コメントをば！