X(Twitter)の埋め込みポスト(ツイート)をダークテーマに自動対応させる

サイトやブログをダークモードに対応させるために、メディア特性の prefers-color-scheme を利用している方は多いのではないでしょうか。

かくいう筆者も同様の方法で当ブログにダークテーマを導入しましたが、記事に X(Twitter)のポスト(ツイート)を埋め込んだ際、端末をダークテーマに切り替えてもポストはライトテーマのままなのが気になっていました。

ブログのテーマと連動してポストのテーマも切り替えたい! そう思い試行錯誤した結果、ダークテーマに自動対応させる方法を見つけました。

アイキャッチ

X の埋め込みポストをダークテーマに変更する

公式で説明されている、埋め込みポストのダークテーマへの変更方法は2つあります。

方法1:meta タグを追加

以下の meta タグを head 内に追加します。

<meta name="twitter:widgets:theme" content="dark"/>

こちらの方法ではページ上のポストのテーマを一括で変更できます。

方法2:data-theme="dark" を追加

埋め込みコードの blockquote タグに data-theme="dark" を追加します。

<blockquote data-theme="dark" class="twitter-tweet"> ... </blockquote>

こちらの方法では埋め込みタグ毎に属性を追加しなければなりませんが、JavaScript を使えば一括処理ができますし、ダークテーマ対応のついでにポストの表示位置や横幅も変更できます。

よって今回は、こちらの方法を使ってライト/ダークテーマの自動対応を行っていきます。

X の埋め込みポストをダークテーマに自動対応させる

スクリプトの設置方法

まず、サイトやブログ上の埋め込みポストの以下のコードを全て削除します。

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

この作業をちゃんと行わないと、このあと提示するスクリプトが動かずとても苦労するはめになるので気を付けてください。筆者も気を付けます……(この記事公開したときもやらかした)。

次に、以下のコードを body 内の、埋め込みポストの blockquote よりも下(できれば </body> の直前)に追加します。

<script>//<![CDATA[
// ダークモード判定
const darkmode = window.matchMedia('(prefers-color-scheme: dark)');

function loadWidgets(){
  const embed = document.querySelectorAll('blockquote.twitter-tweet');
  if(embed.length === 0){
    // ページ上にポストがない場合はここで終了
    return
  }
  for(let i = 0; i < embed.length; i++){
    if(darkmode.matches){
      // 端末がダークモードのときポストをダークモードにする
      embed[i].setAttribute('data-theme', 'dark');
      } else {
      // 端末がライトモードのときポストをライトモードにする
      embed[i].setAttribute('data-theme', 'light');
    }
    // ポストの横幅を 480px で固定
    embed[i].setAttribute('data-width', '480');
    // ポストをセンタリングする
    embed[i].setAttribute('data-align', 'center');
  }
  // ポストを読み込む
  const script = document.createElement('script');
  script.src = '//platform.twitter.com/widgets.js';
  document.body.appendChild(script);
}
  
function changeTheme(){
  const iframe = document.querySelectorAll('div.twitter-tweet-rendered iframe');
  if(iframe.length === 0){
    // ページ上にポストがない場合はここで終了
    return
  }
  for(let i = 0; i < iframe.length; i++){
    if(darkmode.matches){
      // 端末がダークモードのときポストをダークテーマにする
      iframe[i].src = iframe[i].src.replace('&theme=light&', '&theme=dark&')
    } else {
      // 端末がライトモードのときポストをライトテーマにする
      iframe[i].src = iframe[i].src.replace('&theme=dark&', '&theme=light&');
    }
  }
}

// DOM が読み込まれたときに発火
window.addEventListener('DOMContentLoaded', loadWidgets);

// 端末のテーマが切替わったときに発火
darkmode.addEventListener('change', changeTheme);
//]]></script>

ここまでの作業がうまくいっていれば、端末のテーマを切り替えるたびにポストのテーマも連動して変更されます。

スクリプトの解説

端末のダークモードの判定は window.matchMedia('(prefers-color-scheme: dark)').matches で行うことができます。

まず関数 loadWidgets で、端末がダークモードのときは埋め込みポストの blockquote data-theme="dark" を、ライトモードのときは data-theme="light" を追加します。

テーマの自動対応のついでに、下に示すコードでポストの横幅と表示位置の指定も同時に行っています。この部分は適宜変更したり削除したりしてください。

// ポストの横幅を480pxで固定
embed[i].setAttribute('data-width', '480');
// ポストをセンタリングする
embed[i].setAttribute('data-align', 'center');

属性の追加が完了したあとにウィジェットのスクリプトを読み込むことで、ポストが読み込まれた際のダークテーマへの自動対応は完了します。

ただしこの方法だと、埋め込みポストが完全に読み込まれた場合は端末のテーマを変えてもポスト自体のテーマは変わりません。

そこで、ポストが完全に読み込まれたあとの埋め込みコードに着目してみると、ダークテーマのときには iframesrc 属性の URL に theme=dark というパラメータが見つかりました。

<iframe [...] src="https://platform.twitter.com/embed/Tweet.html?dnt=false&[...]&theme=dark&[...]" [...]> ... </iframe>

デベロッパーツールで theme=darktheme=light に書き換えると、ポストのテーマが変わります。

これを利用して、端末のテーマが変更されるたびに関数 changeTheme が発火されるようにし、ダークモードのときは src のパラメータを theme=darkに、ライトモードのときは theme=light に書き換えることでページの再読み込みをしなくてもポストのテーマを変更可能にしました。

端末のテーマを変更すると、下のポストの iframe の再読み込みが起きていることがわかるはずです。

あとがき

X(Twitter)埋め込みポスト(ツイート)のテーマを端末のテーマに連動させるスクリプトを作りました。

筆者自身はすでに X から撤退しているため、今後 X のポストをブログに貼り付る機会は早々来ない気がしますが、Twitter が突然 X に変わったときのように突然気が変わってポストを貼りまくるかもしれないので、対応できてよかったです。

編集
ホーム