DigiPress

Highly Flexible WordPress Theme

メニュー

CSS だけでサムネイルごとで開けるモーダルウィンドウを表現してみる

JavaScript を使わず、CSS のみで複数のモーダルウィンドウを開閉するアイテムの一覧のサンプルコードをつくってみました。

例えば、EC サイトで商品一覧のサムネイルをクリックすると、その商品の拡大画像と説明、価格、購入ボタンなどをまとめたモーダルウィンドウが最前面に固定表示されるようなイメージです。

アイテムをクリックすると、サムネイルが回転しながらモーダルウインドウ上の画像と同一サイズに拡大してめくれるようにモーダルウィンドウに差し替わるようなもを考えていましたが、HTMLの構造上、要素の階層と position の関係で、どうしても CSS のみではイメージするアニメーションができなかったため、最終的にはサムネイルをフレームアウトさせてモーダルウインドウをフレームインさせるという、かなりやっつけで不完全な形でのデモですが、参考までにどうぞ。

デモ

See the Pen
Toggle modal windows for detail with pure CSS
by digistate (@digistate)
on CodePen.

このサンプルコードの動作を簡単に解説します。

モーダルウィンドウの開閉の仕組み

過去記事でもいくつか紹介していますが、CSS だけでクリック動作を検知するには、input 要素のチェックボックスを利用します。
チェックボックスでは、:checked という疑似セレクタによってチェックの有無に分けて隣接する要素も含めてスタイリングできるため、チェックがない場合(初期状態)はモーダルウィンドウの要素は非表示にしておき、チェックされたとき(:checked でのスタイル)にはモーダルウィンドウを表示するようにします。

また、:checked 以外にも、:target 疑似セレクタを利用して何らかの要素の表示有無の制御も可能です。

CSS3で作る汎用的なモーダルウィンドウ

HTML の基本構造

個々のサムネイルの要素は、チェックボックスが含まれる以下のような構成にします。

<div class="panel-item">
  <!-- 表示有無を判別するためのチェックボックス(非表示) -->
  <input type="checkbox" class="panel-check" id="panel1" aria-hidden="true" />

  <!-- サムネイル -->
  <label for="panel1" class="panel-label" role="button">
    <span class="label-inner">
      <span class="thumbnail" style="background-image:url(サムネイルURL);"></span>
    </span>
  </label>

  <!-- モーダルウィンドウ(非表示) -->
  <div class="panel-modal">
    <figure>
      <img src="画像URL" alt="image" />
    </figure>
  </div>
</div>

一覧に表示する数(サムネイル)は、このパネルアイテム(.panel-item)が1つの単位となり、子要素にはチェックボックスの状態を変更可能にするための label 要素(サムネイル)と、このパネルに対応するモーダルウィンドウ用の要素(.panel-modal)が構成されています。

label 要素の子要素には div を含めることができないため、span タグで強引に次ぐ強引なタグ構成でサムネイルを background-image プロパティで表示しています。
なお、img タグも label 内に含めることができるようです。

参考

https://stackoverflow.com/questions/18609554/is-div-inside-label-block-correct

CSSの構造

サムネイル(label)のスタイリング

チェックボックスのトリガーとなるサムネイルのラッパー要素(label)の ::before 疑似セレクタには、サムネイルと同じサイズのレイヤーを一番下に重ね合わせておきます。
こうすることで、モーダルウィンドウが表示される際にアニメーションしてフレームアウトしても、ラッパー要素のサイズは保持されるため、サムネイルの一覧のレイアウトが崩れません

サイズ保持用のレイヤー

.panel-label::before{
  position:relative;
  content:'';
  display:inline-block;
  width:14vw;
  height:20vw;
  min-width:180px;
  min-height:260px;
}

さらに、ラッパー要素の ::after 疑似セレクタは、フルスクリーンサイズの不透明なブラックレイヤーにし、初期状態では非表示にしてモーダルウィンドウが表示(チェックボックスにチェック)された際にその下に表示されるようにします。
ラッパー要素(label)はチェックボックスのトリガーとなるため、この ::after 疑似セレクタのレイヤーがクリックされる(チェックボックスが外れる)と、モーダルウィンドウはフレームアウトしてサムネイルが元の位置に戻ってきます。

モーダルウィンドウを閉じるためのレイヤー

.panel-label::after{
  content:'';
  position:fixed;
  top:0;
  left:0;
  right:0;
  bottom:0;
  z-index:1;
  background-color:rgba(#000, 0.78);
  pointer-events:none;
  opacity:0;
  transition:all .8s ease;
  cursor:zoom-out;
}

モーダルウィンドウのスタイリング

モーダルウィンドウは、position:fixed にして位置を固定し、表示されるときには常にスクリーンの中央に表示されるようにします(初期状態ではフレームアウトしています)。
また、先述のモーダルウィンドウを閉じるレイヤーよりも前面に表示させるため、z-index の値は .panel-label::after よりも大きい値にします。

.panel-modal{
  position:fixed;
  top:50%;
  left:50%;
  width:90vw;
  height:90vh;
  min-width:320px;
  max-width:980px;
  max-height:860px;
  transform:translate(-50%, 100vh) rotateY(180deg);
  background-color:rgba(255,255,255,.1);
  overflow:auto;
  visibility:hidden;
  opacity:0;
  transition:all .8s ease;
  z-index:2;
}

モーダルウィンドウを表示するときのスタイリング

表示するときのスタイリングは、チェックボックスの :checked 疑似セレクタを利用し、さらにチェックボックスの次にあるサムネイルのラッパー要素(.panel-label)については「隣接兄弟結合子(+)」を利用します。

サムネイルのスタイリング

.panel-check:checked + .panel-label .label-inner{
  /* サムネイルをフレームアウトさせるCSS */
  transform:perspective(1800px) rotateY(180deg) translate(50%, calc(100vh + 100%));
  transition-delay:0s;
  width:90vw;
  height:90vh;
  min-width:320px;
  animation: changePosition 0s;
  animation-delay: .8s;
  animation-fill-mode: forwards;
  opacity:0;
}

サムネイルは、animation プロパティを指定していますが、これは positiontransform の対象にすることはできないため、animation プロパティで @keyframes によって指定時間後に position の値を fixed に変更するという、かなりの力技をやっています。

チェックボックスから2つ目にあるモーダルウィンドウ(.panel-modal)には「一般兄弟結合子(~)」を指定することで表現できます。

モーダルウィンドウのスタイリング

.panel-check:checked ~ .panel-modal{
  /* モーダルウィンドウをフレームインするCSS */
  visibility:visible;
  opacity:1;
  transition-delay:.66s;
  transform:translate(-50%, -45vh) rotateY(0); /* 中央に移動 */
}

サンプルで適当に作ってみましたが、やはり CSS のみでは色々と限界がありました。。

その他参考

https://stackoverflow.com/questions/35624241/delay-css-position-change-with-pure-css

Share / Subscribe
Facebook Likes
Tweets
Hatena Bookmarks
Pinterest
Pocket
Feedly
Send to LINE