第10回MLCマークアップ部お題のスクープ型ボックスをCSSマスクで再現した話

UI開発者 古川

先日社内で開催された第10回MLCマークアップ部の総評の中で、「お題のボックス部分はどのように再現するか」という問いかけがありました。

筆者は当初SVGでマスクを適用して再現する想定だったのですが、CSSのみではどのように再現するのだろうかと思い実際に作成してみることにしました。

再現の方針策定

マークアップ部のお題から今回再現したい部分を抽出したキャプチャを用意しました。

デザインを観察してみると、背景がてっくらうんじちゃんのシルエットのパターンになっているため単に疑似要素で背景色と同色の丸を四隅に作成し被せるという方法は採用できません。

CSS Backgrounds and Borders Module Level 4の中のCorner Shapingがまさしく今回の要件に該当するのですが、仕様がEditor's Draftの段階で2019年1月現在ではどのブラウザにも実装されていない状況です。(2013年のものになりますがcorner-shapeの挙動を再現したサンプルが公開されています

そこで今回はCSSでマスクをかける方法を採用し、corner-shape:scoopの他にnotchbevelもあわせて以下の手順で再現します。

  1. ブロックチェック柄のボックスを作成する
  2. かけたいマスクの形をCSSグラデーションで再現する
  3. CSSでマスクを適用する

実際にやってみた

今回のHTMLは以下です:

<div class="box-scoop"></div>

1. ブロックチェック柄のボックスを作成する

CSSグラデーションを利用してブロックチェック柄を再現します。

上のキャプチャのように2つの柄をrepeating-linear-gradientで作成し、重ね合わせてブロックチェック柄になっているように見せます。 background-positionを少し調整してパターン柄である雰囲気を出してみましょう。以下に該当のソースコードを抜粋します。

.box-scoop {
  background-color: #fff;
  background-image: 
    repeating-linear-gradient(45deg, #faf7ee 0%, #faf7ee 25%, transparent 25%, transparent 75%, #faf7ee 75%, #faf7ee 100%), 
    repeating-linear-gradient(45deg, #faf7ee 0%, #faf7ee 25%, transparent 25%, transparent 75%, #faf7ee 75%, #faf7ee 100%);
  background-position: -12px -12px, 13px 13px;
  background-size: 50px 50px;
}

2. かけたいマスクの形をCSSグラデーションで再現する

先ほどのボックスにマスクをかける前に、スクープ型のCSSを背景画像で再現してみましょう。ボックスの領域に対し、四隅それぞれradial-gradientでキャプチャのような背景画像を再現します。円形の大きさはスクープの半径に該当します。

該当のソースコードを以下に抜粋します。

<div class="box-sample"></div>
.box-sample {
  background-image:
    radial-gradient(circle at 0% 0%, transparent 15px, #333 0),
    radial-gradient(circle at 100% 0%, transparent 15px, #333 0),
    radial-gradient(circle at 0% 100%, transparent 15px, #333 0),
    radial-gradient(circle at 100% 100%, transparent 15px, #333 0);
  background-position: 0% 0%, 100% 0%, 0% 100%, 100% 100%;
  background-repeat: no-repeat;
  background-size: 55% 55%;
}

background-size55%としているのは、50%に設定するとボックスのサイズによっては以下のキャプチャのように隙間が生まれてしまう可能性があるためです。

3. CSSでマスクを適用する

先ほど作成した背景のCSSをマスクに転用しましょう。該当コードを以下に抜粋します。

.box-scoop {
  mask-image: 
    radial-gradient(circle at 0% 0%, transparent 15px, #333 0),
    radial-gradient(circle at 100% 0%, transparent 15px, #333 0),
    radial-gradient(circle at 0% 100%, transparent 15px, #333 0),
    radial-gradient(circle at 100% 100%, transparent 15px, #333 0);
  mask-position: 0% 0%, 100% 0%, 0% 100%, 100% 100%;
  mask-size: 55% 55%;
  mask-repeat: no-repeat;
}

掲載は省略しますが、CSSマスクをGoogle ChromeやSafariで表示するには-webkit-のプレフィックスが必要です。

ノッチ型やベベル型も同じ要領で作成

ノッチ型やベベル型も同じ要領で作成します。マスク部分のCSSのみ以下に抜粋します。

.box-notch {
  mask-image: 
    linear-gradient(0deg, transparent 15px, #333 15px, #333 calc(100% - 15px), transparent calc(100% - 15px)), 
    linear-gradient(90deg, transparent 15px, #333 15px, #333 calc(100% - 15px), transparent calc(100% - 15px));
  mask-position: 0 0, 0 0;
  mask-size: 100% 100%;
  mask-repeat: no-repeat;
}
.box-bevel {
  mask-image: 
    linear-gradient(45deg, transparent 15px, #333 15px), 
    linear-gradient(135deg, transparent 15px, #333 15px), 
    linear-gradient(225deg, transparent 15px, #333 15px),
    linear-gradient(315deg, transparent 15px, #333 15px);
  mask-position: 0 100%, 0 0, 100% 0, 100% 100%;
  mask-size: 55% 55%;
  mask-repeat: no-repeat;
}

実際に完成した様子が以下のキャプチャです。半径や透明にする範囲の値を調整すれば、印象の異なるボックスが作成できます。

ボックスにシャドウをつけてみる

ボックスにはシャドウをつけたくなる場合があるかもしれません。先ほどの.box-scoopに直接box-shadowを適用すると、マスクがかかっているのでシャドウごと切り取られてしまいます。また、box-shadowは通常ボックスの境界線の外側に描画されるので四隅のスクープの形に沿ってシャドウがかかりません。

これらを解決するにはCSS filterのdrop-shadowを利用します。ポイントは先ほど作成したボックスとは別のdivにフィルターを適用することです。

HTMLマークアップを変更し、シャドウをかけるdivとスクープ型のスタイルを付与するdivを分けます。

<div class="box-scoop">
<div class="inner"></div>
</div>
.box-scoop {
  filter: drop-shadow(10px 10px 3px rgba(65, 65, 65, 0.5));
}

.box-scoop > .inner {
/* スクープ型ボックスのスタイル */
}

これで完成です。

まとめ

作成したボックスをGoogle Chrome最新版とFirefox最新版を比べたところ、Google Chromeの方はスクープにジャギーが少し目立ちました。

CSSマスクは基本的にIE11では利用できず、ブラウザによってはジャギーが目立ってしまうこともあるため、場合によってはSVGマスクなど別の手法を選択するのがよいかもしれません。