【CSS】スケルトンスクリーンの実装方法

HTML/CSS

スケルトンスクリーン(スケルトンローダー)とはコンテンツの枠組のみを表現したデザインのことを指します。
スケルトンスクリーンは画面のローディング中などにUX向上の目的で利用されます。

今回はCSSによるスケルトンスクリーンの実装方法と解説をします。

スケルトンスクリーンの実装手順

  1. コンテンツの枠(スケルトンスクリーンのデザイン)を作成する
  2. スケルトンスクリーンの要素に対して擬似要素を追加する
  3. スケルトンスクリーンのpositionをrelative, 擬似要素をabsoluteにする
  4. 擬似要素に対してアニメーションを追加する

CSSによるスケルトンスクリーンのサンプルコード

<div class='item'>
  <div class='item__image'></div>
  <div>
    <div class='item__detail'></div>
    <div class='item__detail'></div>
    <div class='item__detail'></div>
  </div>
</div>

@keyframes loading {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

.item {
  border: 1px solid #ddd;
  border-radius: 5px;
  height: 85px;
  width: 225px;
  display: flex;
  align-items: center;
  border-top: 1px solid #ddd;
  padding: 10px;
}

.item__image {
  width: 64px;
  height: 64px;
  margin-right: 10px;
  background: #f4f4f5;
  overflow: hidden;
  position: relative;
}

.item__image::before {
  display: block;
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.50), transparent);
  animation: loading 1.0s linear infinite;
}

.item__detail {
  height: 15px;
  width: 125px;
  background: #f4f4f5;
  overflow: hidden;
  position: relative;
}

.item__detail:not(:last-child) {
  margin-bottom: 5px;
}

.item__detail::before {
  display: block;
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
  animation: loading 1.0s linear infinite;
}

See the Pen
Untitled
by Toshiharu Nishina (@nishina555)
on CodePen.

以下では上記のCSSについて解説します。

擬似要素の背景について

スケルトンスクリーンのCSSに記載されているbackground: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);の意味について解説します。

擬似要素(before/after)の詳細解説は擬似要素before/afterの概要と具体例で紹介しています。

linear-gradientについて

linear-gradient()とはグラデーションを生成するCSS関数です。第1引数にグラデーションの向き、第2引数以降にグラデーションの配色を指定します。

以下はlinear-gradientの例です。

<div class='pattern1'></div>
<hr>
<div class='pattern2'></div>
<hr>
<div class='pattern3'></div>
<hr>
<div class='pattern4'></div>

.pattern1 {
  width: 200px;
  height: 100px;

  /* グラデーション。配色は青、赤の順 */
  background: linear-gradient(0deg, blue, red);
}

.pattern2 {
  width: 200px;
  height: 100px;

  /* 90度傾いたグラデーション。配色は青、赤、黄の順 */
  background: linear-gradient(90deg, blue, red, yellow);
}

.pattern3 {
  width: 200px;
  height: 100px;

  /* 45度傾いたグラデーション。配色は青、赤、黄の順 */
  background: linear-gradient(45deg, blue, red, yellow);
}

.pattern4 {
  width: 200px;
  height: 100px;

  /* 右下から左上に向かうグラデーション。配色は青、赤の順 */
  background: linear-gradient(to left top, blue, red);;
}

See the Pen
Untitled
by Toshiharu Nishina (@nishina555)
on CodePen.

白色半透過の擬似要素を重ねた時の見た目について

rgba()は赤、緑、青の成分で色を表現するCSS関数です。オプションで色の透明度(opacity)を指定できます。

スケルトンスクリーンで利用されているrgba(255, 255, 255, 0.5);は半透過の白色を表現しています。
白色半透過の擬似要素を重ね合わせると、要素の色は薄くなります。

<div class='item'></div>

.item {
  width: 100px;
  height: 100px;
  background: red;
  position: relative;
}

.item::before {
  content: '';
  display: block;
  height: 50%;
  width: 100%;
  background: rgba(255, 255, 255, 0.5);
  position: absolute;
}

See the Pen
background-white-half-opacity
by Toshiharu Nishina (@nishina555)
on CodePen.

transparentな擬似要素を重ねた時の見た目について

transparentは要素を透明にするプロパティですので、transparentな擬似要素を重ね合わせても要素の色はかわりません。

<div class='item'></div>

.item {
  width: 100px;
  height: 100px;
  background: red;
  position: relative;
}

.item::before {
  content: '';
  display: block;
  height: 50%;
  width: 100%;
  background: transparent;
  position: absolute;
}

See the Pen
background-transparent
by Toshiharu Nishina (@nishina555)
on CodePen.

transparentと白色半透過のグラデーションを重ねた時の見た目について

linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);は「左から、透明、白色半透過、透明というグラデーション」を意味します。
上記のグラデーションを要素に重ね合わせると、要素の真ん中の色だけ薄くなります。

<div class='item'></div>

.item {
  width: 100px;
  height: 100px;
  background: red;
  position: relative;
}

.item::before {
  content: '';
  display: block;
  height: 50%;
  width: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
  position: absolute;
}

See the Pen
Untitled
by Toshiharu Nishina (@nishina555)
on CodePen.

擬似要素のアニメーションについて

スケルトンスクリーンのCSSに記載されている以下の意味について解説します。

@keyframes loading {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

.item__detail::before {
  animation: loading 1.0s linear infinite;
}

transformについて

transformは要素を移動するCSSプロパティです。translateXでX軸(横軸)に関する要素の移動距離を指定できます。

<div class='item_1'></div>
<hr>
<div class='item_2'></div>
<hr>
<div class='item_3'></div>

.item_1 {
  width: 100px;
  height: 100px;
  background: red;
  position: relative;
  margin-left: 150px;
}

.item_1::before {
  content: '';
  display: block;
  height: 50%;
  width: 100%;
  background: blue;
  position: absolute;
  transform: translateX(-100%);
}

.item_2 {
  width: 100px;
  height: 100px;
  background: red;
  position: relative;
  margin-left: 150px;
}

.item_2::before {
  content: '';
  display: block;
  height: 50%;
  width: 100%;
  background: blue;
  position: absolute;
  transform: translateX(50%);
}

.item_3 {
  width: 100px;
  height: 100px;
  background: red;
  position: relative;
  margin-left: 150px;
}

.item_3::before {
  content: '';
  display: block;
  height: 50%;
  width: 100%;
  background: blue;
  position: absolute;
  transform: translateX(100%);
}

See the Pen
pseudo-transform-patterns
by Toshiharu Nishina (@nishina555)
on CodePen.

@keyframesを利用したanimationについて

animationとはスタイルのアニメーションを定義するプロパティです。
@keyframesとはアニメーションの各キーフレームにおけるCSSを定義するCSSアットルールです。@keyframesを利用することでアニメーションの詳細な定義、制御ができます。

スケルトンスクリーンに登場したanimation: loading 1.0s linear infinite;は「loadingというキーフレーム名の動きを、1秒1周期(1s)、一定のスピード(liner)、無限に繰り返す(infinite)」というアニメーションを意味します。

スケルトンスクリーンで利用されていたキーフレーム(始点translateX(-100%)、終点translateX(100%))を適用すると以下のようになります。

<div class='item'></div>

@keyframes loading {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

.item {
  width: 100px;
  height: 100px;
  background: red;
  position: relative;
  margin-left: 150px;
}

.item::before {
  content: '';
  display: block;
  height: 100%;
  width: 100%;
  background: blue;
  position: absolute;
  animation: loading 1.0s linear infinite;
}

See the Pen
Untitled
by Toshiharu Nishina (@nishina555)
on CodePen.

さらに要素からはみ出した部分をoverflow: hiddenで無視すると以下のようになります。

<div class='item'></div>

@keyframes loading {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

.item {
  width: 100px;
  height: 100px;
  background: red;
  position: relative;
  margin-left: 150px;
  overflow: hidden;
}

.item::before {
  content: '';
  display: block;
  height: 100%;
  width: 100%;
  background: blue;
  position: absolute;
  animation: loading 1.0s linear infinite;
}

See the Pen
animation-keyframes-overflow-hidden
by Toshiharu Nishina (@nishina555)
on CodePen.

スケルトンスクリーンのコードをあらためて確認

今回紹介したスケルトンスクリーンのサンプルコードは「透明・白色半透過・透明というグラデーションの擬似要素を左右に移動させ、要素の一部の色のみを薄くする」ということを行っています。その結果、ローディング中のようなデザインとなります。

冒頭で紹介したサンプルコードを再掲します。

<div class='item'>
  <div class='item__image'></div>
  <div>
    <div class='item__detail'></div>
    <div class='item__detail'></div>
    <div class='item__detail'></div>
  </div>
</div>

@keyframes loading {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

.item {
  border: 1px solid #ddd;
  border-radius: 5px;
  height: 85px;
  width: 225px;
  display: flex;
  align-items: center;
  border-top: 1px solid #ddd;
  padding: 10px;
}

.item__image {
  width: 64px;
  height: 64px;
  margin-right: 10px;
  background: #f4f4f5;
  overflow: hidden;
  position: relative;
}

.item__image::before {
  display: block;
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.50), transparent);
  animation: loading 1.0s linear infinite;
}

.item__detail {
  height: 15px;
  width: 125px;
  background: #f4f4f5;
  overflow: hidden;
  position: relative;
}

.item__detail:not(:last-child) {
  margin-bottom: 5px;
}

.item__detail::before {
  display: block;
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
  animation: loading 1.0s linear infinite;
}

See the Pen
Untitled
by Toshiharu Nishina (@nishina555)
on CodePen.

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考資料