ターミナルの色は厄介
(jvns.ca)- ターミナルの色の問題は好みというより 標準の不在と実装差 に近く、同じコマンド出力でもターミナル・テーマ・アプリの組み合わせによって読みにくくなることがある
- 基本の 16 ANSI 色、256 色、24 ビット hex 色が併用されるが、色番号が実際にどんな色に見えるかはターミナルエミュレータごとに異なる
- 青文字/黒背景、明るい黄色/白背景、Solarized の灰色マッピング、
ngrokの背景色のような コントラストの衝突 は、プログラムの想定とユーザーテーマが食い違うと表面化する - 対応策はパレットの再設定、
base16-shell、LS_COLORS、NO_COLOR=true、--color=always、unbuffer、Vim/Neovim の 24 ビット色対応 など複数の層に分かれる - iTerm2、tabby、kitty、Ghostty、Windows Terminal の minimum contrast 機能は完全な解決策ではないが、読めない出力が生じるリスクを減らしてくれる
ANSI 色とターミナルごとの差異
- ターミナルには通常、黒、赤、緑、黄、青、マゼンタ、シアン、白と、それぞれの明るい版から成る 16 個の ANSI 色 がある
- プログラムは ANSI escape code を出力して色を指定する
- たとえば
\033[38;5;{num}mの形で前景色番号を指定できる
- たとえば
- 問題は、ANSI 色番号が実際にどの hex 色に対応するかについて 一貫した標準がないこと にある
- ターミナルエミュレータごとに色を独自に選んでいる
- Wikipedia の ANSI escape code の色表でも実装ごとの差が大きい
- この違いのせいで、
lsがディレクトリに使う青色が黒背景では見えにくいことがある - 明るい黄色と白背景の組み合わせもほとんど読めず、明るい緑色のような他の色でも同様の問題が起こりうる
色パレットを再設定する方法
- 既定の ANSI 色が気に入らない、またはコントラストの問題があるなら、パレットの再設定 で対処できる
-
ターミナルエミュレータで設定
- 近年のターミナルエミュレータの多くは色を再設定する機能を提供している
- 一部は内蔵テーマも用意している
- 人気のあるターミナルエミュレータほど選べるテーマが多いことがある
-
シェルスクリプトで設定
- ANSI escape code を出力してターミナルエミュレータの色パレットを変更できる
- base16-shell の Solarized Light スクリプト は、複数のターミナルごとの escape code の慣習を使っている
- スクリプトは
TERM環境変数に応じて適切な escape code スタイルを選ぼうとする - シェルスクリプト方式は、ターミナルエミュレータを替えても同じ色設定を保ちやすい
- ただし、すべてのターミナルエミュレータがこの方法によるパレット変更をサポートしているわけではなく、対応していても結果は多少異なることがある
256 色、24 ビット色、ツールごとのテーマ
- 多くのターミナルは 3 つの色指定方式をサポートしている
- 16 ANSI 色
- 拡張 256 色
#ffea03のような 24 ビット hex 色
fdは出力で^[[38;5;48mのような escape code を使って前景色 48 番を指定する- この色は一般的なターミナル色表にはないライム色のように見えることがある
- 利用中の色表ではコントラストが悪い可能性がある
batも同様に色を使うが、さまざまなターミナルテーマで動くことを意識した既定出力に見えるfd、bat、deltaのような一部の新しいターミナルツールは、任意のカスタムテーマをサポートしている- 既定テーマがターミナル背景と衝突することがある
- その代わり、16 ANSI 色だけを選ぶよりもツール出力のテーマを細かく制御できる
bat --theme ansiを使うと、batは通常のターミナル色表に設定された ANSI 色を使う
Solarized と base16 で起きる衝突
- Solarized Light は実装によって bright 色のマッピング が異なって見えることがある
- iTerm の既定の Solarized Light テーマは、元の Solarized デザインのように色 9〜14、つまり “bright blue”、“bright red” などを灰色系にマッピングする
lsがディレクトリや実行ファイルに bright 色を使うと灰色に見えることがある
- base16 Solarized Light は、bright 色を灰色ではなく実際の色のまま保つ派生形として使われることがある
- この違いは、Vim Solarized カラースキームが灰色を使えるように元の Solarized が設計された結果と思われる
base16-shellはbase16-vimに追加の色を提供するため、色 16〜21 を設定するngrokが背景を色 16 番の “black” に設定しようとしたとき、その色が明るいオレンジに再マッピングされていると非常に奇妙な背景になる- プログラムが ANSI 背景色を指定し、ユーザーがその色を別の意味に再マッピングすると、この種の衝突は起こりやすい
minimum contrast でコントラスト問題を減らす
- 一部のターミナルは minimum contrast 機能により、前景色と背景色のコントラストが不十分なときに色を自動調整する
- iTerm2、tabby、kitty の text_fg_override_threshold、Ghostty、Windows Terminal がこの機能を提供している
- iTerm で minimum contrast の値を 40 に設定すると、
ngrokの背景色衝突のような読みにくい出力も読みやすくなる - 色設定が不適切だったり、プログラムとテーマが衝突したりしたとき、minimum contrast は偶発的な 可読性の問題 を減らす実用的な安全装置になる
TERM、パイプ、色の無効化
- SSH で接続した先のシステムがローカルの
TERM環境変数の値をサポートしていないと、色が正しく動作しないことがある - システムは
terminfoデータベースを利用しており、TERMの値がそのデータベースに存在しないと、ターミナルに合った色出力を判断しにくい - 再現できる環境はなかったが、Stack Overflow の回答では
sshの代わりにTERM=xterm sshのような形が提案されている - コマンドをパイプでつなぐと、色が無効化されることがある
fd | lessは色なしで出力されることがある- 一般に
grepのようなツールへ渡すとき、escape code なしのプレーンテキストだけが出るのは便利でもある
- 色を維持したいなら
unbuffer fd | less -rを使えるunbufferはコマンドが TTY に書き込んでいると認識するよう tty を開く- パイプに書く際の出力バッファリング問題も解決する
- 一部のコマンドは
fdのように--color=alwaysフラグで常に色を出力できる
ls の色を切るか変更する
- 一部のユーザーは
lsの色出力を望んでいない- 青色が黒背景で読みにくい、あるいは
- ターミナル色表を調整したくない、あるいは
- 色そのものが役に立たないことがあるためだ
- 選択肢はいくつかある
ls --color=neverで色を無効化できるLS_COLORS環境変数でlsが使う色をカスタマイズできる- 一部のプログラムは
NO_COLOR=trueをサポートしており、一覧は no-color.org にある
- 例として
LS_COLORS="fi=0:di=0:ln=0:pi=0:so=0:bd=0:cd=0:or=0:ex=0" lsのように設定すると、複数のファイル種別の色スタイルを 0 にできる
Vim と Neovim の変化
- Vim の色設定は、かつてターミナル色表と Vim のカラースキームを合わせる必要があり、問題が多かった
- ターミナル内での Vim カラースキーム設定には大きく 2 つの方式がある
- Vim の色クラスを ANSI 色番号 にマッピングする
- カラースキームが
#faea99のような 24 ビット hex 色 を直接使う
- 20 年前には 24 ビット hex 色をサポートするターミナルは一般的ではなく、Vim もターミナルでの 24 ビット色利用をサポートしていなかった
- Vim は 2016 年に 24 ビット色対応 を追加した
- Neovim 0.10 は 2024 年 5 月にリリースされ、24 ビット色対応ターミナルでは
termguicolorsが 既定で有効 になった - 2024 年時点で 24 ビット色をサポートしないターミナルを使う予定がないなら、ターミナル色表と Vim カラースキームを必ず同期しなければならない負担はなくなる
- この変化により、
base16-shellが Vim 連携のために色 16〜21 を特別に設定する必要性は薄れる- その部分を削除すれば、
ngrokのオレンジ色の背景のような衝突を避けられる
- その部分を削除すれば、
base16 の使用経験と限界
- base16-shell と base16-vim を一緒に使うと、ターミナルと Vim の色を合わせやすい
- fish の設定に 数行 を追加し、
.vimrcに数行書き足す方式で設定できる - base16 は誰にでも合う方法ではない
- 内蔵テーマ数が限られており、気に入るテーマがないかもしれない
- Solarized の base16 テーマは bright ANSI 色を通常色と同じに設定しており、bright 色が異なる必要のある用途では問題になることがある
base16-vimにより多くの色を提供するために色 16〜21 を設定する方式は、24 ビット色対応ターミナルしか使わない環境では不要かもしれない- 色 16〜21 の設定は、Linux framebuffer terminal のように 256 色をサポートしないターミナルでは問題になることがある
- base16 のコミュニティフォークとして tinted-theming がある
色表ツールと人気テーマ
- rootloops.sh は色表生成のためのツールだ
- あわせて読む記事として “let’s create a terminal color scheme” がある
- Mastodon の反応で言及されていた人気のカラースキームは以下のとおり
なお残る問題
tmux、SSH、Vim の組み合わせのように、複数のプログラムが交わる地点では追加の色問題が生じうる- 良いカラースキームを選ぶこと自体も難しい
- 色覚特性のあるユーザーは適切なカラースキームを見つけにくいことがある
- 背景色がカーソルや選択範囲の色に近すぎると位置を把握しにくい
- すべてのプログラムでうまく動く色を見つけるのは難しく、
ngrokの例のように衝突が起きることもある
- Nethack や Midnight Commander のように、既定の ANSI 色ベースの特定の “クラシック” な見た目を期待するプログラムは、Solarized のようなテーマでは違和感が出ることがある
- 一つの対処法は、プログラム実行直前に ANSI パレットを変更することだ
- たとえば Nethack 実行前に黄色をより明るい黄色へ再マッピングできる
- 色がただ普通に動いてほしいユーザーにとって、多くの手動設定は負担であり、妥当な既定値や minimum contrast のような機能が重要になる
1件のコメント
Hacker News のコメント
システム用の明るいカラーテーマをきちんと作ろうとして、こうした問題やさらに多くの問題を一通り経験しました。特に興味深かった点が2つあります。
振り返ってみれば当然ですが、明るいテーマは画面が変わったとき、暗いテーマよりもはるかに微妙に違って見えるという点には驚きました。ハードウェアやソフトウェア設定が色出力の差を生むことはありますが、明るい領域を中心とした変化は、暗い領域の変化よりもずっと大きく知覚されます。
また、Linux や BSD のコンソールのように、フォントや凝った色のサポートが限られた環境まで降りなければならないことがあります。そうすると
fdやexa/lsd/whateverが扱いにくくなったり、使えないレベルになったりします。特に後者をlsにマッピングしているとさらに面倒です。長い試行錯誤の末、X なしでもそれなりのターミナル機能を使うために fbterm を調整しましたが、fbterm にも固有の問題があります。Vim のカラースキームを設定するときは、機能の乏しいコンソール向けの代替テーマを用意するか、Vim のコマンドを見ずに打つ練習をかなりしておく必要があります :D編集: 本格的な作業用ではありませんが、X なしでも何ができるかを示す、スタイリングした fbterm の画面です: https://i.imgur.com/RbDRgtD.png
そのため、適切な露出とコントラストを合わせる際には、ハイライトが「ぴったり」に見えるよう調整するのにかなりの時間を使います。デジタルプリントはしませんが、同じ原理が当てはまりそうです。
残念ながら、JavaScript で書かれていることが多い「モダン」なコマンドラインツールのフレームワークは、これを無視しがちです。
https://www.gnu.org/software/gettext/manual/html_node/The-TE...
データ点に同じ色の弱い「光のにじみ」効果を付ける設定もあり、光る点のように見せることができます。色の飽和度という観点、つまりスペクトル全体の信号対雑音比という観点では、黒い背景のほうが優れているというのは理にかなっています。
異なる色を10色ほどならほぼ問題なく使えます。また、黄色は赤と緑の間、オレンジは赤と黄色の間というように、順序/距離情報を伝えられる利点もあるため、細分化し、2つの色がどれだけ近いかを解釈しやすくなります。
実際には、そこまで難しいとは思わない
ステップ1: ほとんどのターミナルエミュレータはデフォルトのカラーパレットがひどいので、変えればよい。個人的には Solarized、特にダークテーマが好きだが、ライトテーマも悪くない。Solarized でなくても、デフォルト背景の上でどの色でも読めるテーマはたくさんある
ステップ2: デフォルトで 8ビットや 24ビットカラーのエスケープシーケンスを使うコマンドラインツールは避けるべきだ。議論の余地はあるかもしれないが、個人的にはユーザーに敵対的だと思う。すでに自分の可読性の要件に合う最良のターミナルカラーパレットを選んでいるのに、開発者が自分をまがいもののデザイナーか何かだと思ってそれを上書きするのは役に立たない。ツール内でテーマを有効にするオプションを提供することはできるが、デフォルトではなく選択肢であるべきだ
人生の大半をターミナルで過ごす立場としては、この2つのルールを守るだけで、ほとんど設定の負担なしに一貫して読みやすいターミナル環境を作れる。本当の問題は、開発者が 8ビットと 24ビットカラーのエスケープコードを使うことを、私たちがあまりにも容認しすぎている点にある
何年も経つうちに、新機能はユーザーの目の前に押し出し、良いデフォルト値は強く押し付けるべきだという結論に至った。そうしなければ機能はほとんど使われない
8ビットと 24ビットカラーはすでに任意で使う機能であり、設定も可能だ。正しく動作するターミナルプログラムなら、TERM と COLORTERM 環境変数がそうした色を使うかどうかを決める
多くのアプリケーションは “bright black” のテキストが背景上で見えることを期待している。例はここに多い: https://github.com/altercation/solarized/issues/220
ほぼすべての他のカラーパレットでは、“bright black” を黒背景の上で読めるグレーとして表現している
たとえば pamix は背景を黒に設定し、tmux のステータスバーや ngrok も似たようなことをする。すると、それに対処するためにウサギの穴に落ちていくことになる
どちらも、解決策はターミナル側にあるべきだと示唆している。ターミナルプログラムはまず妥当なデフォルト色を持つべきで、青は青として認識できる必要があり、一般的な色覚異常も考慮すべきだ。たとえば純粋な赤を黒背景の上に置くと読みにくいので、
ff0000だけを使うのではなく、少しでも混ぜたほうがよいターミナルが妥当なデフォルト値を持つようになれば、コマンドラインプログラムは標準の16色を使えばよい。ここで 24ビットカラーを使おうとする試みは、「ターミナル側が正常性を提供することを諦めた」か「自分が一番よく分かっている」という意味に近く、結局ユーザーは N 個のプログラムをそれぞれ設定しなければならない。コマンドラインアプリ内にテーマを定義しない、というのが私の考える最善だ
Webサイトやフォントも同じように見ている。コンテンツは HTML で提供し、ユーザーが望む形で見られるようにすべきで、ページレイアウトも雑誌のレイアウトのように固定せず、流れるようにすべきだ
デスクトップソフトウェアでも似ている。Wayland コンポジタがウィンドウ位置を記憶すべきだ。X の各プログラムが自分のウィンドウ位置を保存して復元するのは愚かだった。Wayland は環境情報を知ることはセキュリティ問題だと言い、その点には同意するが、そうであればその記憶はデスクトップ環境の仕事になる。すべてのアプリからそのコードを取り除くこともできる
「ソフトウェアスタックのどこでこれを処理すべきか?」という問いは、さまざまな領域で繰り返される。どこに置くべきかを選ぶときは、目標が何かをまず答えるべきで、私の答えは常に単純さと保守性のほうに傾く
Julia の記事もこのアイデアに触れているが、具体的な苦労までは踏み込んでいない。関連して、ターミナルのカラーパレットが基本16色だけを扱うのか、RGB やグレースケール領域までいじるのかも気になる。8ビットと 16ビットカラーも可読性の要件に合わせて調整できるはずだからだ
色を選ぶ立場なら、新しい APCA アルゴリズム(Advanced Perception of Colour)で色を確認してみるとよさそうだ
APCA は今後 WCAG 3.0 で従来の x:y コントラスト方式を置き換える予定だが、あと数年はかかるかもしれない。フォントサイズ、太さ、前景/背景、さらには周囲の照明や環境、意図した用途まで考慮するという
ターミナルエミュレータがデバイスの照度センサーを使って、環境要因に合わせてコントラストを最適化できるなら、かなりよさそうだ
色を完全にオフにしたがる完全な狂人たちもいる[0]。自分でも試してみたい気はするが、きれいな色も好き
[0] https://no-color.org/
:syntax offが私の唯一の Vim 設定。ターミナルの配色セットは、コンピュータプログラマーと電気工学者、そして色で重要なのは色そのものではなく 明るさ だということを知らない人たちが設計したものだからターミナルで色を望まない人と、ブラウザで強力な広告ブロッカーを使ったりリーダーモードを多用したりする人との間に相関があるのか気になる
出力をパイプに渡すとき、色は何のメリットもなく 負債 にしかならない。要点は選択肢に近い
Linux と Windows を行き来するとさらにひどくなる。多くのツールのデフォルトの色使いは、ほとんど使えないレベルか、実際に使い物にならなくしている
一部のツールは
--color=neverオプションを提供しており、かなり多くのツールが NO_COLOR を尊重する。残念ながら全部ではなく、そうすると言っているツールでも、そのモードはパイプラインやプログラミングインターフェース用途でしか使われないと想定している場合がある。そのため、あらゆる不幸な挙動が起きる意味のある 色温度 という一貫したカラーパレットの概念があり、青みを帯びた光を見すぎる目の疲れを減らすため、暖色寄りになっている。alacritty のカラーパレットも同梱されている
ずっと前からターミナルに 明るいテーマ を使いたかったが、暗いターミナル背景に最適化されたカスタムの非 ANSI カラーを使うプログラムが多すぎて、いつも諦めていた
一部のプログラムでは色を設定できるが、すべてのプログラムを一つずつ設定するのはあまりにも面倒に感じた。そこで今は暗いターミナル背景を使いつつ、すべての色が自分の基準で十分なコントラスト比を持ち、APCA/WCAG 3 のコントラスト検査にも通る ANSI テーマを自分で選んで使っている。満足している :)
OSC 11 制御シーケンスを使うとプログラムは背景色を取得できるが、それを気にするプログラムはほとんどない。たいていは、誰かが黄色い警告メッセージがかっこよく見えると思ったら、そこで終わり
これはさらに別の問題にもつながる。xterm 互換ではないのに、デフォルトで
TERM=xterm256-colorを設定する人気のターミナルがある編集: たった今
xtermcontrolを見つけた。スクリプト内で背景の確認を抽象化でき、主要なパッケージマネージャーすべてに入っている最近は諦めて、ほとんどのアプリを手動で ANSI カラーを使うように設定している。たとえば fzf のようなものにはコマンドラインスイッチがある。Vim と Emacs だけを非 ANSI アプリとして残し、残りはターミナルで使うカラーパレットに合わせて設定している。望むなら vim-dim のようなものでさらに一歩進めることもできる
Emacs にはあまり見つけられていない
frame-background-mode[0] があり、実質的にはlightかdarkのどちらかEmacs がなぜある項目に妙な色を選んだのか気になるときは、たいてい frame-background-mode が間違っている場合が多い
[0]https://doc.endlessparentheses.com/Var/frame-background-mode...
白い文字の暗い背景ターミナルでは
export COLORFGBG='15;0'黒い文字の明るい背景ターミナルでは
export COLORFGBG='0;15'ほぼ 10 年間、少し変更した FlatUI カラーパレット を使ってきたが、実際に問題になった状況はほとんどなかった
png ファイル: https://drive.google.com/file/d/1mkMESeGf7MNdKa-rPDx7Yf9BDit...
imgur リンク: https://imgur.com/a/flatui-7mqVZAY
ベース: https://flatuicolors.com/palette/defo
私も自分のテーマをほぼ 10 年使っている。iTerm、vim、Emacs、vscode などでうまく動作している
Apple Terminalを長年使ってきて、以前は「Terminal Fructose」という良いカラースキーム集があり、作業ごとに異なる背景色を使っていた。Appleもある時点でそれをテーマとして内蔵した
しかしAppleはTerminalのサポートを放置してきており、256色を超える色をサポートせず、より現代的な機能も不足している。Neovim+LazyVim構成とも相性がよくない
より良いターミナルを探そうとiTerm2、Kitty、Alacritty、Weztermを行き来したが、どれもより現代的ではあるものの、複数の背景色/テーマのウィンドウを素早く開ける基本的な「New Window with Profile ...」オプションはなさそうだった。少なくとも、多少の試行錯誤なしにはそうだった
今はWeztermに落ち着いている。主な理由はWindowsでも動作し、
wezterm.lua設定を共有できるからだ。ちなみにWindows Terminalも実際かなり良い。それでも、ちょっとした作業、たとえばpython3をREPLとして使って計算したり、16進数/2進数のテストをしたりするときは、今でもApple Terminalを起動することがあるiTerm2で恋しい機能はいくつかあるが、ほとんどはマウス関連だ。それ以外は数晩かけて望みどおりに設定し、
cmd-nが現在のディレクトリではなくデフォルトディレクトリで新しいウィンドウを開くようにしてからは、満足して使っているlaunch_menu[1]や、コマンドパレットに項目を追加するaugment-command-palette[2]あたりに何かハックを入れられそうだ[1] https://wezfurlong.org/wezterm/config/lua/config/launch_menu...
[2] https://wezfurlong.org/wezterm/config/lua/window-events/augm...
そう、多少の試行錯誤は必要だ。だが、非常に具体的な機能を望む形そのままにプログラムすることこそが、WezTermらしい体験の頂点だ
ターミナルだけでなく、ネオンサインやLED看板でも青色は夜に最も目に厳しい
遠くから見ると、いつもぼやけてピントも合わない。大企業が青い照明で巨大な看板を掲げているのを見ると、人々が読めないことに気づきもしないまま大金を使っているのが不思議だ
ノートPCで日光の周期に合わせてライト/ダークモードを自動切り替えするようにしているが、明るい背景に暗い文字のモードへ切り替えると壊れるカラー出力アプリケーションの多さには驚く
コマンドラインツールを作る際、どうしても色を使わなければならないなら、ANSI 16色だけを使うか、エンドユーザーがカラースキームを変更できるようにすべきだ