【ITニュース解説】Three Hex Color Bugs That Survive Code Review
2026年10月29日に「Dev.to」が公開したITニュース「Three Hex Color Bugs That Survive Code Review」について初心者にもわかりやすく解説しています。
ITニュース概要
Hexカラーコードは数値のセットであり、プラットフォームによって解釈が異なり、見落とされがちなバグの原因となる。主な問題は、8桁Hexのアルファ値の順序違い、透過色を単色Hexに変換する際の背景依存、カラースペース情報の欠如の3点だ。正しい色表示のため、これらの違いを理解し、RGBAなどの形式での指定を検討しよう。
ITニュース解説
システム開発において、デザイナーから受け取った画面の色を正確に再現することは非常に重要である。しかし、色が正しく表示されないという問題は頻繁に発生し、その原因がコードレビューでは見つけにくいような、意外な場所にあることがある。特に、色を表現するためによく使われる16進数カラーコード(通称Hex Code)には、いくつかの隠れた落とし穴が存在する。これらの問題は、数値としては正しくても、その解釈の仕方や文脈によって結果が大きく異なってしまうために生じる。システムエンジニアを目指す初心者がこれらの問題に直面した際に、混乱しないように、主要な三つの落とし穴とその対策を理解しておくことは、今後の開発において非常に役立つだろう。
まず一つ目の問題は、8桁の16進数カラーコードにおける「アルファチャンネルの順序の違い」である。色の情報には、赤(Red)、緑(Green)、青(Blue)の三原色だけでなく、透明度(Alpha)を示すアルファチャンネルが含まれることがある。通常の16進数カラーコードは#RRGGBBのように6桁で表現され、これは赤、緑、青の各色に2桁ずつ割り当てられる。しかし、透明度を加えた8桁の表現では、このアルファチャンネルの記述順序がプラットフォームによって異なる場合がある。
例えば、Webサイトのスタイルを定義するCSSでは、8桁の16進数カラーコードは#RRGGBBAAという順序で定義される。これは、赤(RR)、緑(GG)、青(BB)の後に、透明度(AA)が続くことを意味する。しかし、Androidアプリケーション開発やFlutter、Kotlinといった環境では、同じ8桁の16進数カラーコードを#AARRGGBBという順序で定義することが一般的だ。ここでは、透明度(AA)が最初に記述される。
この順序の違いが、どのような問題を引き起こすか具体的な例で考えてみよう。「#80FF0000」という16進数カラーコードをデザイナーが開発者に渡したとする。
CSSの解釈では、これは赤が0x80、緑が0xFF、青が0x00、そして透明度が0x00となる。透明度(アルファ値)が0x00であるということは、色が完全に透明であり、結果としてそのUI要素は画面上から完全に消えてしまうことになる。
一方、AndroidやFlutterの解釈では、これは透明度が0x80、赤が0xFF、緑が0x00、青が0x00となる。透明度0x80は約50%の不透明度を意味するため、結果として約50%の透明度を持つ赤いオーバーレイとして表示される。
同じ「#80FF0000」という文字列にもかかわらず、CSSでは「完全に透明な何か」となり、Androidでは「半透明の赤いオーバーレイ」となる。どちらもシステム上のエラーは発生せず、コードレビューでも数値自体は正しいと判断されるため、意図しない表示のままリリースされてしまう可能性がある。
このような問題を避けるためには、6桁の16進数カラーコード(#RRGGBB)であれば、赤、緑、青の順序は共通であるため安全性が高い。透明度が必要な場合は、rgba(255, 0, 0, 0.5)のように明示的に透明度を指定する形式を使用するか、6桁の16進数カラーコードと透明度を別々の情報として渡す方法が推奨される。
二つ目の問題は、「アルファ値(透明度)の事前ブレンド」に関するものである。デザインでは、半透明の要素が特定の背景色の上に重ねて表示されることがある。この半透明の要素を、デザイナーがその時点の背景色と合成(ブレンド)して、不透明な16進数カラーコードに変換して開発者に渡すケースがある。これは「パレットに合うように」といった理由で行われることが多い。
しかし、この事前ブレンドは大きな問題を引き起こす可能性がある。半透明の要素は、背景色によって最終的な見え方が変わるため、特定の背景色の上でブレンドされた不透明な色は、その特定の背景の上でしか意図した通りに見えないからだ。
例えば、50%の透明度を持つ黒(rgba(0, 0, 0, 0.5))という半透明要素があるとする。
もしこれが白(#FFFFFF)の背景の上に表示されると、ブレンドされた結果は灰色(#808080)になる。
しかし、同じ50%の透明度を持つ黒が、濃い青(#3366CC)の背景の上に表示されると、ブレンドされた結果は暗い紺色(#1A3366)になる。
つまり、元々同じ「50%透明な黒」であったはずのものが、背景によって「中くらいの灰色」になったり、「暗い紺色」になったりする。もし開発者が「#808080」や「#1A3366」という不透明な16進数カラーコードだけを受け取ってUIを実装した場合、その要素が異なる背景の上に配置されると、デザイナーが意図した半透明の効果とは全く異なる色に見えてしまう。
この問題の解決策は、アルファ値をアルファ値として常に保持し、開発時に適切な背景の上で合成されるようにすることである。もし何らかの理由で不透明な16進数カラーコードが必要な場合は、その色がどの背景とブレンドされたものなのかを明確に記録し、情報として共有する必要がある。
三つ目の問題は、「16進数カラーコードが色空間情報を持たない」ことである。色空間とは、ある色がどのような色の範囲(ガマット)で表現されるか、そしてその色がどのように解釈されるかを定義するものである。一般的なコンピューターディスプレイやWebでは「sRGB」という色空間が広く使われているが、近年ではスマートフォンや一部のプロフェッショナル向けディスプレイで「Display P3」のような、より広い色域を表現できる色空間が普及している。
「#FF0000」という同じ16進数カラーコードがあったとしても、これがsRGBの色空間を持つ一般的なノートパソコンの画面で表示される赤と、Display P3の色空間を持つスマートフォンの画面で表示される赤は、物理的には異なる色として表現される可能性がある。Display P3はsRGBよりも表現できる色の範囲が広いため、より鮮やかな赤を表示できる可能性がある。
デザイナーが広色域ディスプレイで作業し、その画面に表示されている鮮やかな色をスクリーンショットとして取り、そこから16進数カラーコードをサンプリングして開発者に渡すというシナリオを考えてみよう。このとき、デザイナーの意図する色はDisplay P3の色空間で表現された非常に鮮やかな色かもしれない。しかし、その色をサンプリングして得られた16進数カラーコードは、色空間の情報を含まないため、開発者の一般的なsRGBディスプレイや、sRGBを前提としたアプリケーションで表示されると、デザイナーが意図したよりもくすんで見えたり、逆に不自然に鮮やかに見えたりすることがある。これは、P3の色空間の座標で定義された色が、sRGBの色空間で表示されるように誤って解釈されているような状態と言える。
この問題に対応するため、CSSではcolor(display-p3 …)のような、色空間を明示的に指定できる新しい表記方法が登場している。これは、従来の8ビットの16進数カラーコードでは、広色域ディスプレイが表現できるすべての色を正確に表現しきれないという課題があるためだ。
この問題を回避するためには、デザイナーと開発者の間で、どの色空間を基準にして色を定義し、表示するかを事前に明確に合意しておくことが重要である。
これらの問題は、どれも特殊なケースではなく、デザインの受け渡し、ダークモードの実装、そして「私の画面では色が違う」といった日常的な状況で頻繁に発生する。これらに共通するのは、数値としてのカラーコードは正しいにもかかわらず、そのフォーマットや文脈の解釈が間違っているために、意図した表示と異なる結果を生み出してしまう点である。 システムエンジニアを目指す初心者は、単にデザイナーから受け取った16進数カラーコードをそのまま貼り付けるだけでなく、それがどのような意味を持ち、どのようなプラットフォームでどのように解釈されるのか、そしてどのような色空間を前提としているのかを常に意識し、疑問があれば積極的に確認することが大切である。色の問題はユーザーインターフェースの品質に直結するため、細心の注意を払う必要がある。