SWELLにダークモードを追加しようと挑戦したが諦めた

こういう感じのダークモードを作ってみた。パッと見は悪くないが、私のやり方が泥臭過ぎて、汎用性も保守性も無いので、諦めた。

動作するページはこちら

目次

環境

  • WordPress: 6.9
  • SWELL: 2.16
  • PHP: 8.2.28

やったこと

  • JavaScriptでCookieにダークモード判定用のフラグを保存
  • ダークモード用のCSSを作成
  • ダークモードにするためのクラスをbodyあたりに付与する処理を作成(JS, PHP)

追加CSSにスタイルを追記

カスタマイズ>追加CSSにて、下記のCSSを追記。

my-custom-darkmode というクラスに対して、背景色や文字色のCSS変数を上書きするスタイルを追記。
※パンくず領域の背景色はCSS変数じゃなかったので背景色を指定。

ダークモード切替の入力用のトグルスイッチのスタイルもここに記載。

/* ダークモード用の変数上書き */
.my-custom-darkmode,
body:has(.my-custom-darkmode) {
  /* 背景色 */
  --color_bg: #353535;
  --color_header_bg: #353535;
  --color_footer_bg: #353535;
  /* 文字色 */
  --color_text: #ffffff;
  --color_header_text: #ffffff;
  --color_footer_text: #ffffff;
}
/* パンくず */
.my-custom-darkmode .-body-solid .p-breadcrumb.-bg-on {
  box-shadow: inset 0 -1px 8px rgba(255, 255, 255, .06);
}
.my-custom-darkmode .p-breadcrumb.-bg-on {
  background: #454545;
}
/* SPサイドメニュー */
.my-custom-darkmode .p-spMenu__inner::before {
  background: #454545;
}
.my-custom-darkmode .p-spMenu a {
  color: var(--color_text);
}
/* トグルスイッチ */
.my-custom-toggle {
  position: relative;
  display: inline-block;
  width: 60px;
  height: 32px;
}
.my-custom-toggle-input {
  opacity: 0;
  width: 0;
  height: 0;
}
.my-custom-toggle-input:checked + .my-custom-toggle-slider {
  background: var(--color_main, #86F096);
}
.my-custom-toggle-input:checked + .my-custom-toggle-slider:before {
  transform: translateX(26px);
}
.my-custom-toggle-input:focus-visible + .my-custom-toggle-slider {
  outline: 2px solid;
}
.my-custom-toggle-slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #ccc;
  transition: 0.4s;
  border-radius: 32px;
}
.my-custom-toggle-slider::before {
  border-radius: 50%;
  position: absolute;
  content: "";
  height: 26px;
  width: 26px;
  left: 3px;
  bottom: 3px;
  background: #fff;
  transition: 0.3s;
  box-shadow: 0 0 1px rgba(0, 0, 0, 0.5);
}
.my-custom-df {
  display: flex;
}
.my-custom-aic {
  align-items: center;
}
.my-custom-gap4px {
  gap: 4px;
}
.my-custom-gap8px {
  gap: 8px;
}

他にも、ダークモードのためにスタイルを上書きすべき個所がいくらでもある。

SWELLの自分が使いたいパーツにだけスタイルを上書きするならできなくはないが、SWELLのアップデートが来た時に破綻するのが目に見えている。

CSSの上書きという一点で、諦めた。

CSSの注意点

CSSにて body:has(.my-custom-darkmode) という具合に has という疑似クラス関数を使用しているため、一部のブラウザでは意図した通りにダークモードにはならない問題がある。

Can I use で確認したところ、主要なブラウザでは has 疑似クラス関数サポートされているため、大半は問題ないはず。

ダークモード切替用のブログパーツを作成

ブログパーツにて、カスタムHTMLの入力欄を使ってダークモード切替用のトグルスイッチのhtmlとダークモード切替用のJavaScriptを記載。

ダークモードの切り替えを行う入力欄としてチェックボックスを用意し、チェックボックスの見た目をトグルスイッチのように装飾しているコード。

トグルスイッチの ON, OFF の切り替え時に Cookie にダークモード判定用の値(0ならダークモードOFF,1ならダークモードON)を保存している。

Cookieに値を保存する際の形式は、今後、他の値も追加する可能性を考慮してオブジェクト形式にしてある。

<div class="my-custom-df my-custom-aic my-custom-gap8px">
<span>ダークモード</span>
<label class="my-custom-toggle">
<input class="my-custom-toggle-input js-darkmode-switch" type="checkbox" role="switch">
<span class="my-custom-toggle-slider"></span>
</label>
</div>

<script>
  const darkmodeClassName = 'my-custom-darkmode'; // ダークモード用クラス名
  const myCustomCookieName = 'my_custom_display_settings'; // ダークモード用クッキー名
  
  jQuery(function(){
    // ========================================
    // 初回実行
    // ========================================
    // ダークモード用クラスの有無でダークモードのスイッチのオンオフを切り替え
    // NOTE: PHP側でbodyにダークモード用クラスを付与している想定
    // NOTE: swell は body タグじゃなくて直下の div#body_wrap に付与されるっぽい?
    if (jQuery('#body_wrap').hasClass(darkmodeClassName)) {
      jQuery('.js-darkmode-switch').prop('checked', true);
    } else {
      jQuery('.js-darkmode-switch').prop('checked', false);
    }

    // ========================================
    // イベント登録
    // ========================================
    // ダークモードラジオボタンの選択時
    jQuery('.js-darkmode-switch').on('change', function(){
      let val = 0;
      if (jQuery('.js-darkmode-switch').prop('checked')){
        val = 1;
      }
      const darkmode_settings = {
        darkmode_flag: val
      };
      // セッションストレージの値更新
      setDarkmodeSettings(darkmode_settings);
      // ダークモード用クラスの付け替え
      if (val) {
        addDarkmodeClassName();
      } else {
        removeDarkmodeClassName();
      }
    });
  });

  // ========================================
  // 関数
  // ========================================
  // セッションストレージにダークモード設定データを保存
  function setDarkmodeSettings(obj){
    const jsonObj = JSON.stringify(obj);
    const days = 365; // 有効期限日数
    let date = new Date();
    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
    const expires = "; expires=" + date.toUTCString();
    // エンコードしてCookieを書き込み(パスをNovel配下のページで有効にする)
    document.cookie = myCustomCookieName + "=" + encodeURIComponent(jsonObj) + expires + "; path=/Novel";
  }
  // ダークモード用のクラスを付与
  function addDarkmodeClassName(){
    jQuery('#body_wrap').addClass(darkmodeClassName);
  }
  // ダークモード用のクラスを削除
  function removeDarkmodeClassName(){
    jQuery('#body_wrap').removeClass(darkmodeClassName);
  }

</script>

ブログパーツを投稿ページなどに設置すると下記画像のような見た目になる。

functions.phpにダークモード判定処理を追加

外観>テーマファイルエディタ>SWELL_SHILD>functions.php にて、下記のように追記。

bodyタグにダークモード用のクラスを付与するために body_class を使っているが、SWELLの仕様なのか今一分かっていないが、body タグ配下の div#body_wrap にダークモード用のクラスが付与される。

そのため、前述の CSS や JavaScript は div#body_wrap にダークモード判定用のクラスが付与される前提で作ってある。

/**
 * ========== My Custom ==========
 * Cookie内のJSONオブジェクトを確認し、bodyタグにクラスを付与する
 * ========== My Custom ==========
 */
add_filter('body_class', function($classes) {
  $cookie_name = 'my_custom_display_settings';

  if (isset($_COOKIE[$cookie_name])) {
    // Cookieの値を取得し、念のためスラッシュを解除してからデコード
    $json_data = stripslashes($_COOKIE[$cookie_name]);
    $data = json_decode($json_data, true);

    if ($data) {
      if (isset($data['darkmode_flag'])) {
        // darkmode_flagが1の場合にクラスを追加
        if ($data['darkmode_flag'] === 1) {
          $classes[] = 'my-custom-darkmode';
        }
      }
    }
  }

  return $classes;
});
よかったらシェアしてね!
目次