Использование цвета в терминале
Я пишу небольшие скрипты для работы в программах-оболочках «Windows PowerShell» версии 5.1 и «PowerShell» версии 7 в операционной системе «Windows 10». Иногда тянет подсветить текстовый вывод в консоль разными цветами. В этой статье я хочу рассказать о паре грабель, на которые можно при этом наступить.
Окунемся в историю
Чтобы понять проблемы с использованием цвета в терминалах (сегодня — в эмуляторах терминалов), полезно в двух словах представлять, как цвет появился в терминалах и как он развивался. Когда терминалы были физическими устройствами, возможность использования разных цветов была ограничена на уровне устройства. Меньшее количество цветов в терминале означало более дешевое устройство, а, значит, более массовое (больше продаж — больше прибыль; вообще это не всегда так, но в данном случае это работало).
Сначала были монохромные терминалы (два цвета — для текста и для фона). Потом появился трехбитный цвет: пользователю терминала стали доступны 8 разных цветов. Именно тогда из ограничения в 3 бита родилась знаменитая восьмерка названий цветов, ставшая «родной» для терминалов:
000 #000000 black черный
001 #0000ff blue синий
010 #00ff00 green зеленый
011 #00ffff cyan голубой
100 #ff0000 red красный
101 #ff00ff magenta пурпурный (лиловый и т.д.)
110 #ffff00 yellow желтый
111 #ffffff white белый
Во второй редакции стандарта «ECMA-48» от 1979 года (первая редакция — от 1976 года, но я не смог найти ее в интернете) за этими названиями цветов уже закреплены соответствующие управляющие последовательности (коды). (Действующая редакция этого стандарта — пятая, от 1991 года.) В редакции от 1979 года есть еще управляющий код, с помощью которого можно было выделять текст. В разных терминалах это делалось по-разному: увеличением толщины (насыщенности) шрифта или увеличением яркости цвета. Таким образом из 8 имеющихся цветов выжимали 16 разных вариантов для текста (для фона использовали 8 базовых цветов):
black | bright (bold) black
blue | bright (bold) blue
green | bright (bold) green
cyan | bright (bold) cyan
red | bright (bold) red
magenta | bright (bold) magenta
yellow | bright (bold) yellow
white | bright (bold) white
Эти 16 названий цветов часто называют «цветами ANSI», хотя в соответствующем стандарте «ANSI X3.64» эти названия появились позже, чем в «ECMA-48», в 1980-е (сейчас стандарт «ANSI X3.64» не действует).
Следует отметить, что в упомянутых стандартах указаны только названия цветов, а не точные значения. Разные производители терминалов использовали для этих названий разные значения цветов. Поэтому цвет с одним и тем же названием мог выглядеть очень по-разному на терминалах разных производителей. Из этого в современных эмуляторах терминалов родились цветовые схемы с ограниченной палитрой из 8, 16 или более цветов.
Грабли первые
Цвет текста по умолчанию и цвет фона по умолчанию.
При ограничении цвета 3 битами на уровне устройства цвет текста по умолчанию и цвет фона по умолчанию приходилось выбирать из 8 доступных цветов. Обычно цветом фона по умолчанию выбирался черный (000), а цветом текста по умолчанию — белый (111).
При переходе от терминалов-физических устройств к эмуляторам терминалов (к программам) цветовые схемы с ограниченной палитрой в 8 и 16 цветов перешли в эмуляторы терминалов «по наследству». Несмотря на то, что сегодня физическое ограничение количества цветов в палитре цветовой схемы исчезло, никто не торопится увеличивать палитры цветовых схем. Главная причина этого в том, что с точки зрения дизайна пользовательских интерфейсов увеличение количества цветов в пользовательском интерфейсе увеличивает сложность нахождения гармоничного сочетания цветов, имеющихся в палитре (поэтому дизайнеры предпочитают обходиться как можно меньшим количеством цветов при проектировании пользовательских интерфейсов, да и не только их).
В самых свежих версиях современных эмуляторов терминалов для цвета текста по умолчанию и цвета фона по умолчанию в палитру цветовой схемы добавлены отдельные слоты, то есть вместо, к примеру, палитры из 16 слотов мы имеем палитру в 18 слотов (16 цветов ANSI, цвет текста по умолчанию, цвет фона по умолчанию). Но большинство авторов цветовых схем отказывается от использования этой возможности, назначая отдельному слоту для цвета текста по умолчанию то же значение цвета, что и для слота цвета «белый», а отдельному слоту для цвета фона по умолчанию — то же значение цвета, что и для слота цвета «черный».
Иллюстрация этой ситуации на примере эмулятора терминала «Windows Terminal». Видно палитру из 16 цветов ANSI и добавочные слоты палитры для цвета текста по умолчанию и цвета фона по умолчанию (в столбце «System colors»). Слот «Foreground» совпадает по значению цвета со слотом «White», а слот «Background» совпадает по значению цвета со слотом «Black». Это одна из цветовых схем этого эмулятора терминала, она называется «Campbell» и является цветовой схемой по умолчанию в этом эмуляторе терминала, но ее, конечно, можно поменять как на одну из встроенных, так и на созданную самостоятельно (или загруженную из интернета в формате JSON):
К чему это приводит? Предположим, я подсветил текст в терминале цветом из «черного» слота палитры установленной в терминале цветовой схемы. При этом я не учел, что во многих цветовых схемах «черный» цвет также является цветом фона по умолчанию. Таким образом, во многих терминалах мой скрипт выведет в консоль текст, который никто не увидит, так как текст «черного» цвета сольется с фоном по умолчанию («черного») цвета.
То же самое случится, если я захочу подсветить какой-нибудь текст, изменив цвет фона под ним на цвет из слота «белый» палитры текущей цветовой схемы, но оставив цвет самого текста цветом по умолчанию. Во многих терминалах такой текст никто не увидит, так как текст цвета по умолчанию («белого») сольется с цветом «белый» фона.
Как избежать этой проблемы? Хоть вам никто не может запретить использовать в терминале подсветку текста черным цветом на фоне по умолчанию или подсветку фона белым цветом у текста цвета по умолчанию, я советую избегать этих сочетаний.
Причина проблемы
Описанная выше проблема, а также ряд других, появляется в том случае, когда мы меняем только одну составляющую: или цвет текста, или цвет фона под текстом, оставляя другую составляющую на волю настроек цвета, произведенных пользователем эмулятора терминала. Очевидно, что избежать этих проблем можно, если во всех случаях контролировать обе составляющие: и цвет текста, и цвет фона под текстом. Однако, такое решение может не понравиться пользователю: вы навяжете ему свою цветовую схему вместо той, которую выбрал он для своего эмулятора терминала. Кроме того, создание своей цветовой схемы — не такая простая задача, как это кажется на первый взгляд. В среде пользователей терминалов не так много популярных цветовых схем, одна из них — знаменитая «Solarized». На сайте автора этой схемы можно узнать о множестве подводных камней, которые могут попасться при разработке цветовой схемы.
Грабли вторые
Использование 24-битного цвета.
Мне, как человеку, привыкшему писать тексты в интернете с использованием языков HTML и CSS, удобнее пользоваться цветовой моделью RGB. В этой модели для каждой составляющей (красная, зеленая, синяя) отводится один байт, то есть в целом это три байта, а, значит, 24 бита. 24-битный цвет (TrueColor) позволяет использовать более 16 миллионов различных цветов (224). Современные эмуляторы терминалов предоставляют возможность пользоваться таким цветом.
В одном из своих скриптов я захотел подсветить некоторые слова в тексте, выводимом в консоль. На языке PowerShell это можно исполнить достаточно просто с помощью соответствующего управляющего кода:
$text = "слово"
$Esc = [char]27 # с PowerShell версии 6 доступны `e и `u{1B}
$color = "38;2;255;255;0" # желтый цвет, RGB: 255;255;0
"$Esc[$color`m" + $text + "$Esc[0m"
В моем эмуляторе терминала «Windows Terminal» при установленной цветовой схеме «One Half Dark» это выглядит достаточно симпатично. Однако, я не учел, что существуют не только темные («ночные») цветовые схемы, но и светлые («дневные»). (Хотя я считаю использование светлых цветовых схем в терминалах кощунством.)
Иллюстрация моего вывода в терминал при цветовой схеме «One Half Dark» и при цветовой схеме «One Half Light»:
Видно, что при светлой цветовой схеме выделенные желтым слова хоть и видны, но имеют малую контрастность с цветом фона по умолчанию.
Кстати, тут следует упомянуть, что понятие «контрастность» можно выразить в числах, а, следовательно, это понятие объективное, а не субъективное. По крайней мере, это верно для веба: формула вычисления контрастности двух цветов определена в документе «WCAG» (действующая версия этого документа — 2.1, она опубликована в 2018 году). Этот же документ устанавливает критерий приемлемой контрастности цветов, разделенный на несколько уровней: «AA» и «AAA» (последний — более строгий). Согласно этому документу минимально приемлемая контрастность двух цветов равна 3:1 (яркость двух цветов должна отличаться в три раза). Нахождение хорошей контрастности всех пар цветов — это, кстати, один из подводных камней построения хорошей цветовой схемы для терминала.
Контрастность желтого #ffff00
цвета и цвета фона по умолчанию (для цветовой схемы «One Half Dark» #282c34
и для цветовой схемы «One Half Light» #fafafa
): 13,03:1 и 1,02:1. Ясно, что второй случай под критерий не подходит, да это видно и невооруженным взглядом.
Как избежать этой проблемы? Либо искать цвет, который подойдет для большинства используемых цветовых схем, но это аналогично построению своей цветовой схемы, о чем уже говорилось выше. Либо положиться на ограниченную палитру популярных цветовых схем для терминалов. То есть вместо цвета по модели RGB можно использовать цвет из текущей цветовой схемы. Для этого используется другой управляющий код, в котором цвет задается одним числом из 8, 16, 18 или более доступных слотов палитры текущей цветовой схемы.
Например, в моем случае я использовал слот с названием «желтый» из палитры текущей цветовой схемы. Вот как это выглядит в коде на языке PowerShell:
$text = "слово"
$Esc = [char]27 # с PowerShell версии 6 доступны `e и `u{1B}
$color = "33" # желтый цвет, палитра текущей цветовой схемы
"$Esc[$color`m" + $text + "$Esc[0m"
Иллюстрация моего вывода в терминал при цветовой схеме «One Half Dark» и при цветовой схеме «One Half Light» с использованием слота из палитры этих цветовых схем:
Как видно из вышеприведенной иллюстрации, такое использование цвета выглядит намного приятнее. Тут мы не выдумываем своё, а полагаемся на уже придуманное другими людьми цветовое решение. Контрастность цвета из «желтого» слота палитры двух указанных цветовых схем по сравнению с цветом фона по умолчанию:
«One Half Dark»:
#E5C07B
«желтый» и#282C34
(фон по умолчанию) — 8,1:1;«One Half Light»:
#C18301
«желтый» и#FAFAFA
(фон по умолчанию) — 3,08:1.
Оба варианта удовлетворяют критерию приемлемой контрастности из документа «WCAG». Контрастность можно вычислить самому или воспользоваться для ее вычисления одним из веб-сервисов (например).
Заключение
Использование цвета в эмуляторах терминала может вызвать проблемы. Следует учитывать и обходить некоторые узкие моменты. Например, не стоит подсвечивать текст черным цветом при фоне цвета по умолчанию или не стоит подсвечивать фон белым цветом при тексте цвета по умолчанию. Кроме этого, вместо использования любых цветов, какие придут в голову, стоит рассмотреть возможность использования цветов из ограниченных палитр распространенных цветовых схем для терминалов. Это поможет сохранить рекомендуемую стандартами контрастность цветов, что сохранит зрение пользователям. Как вариант — создание собственной цветовой схемы для терминала. Но это не так просто, как кажется, а также может быть воспринято пользователем как навязывание цветовой схемы.