Тема и цвета Telegram
Адаптер WebApp уже передаёт доступное окружение, а первый экран сохраняет свой выбор курса. Теперь согласуем цвета с Telegram. Пользователь может открыть светлый или тёмный клиент и сменить тему во время работы. Результат урока — интерфейс, который использует цвета хоста с понятными запасными значениями и обновляет схему, не пересоздавая форму и не сбрасывая несохранённый выбор.
Telegram предоставляет themeParams, colorScheme и CSS-переменные для темы. Официальное описание перечисляет смысл цветов, включая фон, текст и кнопку. Это функциональные значения: цвет текста не заменяется произвольным красивым акцентом. Некоторые поля могут отсутствовать на определённом клиенте, поэтому CSS содержит fallback. Поддержка темы не является доказательством доступности всей страницы: контраст и фокус нужно оценивать на фактическом результате отдельно.
Цвета по назначению
В screen.css замените исходное правило body, сохраните остальные правила и добавьте оформление поля. Это точно обозначенный фрагмент; полная копия CSS есть в снимке урока:
body {
margin: 0;
color: var(--tg-theme-text-color, #202126);
background: var(--tg-theme-bg-color, #ffffff);
font: 1.125rem/1.6 system-ui, sans-serif;
}
select {
width: 100%;
min-height: 3rem;
font: inherit;
color: var(--tg-theme-text-color, #202126);
background: var(--tg-theme-secondary-bg-color, #f2eef8);
}
#status { color: var(--tg-theme-hint-color, #535563); }
Статус использует hint-color для вторичного текста, но важный отказ сохранения позднее не должен становиться малозаметным только из-за этой роли. Нужно различать обычное пояснение и обязательное действие. Не окрашиваем ошибку в красный как единственный сигнал: её текст объясняет проблему, а атрибуты формы связывают сообщение с полем. Сейчас status говорит о несохранённом плане и не заменяет error-region будущей формы.
Пара фон/текст должна оставаться согласованной. Если заменить только фон на тёмный, сохранив тёмный текст исходной темы, экран может стать нечитаемым. Поэтому используем функциональные пары хоста. Собственный фирменный фиолетовый акцент можно оставить для определённых элементов, но его нужно проверить на обоих фонах. Цвет из опыта ProfessorWeb не обязателен для любого элемента Telegram: узнаваемость не должна мешать чтению внутри пользовательской темы.
Реакция на смену темы
Создадим полный модуль theme.js, который отмечает текущую схему в корневом элементе и возвращает функцию снятия подписки. Основные CSS-переменные предоставляет SDK; модуль не копирует каждый цвет вручную:
export function bindTheme(host, root = document.documentElement) {
const update = () => {
root.dataset.scheme = host?.colorScheme === 'dark' ? 'dark' : 'light';
};
update();
if (!host) return () => {};
host.onEvent('themeChanged', update);
return () => host.offEvent('themeChanged', update);
}
В полном screen.js урока добавляются import bindTheme и вызов const releaseTheme = bindTheme(host). Существующие catalog, select и renderSelection не изменяются. Переменная cleanup пригодится при уничтожении экрана; в простом документе экран живёт до закрытия страницы. Не вызывайте releaseTheme сразу после монтажа: тогда первое значение останется, а дальнейшие события темы перестанут обрабатываться. В отдельном сценарии архива можно увидеть, какой callback должен быть снят.
themeChanged сообщает об изменении, после которого читаются актуальные свойства WebApp. Мы не принимаем присланное событие как новую серверную личность и не повторяем обмен сессии. Цветовая схема относится к представлению. Такой узкий эффект позволяет не потерять выбранный Markdown при переходе в тёмный режим. Перезагрузка страницы ради цвета тоже возможна технически, но разрушает временное состояние и не нужна для этого механизма.
Системное поле и реальные клиенты
Штатный select может оформляться частью среды, а не только нашим CSS. Его выпадающая панель на разных платформах выглядит по-разному. Не обещаем одинаковую форму стрелки и пиксельные отступы. Важно, чтобы название курса читалось, фокус был виден и выбранное значение было понятно. Если нужен сложный нестандартный выбор, его доступность станет отдельной задачей; смена темы не оправдывает немедленную замену штатного элемента.
Для самих верхней и нижней областей Telegram есть методы задания поддерживаемых цветов и дополнительные параметры. В этом уроке их не вызываем: сначала адаптируем собственный документ. Не помещайте hex-строку в любой метод по предположению, что он принимает все CSS-цвета. Поддержка и допустимые значения проверяются по конкретному API и версии. Наш модуль ограничен colorScheme и событием, а будущая матрица capabilities рассмотрит дополнительные функции отдельно.
Для собственных иллюстраций также нужен вариант на разных фонах. PNG с белым прямоугольником может выглядеть нормально в светлой теме и случайно выделяться в тёмной. Это не ошибка события themeChanged: ресурс имеет свою подготовленную палитру. Можно использовать прозрачность или отдельные изображения, но решение зависит от содержания и качества. В нашем первом экране иллюстраций нет, поэтому цветовой договор проще. Если добавлять схему учебного маршрута, её подписи и границы нужно проверить вместе с окружением, а не только на белом холсте редактора.
Смена темы не должна неожиданно перемещать фокус. Читатель мог открыть select и выбрать значение с клавиатуры; обновление dataset корня не создаёт новое поле. Если CSS скрывает выбранный элемент для одной схемы, это уже предметная ошибка оформления. Для проверки сохраните Markdown, смените тему и вернитесь к полю: значение должно остаться прежним. Такой сценарий связывает цвет с реальной задачей, вместо отдельного сравнения двух декоративных картинок без состояния.
Ошибки переинициализации
Рассмотрим неправильный обработчик: после themeChanged он снова выполняет render всей страницы через innerHTML. Выбранный курс и фокус теряются, а прежний select может остаться связанным с несуществующим DOM-узлом. Для цветов такая операция не нужна. Исправление — изменить CSS-переменные либо небольшой атрибут корня и оставить содержимое в прежнем дереве. Это конкретный пример различия данных и представления, которое мы уже использовали в обычном сайте.
Другая ошибка — подписывать новый update при каждом выборе курса. Через несколько действий один themeChanged вызовет много одинаковых callback. Визуально проблема может быть незаметна, пока обработчик дешёвый, но структура уже неправильна. Сохраняйте одну подписку на время жизни экрана и снимайте её той же функцией. Это правило повторится с viewport и BackButton, где дублирование способно изменить уже не только цвет, но и само пользовательское действие.
Ожидаемый результат — светлый и тёмный документ с доступным текстом и сохранённым выбором; при отсутствии host используются обычные fallback-цвета. Telegram, контрастные измерения и браузерные просмотры здесь не запускались. Значения следует подтвердить на реальных целевых клиентах и при увеличении текста. Следующий урок оставит тот же интерфейс, но рассмотрит другую переменную среду: высоту WebView и безопасные отступы, которые меняются независимо от цвета.