😎
jQueryとcssで作るダークモードテーマ対応
今回ご紹介する内容
今回はWebサイトなどでダークモードに対応する方法と動的に配色を切り替えに対応する方法をご紹介いたします。
やりたいこと
- OSのダークモードにあわせて配色を暗くしたい
- OSの設定に関係なくライトモード・ダークモードを切り替えたい
- ユーザが配色の設定をできるようにしたい
サンプル・ソース
全体のソースはGitHubに公開しています。
動作イメージは以下の通りです。
戦略
- テーマ自体はライトモード、ダークモードをcssで用意
- 切り替えは
html
タグの属性にtheme
をjQueryで付け替えして行う - 環境の変更をイベントハンドラで監視、変更があるたびに
theme
を切り替える - ユーザによる色の変更は
body
タグのstyle
で上書再定義
全体構成をつくる
スタイルシートとして3つ用意します。
style.css
全体の構造用のスタイルシート
theme.css
テーマの色を定義するスタイルシート
ripple.css
クリック時のアニメーション用のスタイルシート
index.html
... 略 ...
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="theme.css" />
<link rel="stylesheet" href="ripple.css" />
... 略 ...
メイン領域にはクリックできる擬似ボタンを3つ用意。これでテーマを切り替える。
またプライマリ処理を変更できるようにカラーピッカーも追加。
index.html
<header>
<span class="title">
CSSテーマサンプル
</span>
</header>
<main>
<div class="theme-selector">
<div class="theme-auto ripple">
<span>自動/環境に合わせる</span>
</div>
<div class="theme-light ripple">
<span>ライト</span>
</div>
<div class="theme-dark ripple">
<span>ダーク</span>
</div>
<div class="theme-color-pallet">
<label for="primary-color">プライマリ色</label>
<input id="primary-color" type="color" title="色"/>
</div>
</div>
</main>
CSS変数で色を定義
今回テーマによって色が変わるところをcss変数を利用して設定します
変数 | 説明 |
---|---|
--header-background | ヘッダーの背景色 |
--header-color | ヘッダーの文字色 |
--main-background | メイン領域の背景色 |
--main-color | メイン領域の文字色 |
--p-color | プライマリーカラー |
--p-highlight | プライマリーカラーのハイライト |
--p-text | プライマリーの文字色 |
大分割愛していますが大まかには以下のようにCSS変数で色を指定しています
style.css
header {
background-color: var(--header-background);
color: var(--header-color);
}
main {
background-color: var(--main-background);
color: var(--main-color);
}
div.ripple {
background: var(--p-color);
color: var(--p-text);
}
div.ripple:hover,
div.ripple:active {
background: var(--p-highlight);
}
CSS変数の色をテーマ別に設定
テーマカラーのCSS変数は :root
擬似クラスを利用して設定します
theme.css
...略...
/* デフォルトのテーマ。ライトモード時の色 */
:root,
:root[theme="light"] {
--header-background: #4682b4;
--header-color: #f5f5f5;
--main-background: #f5f5f5;
--main-color: #222222;
--p-color: #4682b4;
--p-highlight: #86afd0;
--p-text: #f5f5f5;
}
/* ダークモード時の色 */
:root[theme="dark"] {
--header-background: #444444;
--header-color: #dddddd;
--main-background: #333333;
--main-color: #dddddd;
--p-color: #274863;
--p-highlight: #437dad;
--p-text: #f5f5f5;
}
JavaScriptでテーマを定義する
変数 themMode
を用意その値で現在のテーマを表す。モードはAuto
, Dark
, Light
を用意。
index.js
const ThemeMode = {
Auto: null, // 自動
Dark: 'dark', // ダークモード
Light: 'light' // ライトモード
}
let themMode = ThemeMode.Auto // 初期設定は自動
ブラウザがダークモードかを確認
window.matchMedia
を利用してダークモードかを確認する
また変更があった時のイベントハンドラも登録
main.js
// メディアクエリーリスト。ダークモードが設定されているかを確認。
const mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)')
// 環境の変更イベントを監視
mediaQueryList.addEventListener('change', updateTheme)
// テーマモードによってテーマを切り替える
function updateTheme () {
const theme = themMode ?? getBrowserTheme()
const htmlElement = document.body.parentElement
htmlElement.setAttribute('theme', theme)
... 略 ...
}
// メディアクエリーリストの状態に沿ったテーマを取得する
function getBrowserTheme () {
return mediaQueryList.matches ? 'dark' : 'light'
}
updateTheme() // 初期設定
ボタンのイベントハンドラを登録
擬似ボタンをクリックされたときのハンドラを登録
ボタンに合わせてテーマ設定を行う
main.js
$('.theme-auto').on('click', () => {
themMode = ThemeMode.Auto // テーマ設定を自動にする
updateTheme() // テーマを更新
})
$('.theme-light').on('click', () => {
themMode = ThemeMode.Light // テーマ設定をライトモードで固定
updateTheme() // テーマを更新
})
$('.theme-dark').on('click', () => {
themMode = ThemeMode.Dark // テーマ設定をダークモードで固定
updateTheme() // テーマを更新
})
カラーピッカーのイベントハンドラを登録
計算済みのスタイルを取得し現在のプライマリ色を取得
カラーピッカーの色を取得
色が異なればbody
タグのstyle
属性でCSS変数を追加し色を設定する
main.js
$('#primary-color').on('change', () => {
// 計算済みのスタイルを取得
const body = document.body
const computedStyle = getComputedStyle(body)
// プライマリ色を取得
const themeRgb = computedStyle.getPropertyValue('--p-color').trim()
// カラーピッカーの色を取得
const pickerRgb = $('#primary-color').val()
if (themeRgb !== pickerRgb) {
// 色が異なる場合は r, g, b を取得
const r = Number.parseInt(pickerRgb.substring(1, 3), 16)
const g = Number.parseInt(pickerRgb.substring(3, 5), 16)
const b = Number.parseInt(pickerRgb.substring(5, 7), 16)
// hsl方式へ変換
const { h, s, l } = rgba2hsla({r, g, b})
// プライマリ色を設定
body.style.setProperty('--p-color', pickerRgb)
// プライマリ色のハイライトはhslのlを20%増しで設定
body.style.setProperty('--p-highlight', `hsl(${h}, ${s}%, ${Math.min(l + 20, 100)}%)`)
} else {
// ユーザが設定した色設定を破棄
clearCustomColorPallet()
}
})
最後に
以上、ダークモード対応のご紹介でした。カラーピッカーを利用すればユーザが好きに色を変更できるようになります。
RSSリーダのレビューでUIが弱いとのご指摘受けてるのでこれを気に対応したいと考えています。
Discussion