紡Book の勉強ノートに戻る
技術ブログ2026.07.30

「何を書いても色が変わらない」の犯人は優先順位ではなかった — 止まったタブと CSS トランジション

CSSデバッグブラウザ紡Book

見た目を検証するときに使っている非表示のブラウザペインで、測定値そのものが嘘になっていた、という記録です。

トップページの一番上、最初に目に入る大きな見せ場(ヒーロー)を、真っ暗な画面に問いが立ち上がって、そこからスクロールで写真が出て紙色に明ける、という作りに入れ替えた。

作り終えて実物を測ったら、上に重なっているナビゲーションの文字が墨色のままだった。ロゴが #302E2A、横のメニュー項目が #696560。背景は暗幕(#15130f)なので、コントラスト比はロゴ 1.37、メニュー項目 3.21。文字と背景の明暗差を 1〜21 で表す指標で、本文サイズの文字なら 4.5 以上が目安とされます。どちらも読めない。

暗幕でいる間だけナビを明るい側へ寄せる規則を入れてみた。ヒーロー側が <html> タグに目印(data-lp-hero="dark" という属性)を付けて、CSS がそれを見る形にした。

css
/* ヒーローが暗幕でいる間だけ立つ属性を見て、ナビを暗がりの側へ寄せる */
html[data-lp-hero='dark'] nav a,      /* html に data-lp-hero="dark" が付いている間だけ当たる */
html[data-lp-hero='dark'] nav button,
html[data-lp-hero='dark'] nav span {
    color: rgba(239, 228, 210, 0.8);  /* 紙色。末尾の 0.8 は不透明度 */
}

効かない。ただしロゴだけ効いている

測ると、ロゴの「綴Photo」は rgba(239, 228, 210, 0.8) に変わっていた。ところが横のメニュー項目(できること / Web版 / コンセプト)は rgb(105, 101, 96) のまま。どちらも nav の中の <a> で、同じ規則にかかるはずだった。

メニュー項目は Tailwind の text-lp-ink-light を持っている。ロゴは text-lp-ink。ここが違うので、ユーティリティとの優先順位で負けているのだろうと考えた。

!important を足した。

変わらない。

セレクタは当たっている

優先順位の話にする前に、そもそも規則が届いているかを確かめた。

js
const sel = "html[data-lp-hero='dark'] nav a";
document.querySelectorAll(sel).length;  // このセレクタに当たる要素の数を数える → 5
link.matches(sel);                      // この link 自身が条件に合うか → true

当たっている。次に、CSS ではなく要素に直接書いたらどうなるかを試した。

js
// CSS ファイルを経由せず、要素そのものに直接スタイルを書き込む(=最強の札)
link.setAttribute('style', 'color: rgb(4,5,6) !important');
link.getAttribute('style');             // ちゃんと書き込めているか確認 → "color: rgb(4,5,6) !important"
getComputedStyle(link).color;           // その要素が実際に使っている色を読む → "rgb(105, 101, 96)"

インラインの !important が効かない。ここで優先順位の説明は成り立たなくなった。これはサイトを書く側が出せる最強の札で、これに勝てる指定をこちらから書くことはできません。効かないなら、順位で負けているのではなく、そもそも別の何かが色を決めている。

手掛かりは最初の観察に戻ってあった。効いていたのはロゴだけで、ロゴのクラスは font-serif font-bold text-base text-lp-ink tracking-wider no-underline。メニュー項目は ... text-lp-ink-light hover:text-lp-ink transition-colors ...。

違いは text-lp-ink-light ではなく transition-colors のほうだった。

クラスは DOM の要素。規則は CSS ファイル。どちらも DB には無く、保存しない。ロゴの a綴Phototext-lp-inktransition-colors なし同じ規則が当たるメニューの aできること / Web版 / コンセプトtext-lp-ink-lighttransition-colors あり同じ規則が当たるhtml[data-lp-hero='dark'] nav a(CSS ファイル)

止まったタイムラインの上では、トランジションは 0% で固まる

このペインは document.visibilityState === 'hidden'(=画面に映っていない状態)で動いています。見えていないページを描き続けても意味がないので、ブラウザは描画に紐づく処理を止めます。前の作業で requestAnimationFrame — ブラウザが画面を描き替えるたびに呼んでくれる関数で、普通は1秒に60回くらい回ります — が1度も発火しないことは確認済みだった(500ms 待っても呼ばれた回数 0)。アニメーションやトランジションが「いま何秒経ったか」を数えるための時計(ドキュメントのタイムライン)も、同じ理由で進みません。

属性が立った瞬間に色の変化が起きて、transition-colors がトランジションを開始します。開始はします。ただしタイムラインが進みませんので、進行度は 0% のまま止まります。

現在の色はブラウザのメモリ。行き先は CSS ファイルと DOM の style 属性。DB には無く、保存しない。CSS ファイル行き先 紙色rgba(239, 228, 210, 0.8)優先順位が決めるのはここDOM の style 属性color: rgb(4,5,6) !importantインラインの最強札これも行き先だけ行き先を渡すメモリ: トランジション進行度 0% で固着 開始色 rgb(105, 101, 96)getComputedStyle はここを読むタイムライン停止。保存しない

測る前にトランジションを切る

測定手順を変えた。読む前に一時的なスタイルを差し込む。

js
// このペインは document timeline が止まっている=started した transition が 0% で固着する。
// transition を切って「最終値」を測る。
const probe = document.createElement('style');  // <style> タグを作る
probe.textContent = '*,*::before,*::after{transition:none !important}';  // 全要素(と ::before/::after の飾り)のトランジションを一律オフ
document.head.appendChild(probe);               // head に差し込んで有効化
 
// ここで getComputedStyle を読む
 
probe.remove();

差した状態で測り直したら、ナビの項目は全部 rgba(239, 228, 210, 0.8) になった。0.8 の透過なので暗幕と混ざって実効色は #C3BAAB、コントラスト比 9.66。紙色に明ければ元の #696560 に戻り、こちらは 5.35。どちらの局面でも読める。

そして !important は要らなかったので外した。効かないと思っていたのは測り方のほうで、規則は最初から正しく効いていた。危ないところで、本番の CSS に不要な !important を残すことになっていた。

学び

  • 詳細度や !important が決めていたのはトランジションの行き先で、進行中の現在値はそれとは別に決まっていた。優先順位を足す方向で粘っていた時間が丸ごと無駄だった
  • インラインの !important が効かなかった時点で、優先順位の話は終わっていた。作者の !important より上に置かれている宣言はトランジションだけ(CSS アニメーションは !important に負ける側)。次に同じ壁に当たったら、トランジション・止まったタイムライン・そもそも別の要素を見ていないか、を順に見ます
  • 見た目を測る前に * { transition: none !important } を差す、を手順に入れた。requestAnimationFrame が発火しないのと、トランジションが 0% で固着するのは同じ原因なので、このペインで色を読む限りは毎回必要になります(CSS アニメーションはこれでは切れないので、そこは別の手が要ります)
  • 犯人を text-lp-ink-light だと思ったのは、症状が「色」だったので同じ語彙を名乗っているクラスから疑ったからだった。真犯人の transition-colors は同じ行に並んでいたのに、色の話ではない飾りとして読み飛ばしていた
  • そしてこの間ずっと、疑う先はコードの中だけだった。壊れていたのはコードではなく測っている場所のほうで、規則は最初から効いていた

この記事は2026年7月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。