開発情報・ナレッジ

投稿者: 株式会社ゴンドラ 2025年7月16日 (水)

暗い場所でも快適に。OS設定に連動するダークモードでUXを向上させるCSS

フォームを入力するユーザーのPC、ブラウザの環境によってライトモードとダークモードを自動で切り替える方法をご紹介します。
デモサイトをご用意しました。
デモサイト
フォームのデザインテンプレートも用意いたしましたので、ダウンロードして試してみてください。

ダークモードとは?

ダークモードが普及し、現在ではほぼ全てのブラウザがその機能をサポートしています。暗い背景に明るい文字を表示することで、夜間や室内など、光の少ない環境での利用が快適になります。
この普及の背景には、Apple (iOS/macOS) や Google (Android)、Microsoft (Windows) といった主要OSが標準機能としてダークモードをサポートしたことが大きく影響しています。
ダークモードの主なメリットは以下の通りです。
・視認性の向上(暗い環境): 光の少ない環境で、画面の眩しさを抑え、目の負担を軽減します。
・バッテリー消費の抑制: 2017年のiPhone X以降で採用が広まったOLED(有機EL)ディスプレイは、黒いピクセルが発光しないため、電力消費を抑えバッテリー持続時間の改善に繋がります。
・ユーザーエンゲージメントの向上: ユーザーの好みに合わせた表示を提供することで、より良い体験を提供し、サービスの継続利用に繋がります。

※「ダークモードは目に優しい」という意見もありますが、これには個人差があり、乱視の方など一部のユーザーにとっては白背景に黒文字の方が見やすい場合もあるため、一概には言えません。
以下にライトモードとダークモードの比較をまとめました。

観点 ライトモード ダークモード
明るい環境での視認性
  • 高い視認性。明るい背景に黒文字は、日中や明るい場所での読みやすさに優れる。
  • 視認性が低下する可能性あり。明るい環境では、黒背景に白文字が見づらくなることがある。
暗い環境での視認性
  • 目に負担がかかる可能性あり。暗い場所での白背景は眩しさを感じることがある。
  • 目に優しい。暗い環境での使用に適しており眩しさを軽減する。
バッテリー消費
  • OLEDディスプレイでは、白い背景が全てのピクセルを点灯させるため、バッテリー消費が多くなる。
  • OLEDディスプレイでは、黒いピクセルが発光しないため、バッテリーの消費を抑えられる。
アクセシビリティ
  • 一般的に可読性が高く、多くのユーザーにとって読みやすい。
  • 一部のユーザー(乱視や視力障害を持つ人)にとっては、白文字が見づらい場合がある。

このように、どちらのモードにも得意な環境と不得意な環境が存在します。
ライトモードを暗い場所で使うと眩しさで目が疲れ、逆にダークモードを明るい場所で使うと視認性が落ちてしまいます。
ユーザーに最適な環境を提供できないことは、どちらのケースでもストレスの原因となります。

ダークモード対応の現状と「自動切り替え」の重要性

現在、多くの大手企業のWebサイトやアプリでは、ダークモードは特別な機能ではなく「対応していて当たり前」の標準機能として位置付けられています。 特にユーザーとの接点が多いBtoCサービスではその傾向が顕著です。
一方で、歴史が長く規模の大きいWebサイトなど未対応のケースも少なくありません。
対応方法としては、ユーザーが手動で表示を切り替えるボタンを設置するケースもあります。 しかし、ユーザーにその都度操作を強いるのではなくOSの設定に連動して表示が自動で切り替わることこそが、最もユーザーの負担が少ない理想的な形と言えるでしょう。

本記事ではユーザーの端末やブラウザの設定によって自動的にライトモードとダークモードを切り替える設定をご紹介します。JavaScriptなどの記述は必要なく、紹介するCSSのみの追記で実装が可能です。

基本の実装

今回のデザインはフォームに読み込むだけでデザインが完成するCSSを添付しています。後ほど配色を変更する方法もお伝えします。
※テキスト系のフィールドやセレクト系、入力項目が分かれる郵便番号や電話番号など網羅しましたが、特殊なフィールドを使用することでデザイン崩れが起きる可能性があります。その際は適宜CSSを調整してください。

手順1:設定デザインをDLし既存のCSSを削除
SPIRALで設定が済んだ設定デザインをダウンロードしてstyleタグの中に記述されてある既存のCSSを削除してください。
<!DOCTYPE html>
<html id="SMP_STYLE">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title></title>

<style type="text/css">
/* ここを空にする */
</style>

</head>
手順2:CSSを適用する
下記CSSを入力、確認、完了ページのstyleタグの中に挿入してください。
どのページでも同様のファイルを使用するのでカスタムモジュールに入れて呼び出す形式でも大丈夫です。
:root {
  /* --- カラーパレット定義 --- */
  --background-color: #FDFCF9;
  --text-color: #2B2B2B;
  --primary-color: #B8860B;
  /* UI要素の色 */
  --header-background: #847e7e;
  --title-color: #FFFFFF;
  --button-color: var(--primary-color);
  --button-back-color: #847e7e;
  --button-text: #FFFFFF;
  --input-background: #FFFFFF;
  --border-color: #DCDCDC;
  --table-border: #E5E5E5;
  --error-color: #C0392B;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* --- カラーパレット定義 --- */
    --background-color: #1A1A1A;
    --text-color: #EAEAEA; 
    --primary-color: #D4AF37;
    /* UI要素の色 */
    --header-background: #111111;
    --title-color: #FFFFFF;
    --button-color: var(--primary-color);
    --button-text: #1A1A1A;
    --input-background: #252525;
    --border-color: #444444;
    --table-border: #444444;
    --error-color: #E74C3C;
  }
}
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: "Noto Sans JP","メイリオ",Meiryo,"Hiragino Kaku Gothic Pro","ヒラギノ角ゴ Pro W3","MS Pゴシック",Helvetica,Arial,Verdana,sans-serif;
  background-color: var(--background-color);
  color: var(--text-color);
  line-height: 1.6;
  margin: 0;
  padding: 20px;
}

.body_tbl {
  max-width: 800px;
  width: 100%;
  margin: 0 auto;
  background-color: var(--background-color);
}

.smp_tmpl {
  width: 100%;
  border-collapse: collapse;
  margin: 20px 0;
  border: 1px solid var(--table-border);
  border-radius: 4px;
}

/* Header message */
.header_rmesg, .header_emesg {
  background-color: var(--header-background);
  color: var(--title-color);
  padding: 10px 15px;
  margin-bottom: 20px;
  border-radius: 4px;
  font-weight: bold;
}
.header_emesg {
  background-color: var(--error-color);
}

.smp_tmpl dl {
  display: flex;
  border: none;
}
.smp_tmpl dl + dl {
  border-top: 1px solid var(--table-border);
}


.smp_tmpl dt.title {
  padding: 15px;
  width: 30%;
  min-width: 200px;
  text-align: left;
  color: var(--text-color);
  font-weight: bold;
  border-right: 1px solid var(--table-border);
}

.smp_tmpl dd.data {
  padding: 15px;
  width: 70%;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  text-align: left;
}

ul{
  list-style: none;
}
.smp_tmpl dd ul {
  padding-left: 0;
}

.smp_tmpl .input,
.smp_tmpl input[type="text"],
.smp_tmpl input[type="password"],
.smp_tmpl input[type="tel"],
.smp_tmpl input[type="email"],
.smp_tmpl select,
.smp_tmpl textarea {
  width: 100%;
  max-width: 350px;
  padding: 10px;
  border: 1px solid var(--border-color);
  border-radius: 4px;
  font-size: 1rem;
  color: var(--text-color);
  background-color: var(--input-background);
}
.smp_tmpl select {
    height: 42px;
}
.smp_tmpl textarea {
    min-height: 100px;
}
input:focus,textarea:focus,select:focus{
  outline: none;
  border-color: var(--primary-color);
  box-shadow: 0 0 0 2px rgba(212, 175, 55, 0.6);
}

input[type="radio"], input[type="checkbox"]{
  accent-color: var(--primary-color);
}

.smp_tmpl .zipcode ul,
.smp_tmpl .phone ul {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  max-width: 350px;
}

.smp_tmpl .zipcode ul li,
.smp_tmpl .phone ul li {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 8px;
}

.smp_tmpl .zipcode ul li:last-of-type,
.smp_tmpl .phone ul li:last-of-type {
    gap: 0;
}

.smp_tmpl .zipcode input,
.smp_tmpl .phone input {
  width: 100%;
  flex: 1;
}

dd.multi2 > ul.cf{
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  align-items: center;
}
dd.multi2 ul.cf li label {
    display: flex;
    align-items: center;
    cursor: pointer;
}
dd.multi2 ul.cf li input {
    margin-right: 0.5em;
    width: auto; 
    height: auto;
    flex-shrink: 0;
}

.submit {
  display: block;
  width: 100%;
  max-width: 300px;
  margin: 40px auto 20px auto;
  background-color: var(--button-color);
  color: var(--button-text);
  padding: 12px 30px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 18px;
  font-weight: bold;
  border: 2px solid transparent;
  transition: all 0.2s ease-in-out;
}

.submit:hover {
  background-color: var(--button-text);
  color: var(--button-color);
  border-color: var(--button-color);
  opacity: 1;
}

input[name="SMPFORM_BACK"] {
  display: block;
  width: 100%;
  max-width: 300px;
  margin: 40px auto 20px auto;
  background-color: var(--button-back-color);
  color: var(--button-text);
  padding: 12px 30px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 18px;
  font-weight: bold;
  border: 2px solid transparent;
  transition: all 0.2s ease-in-out;
}
input[name="SMPFORM_BACK"]:hover {
  background-color: var(--button-text);
  color: var(--button-back-color);
  border-color: var(--button-back-color);
  opacity: 1;
}

input[type="file"] {
  padding: 10px;
  border: 1px dashed var(--border-color);
  border-radius: 4px;
}

.msg {
  color: var(--error-color);
  font-size: 0.875rem;
  margin-top: 8px;
  display: block;
}
.smp_tmpl .required:after {
  content: " *";
  color: var(--error-color);
  margin-left: 4px;
}

@media screen and (max-width: 767px) {
  body {
    padding: 0;
  }
  .body_tbl {
    padding: 10px;
  }
  .smp_tmpl dl {
    flex-direction: column;
    border-right: 0;
    border-left: 0;
  }
  .smp_tmpl dt.title {
    width: 100%;
    min-width: initial;
    border-right: 0;
    border-bottom: 1px solid var(--table-border);
  }
  .smp_tmpl dd.data {
    width: 100%;
    padding: 15px 10px;
  }
  .smp_tmpl input[type="text"],
  .smp_tmpl input[type="password"],
  .smp_tmpl input[type="tel"],
  .smp_tmpl input[type="email"],
  .smp_tmpl select,
  .smp_tmpl textarea {
      max-width: none;
  }
  .smp_tmpl .zipcode ul,
  .smp_tmpl .phone ul {
    max-width: none;
  }

  dd.multi2 > ul.cf {
    flex-direction: column;
    align-items: flex-start; 
    gap: 0.8rem;
  }
}

これにて実装は完了です。ご自身のSPIRAL環境に貼り付けて見てください。

ブラウザで動作を確認する

SPIRALへ貼り付けたらOS、ブラウザで選択した一方のモードで表示されます。
もう一方のモードも見たい場合はOS、ブラウザの設定を変えて確認することも出来ますがいちいち切り替えるのは面倒ですよね。
そんな時はchromeのDevToolsでクリック一つで切り替えることができます。
フォームでマウスを右クリックし、「検証」を押下します。
Element(要素)→Styles(スタイル)の中にToggle common rendering emulations(一般的なレンダリング エミュレーションを切り替える)というペンキを塗る刷毛のようなマークがあります。(赤枠部分)こちらを切り替えることによってライトモード、ダークモードの出しわけを検証することができます。

仕組み

CSS全体でも300行ほどの短いコードですが、ユーザー環境を自動で判別して色を出し分けしているのは以下の部分です。

@media (prefers-color-scheme: dark) {
  :root {
    /* --- カラーパレット定義 --- */
    --background-color: #1A1A1A;
    --text-color: #EAEAEA; 
    --primary-color: #D4AF37;

    /* UI要素の色 */
    --header-background: #111111;
    --title-color: #FFFFFF;
    --button-color: var(--primary-color);
    --button-text: #1A1A1A;
    --input-background: #252525;
    --border-color: #444444;
    --table-border: #444444;
    --error-color: #E74C3C;
  }
}

prefers-color-scheme
はユーザーがシステムに要求したカラーテーマが明色か暗色かを検出することができるメディア特性で、デフォルトで明るい配色を使用し
prefers-color-scheme: dark
と宣言しその中でダークモードで適用したいスタイルを記述することでユーザーが明色を希望した場合はデフォルトのスタイルが適用され、暗色を希望した場合は
prefers-color-scheme: dark
の中で宣言したスタイルを適用することができます。

また今回はCSSカスタムプロパティ(--background-colorなど)の記述をHTML全体に適用できる:root内(デフォルト)と

@media(prefers-color-scheme: dark){:root}
の中で共通した変数名で宣言することで各タグやクラスへのスタイル適用はなるべくライトモードとダークモードで2回記述する必要がないようにしています。

色を変更する

配色の確認をしていると各部の色を変更したくなることがあると思います。先述の通り今回添付したCSSファイルはCSSカスタムプロパティという共通するスタイルをあらかじめ定義し、文書全体で再利用可能にしているので一部を変更するだけで同じスタイルを適用しているすべての箇所が一括で変更できる便利仕様となっています。
以下に何をどこで使用しているのかまとめました。参考の上好きな配色にしてみてください。

※とはいえライトモードとダークモードを用意する意義が薄れてしまうのでデフォルトで背景を暗めにする、ダークモード時に明るい色を多用することは避けていただくと良いかと思います。

CSSカスタムプロパティ 使用箇所
--background-color
  • 背景色
  • フォームテーブルの背景色
--primary-color
  • 各入力欄をクリックした時のボーダーの色
  • ダークモード時のボタン、チェックボックスの色
--header-background
  • 最上部メッセージの背景色
--title-color
  • 各入力項目のタイトルの文字色
  • 最上部メッセージの文字色
--button-text
  • ボタンの文字色
  • ボタンホバー時の背景色
--input-background
  • 各入力欄の背景色
--border-color
  • 各入力欄のボーダーカラー
--table-border
  • フォームテーブル全体の枠色
--error-color
  • 最上部エラーメッセージの背景色
  • 各入力項目のエラーメッセージの文字色
解決しない場合はこちら コンテンツに関しての
要望はこちら