Темизация приложений

Темизация приложения

Темы Q.Palette основаны на CSS Custom Properties (opens in a new tab): тема — это обычный CSS-файл, который переопределяет значения токенов. Благодаря этому тему можно менять на лету, без перезагрузки страницы и без пересборки приложения. Для токенов Q.Palette зарезервирован префикс --q- — учитывайте это, создавая собственные переменные.

Темизация состоит из двух независимых осей:

ОсьАтрибут на <html>Значения
Темаdata-color-themesoftui (по умолчанию), minimalism, идентификатор вашей темы
Цветовая схемаdata-color-schemelight, dark

Как приложение применяет тему

Рутовое приложение хранит выбранную тему и схему в localStorage, выставляет соответствующие атрибуты на <html> и подгружает CSS-файл темы в заранее объявленный тег <link id="theme-file"> в index.html.

Файл темы приложение ищет в двух местах, по порядку:

  1. Сервис конфигурации — /api/<pbc><serviceName>/<pbc><serviceName>/theme/<id>.css. Запрос выполняется, только если в assets/data/config.json заполнено поле pbc; serviceName по умолчанию — configuipd. Так тему можно обновить на работающем стенде, не пересобирая образ.
  2. Ассеты рутового приложения — assets/themes/<id>.css. Используются, если поле pbc не заполнено или сервис не вернул стили.
⚠️

Загрузка темы из сервиса конфигурации работает с версии 8.1.0. В более ранних версиях запрос выполнялся, но ответ отбрасывался, и тема всегда бралась из ассетов образа — там единственный рабочий способ обновить тему — обновить ассеты.

Посмотреть, как токены работают в живом интерфейсе, можно на странице «Темизация приложения» (opens in a new tab) портала для разработчиков: там значения токенов меняются селектором цвета и сразу применяются к области предпросмотра.

Создание собственной темы

Возьмите за основу готовую тему

Файлы поставляемых тем лежат в пакете @diasoft/qpalette-themes:

node_modules/@diasoft/qpalette-themes/assets/softui.css
node_modules/@diasoft/qpalette-themes/assets/minimalism.css

Скопируйте подходящий файл и переименуйте его, например в my-theme.css.

⚠️

Файл темы самодостаточен: рутовое приложение подменяет содержимое #theme-file целиком, поэтому тема должна содержать полный набор токенов. «Частичную» тему из нескольких переменных сделать нельзя — недостающие токены не подхватятся из другой темы.

Замените идентификатор в селекторах

В скопированном файле укажите идентификатор своей темы:

/* светлая схема */
html[data-color-theme='my-theme']:not(data-color-scheme),
html[data-color-theme='my-theme'][data-color-scheme='light'] {
  /* ... */
}
 
/* тёмная схема */
html[data-color-theme='my-theme'][data-color-scheme='dark'] {
  /* ... */
}

Задайте нужные значения токенов

--q-page-background: #fffdf5;
--q-page-text: #e18e46;
--q-page-text-secondary: hsl(55, 73%, 42%);

Разместите файл темы

Файл кладётся в ассеты рутового приложения — src/assets/themes/my-theme.css.

Если стенд работает с сервисом конфигурации (в assets/data/config.json заполнено поле pbc), тему можно отдавать оттуда — тогда её обновляют без пересборки образа. Начиная с версии 8.1.0 приложение сначала запрашивает тему у сервиса и берёт ассеты только как запасной вариант.

Зарегистрируйте тему в конфигурации

config.base.json
{
  "theme": {
    "themes": [
      { "name": "My Theme", "id": "my-theme" }
    ]
  }
}

Скелет файла темы

Файл темы состоит из трёх блоков: два по схемам и один общий. Токены, значения которых вы не меняете, оставляйте такими же, как в исходном файле, — удалять их нельзя.

my-theme.css
/* 1. Светлая схема: палитра и цветовые токены компонентов */
html[data-color-theme='my-theme']:not(data-color-scheme),
html[data-color-theme='my-theme'][data-color-scheme='light'] {
  --q-font-family: 'Inter', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Arial, sans-serif;
  --q-is-dark-scheme: 0;
 
  /* палитра */
  --q-base-0: white;
  --q-base-500: #69727b;
  --q-base-1000: black;
  --q-primary-500: #3b82f6;
  --q-primary: var(--q-primary-500);
  /* …остальные рампы и семантические токены — из исходного файла */
 
  /* токены компонентов */
  --q-sidebar-bg: var(--q-content-background);
  --q-sidebar-text: var(--q-content-text);
  /* … */
}
 
/* 2. Тёмная схема: те же токены с другими значениями */
html[data-color-theme='my-theme'][data-color-scheme='dark'] {
  --q-is-dark-scheme: 1;
 
  --q-base-0: black;
  --q-base-500: #969ca3;
  --q-base-1000: white;
  /* … */
}
 
/* 3. Общая часть: размеры, радиусы и типографика — от схемы не зависят */
html[data-color-theme='my-theme'] {
  --q-border-radius-sm: 0.25rem;
  --q-border-radius-md: 0.375rem;
  --q-border-radius-lg: 0.5rem;
  /* … */
}

Шрифт темы

Токен --q-font-family называет гарнитуру, но не подключает её. Рутовое приложение везёт Open Sans и Rubik — гарнитуры двух стандартных тем; фирменные гарнитуры остальных (TT Commons Pro, Stem) оно пока тоже везёт, но в одном из следующих выпусков (не раньше 8.3) перестанет — их подключает продукт, которому это нужно. Причин две: лицензии на коммерческие гарнитуры принадлежат конкретным продуктам, а файлы шрифта весят больше, чем сам файл темы.

@font-face действует на весь документ, но объявлять его нужно в CSS, который страница действительно загружает. Глобальные стили UI-сервиса для этого не годятся: рутовое приложение их не подключает (Настройка сборки UI-сервиса). Место для объявления — файл темы: тему рутовое приложение загружает по assets/themes/<id>.css, и url() в ней отсчитывается от самого файла. Шрифт кладите рядом — в assets/fonts того же rootapp/assets, которым продукт дополняет ассеты рутового при сборке образа:

rootapp/assets/themes/my-theme.css
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/inter-400.woff2') format('woff2');
}
 
html[data-color-theme='my-theme'] {
  --q-font-family: 'Inter', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Arial, sans-serif;
  /* … */
}

Так же готовится стандартная тема с фирменной гарнитурой к тому моменту, когда рутовое перестанет её возить: положите в rootapp/assets/themes/ копию файла темы с объявлениями @font-face — при сборке образа она ляжет поверх файла рутового.

Формата woff2 достаточно: его понимают все поддерживаемые браузеры. Объявляйте отдельный @font-face на каждое начертание, которое действительно используете, — для остальных браузер синтезирует жирность и наклон из ближайшего доступного.

💡

Пока фирменные гарнитуры стандартных тем (TT Commons Pro, Stem) приходят со стилями палитры, и подключать их отдельно не нужно. Когда они уедут из рутового (не раньше 8.3), без своего @font-face текст в такой теме будет рисоваться системным шрифтом из списка --q-font-family — переходите заранее.

Настройка через конфигурацию

Раздел theme в config.json управляет и списком доступных тем, и тем, что именно увидит пользователь.

ПолеНазначение
theme.themesСписок тем в панели настроек: name и id каждой темы
theme.themeФиксирует тему: выбор темы в панели настроек скрывается
theme.schemeФиксирует цветовую схему: выбор схемы в панели настроек скрывается
theme.defaultThemeТема при первом посещении, пользователь может её сменить
theme.defaultSchemeСхема при первом посещении
config.base.json
{
  "theme": {
    "defaultScheme": "light",
    "defaultTheme": "softui",
    "themes": [
      { "name": "SoftUI", "id": "softui" },
      { "name": "Минимализм", "id": "minimalism" },
      { "name": "My Theme", "id": "my-theme" }
    ]
  }
}

Что применяется при старте

Сначала приложение собирает список доступных тем, затем выбирает из него тему и цветовую схему. Список берётся из первого непустого источника:

  1. themes — устаревший вариант записи на верхнем уровне конфигурации;
  2. theme.themes;
  3. список по умолчанию — SoftUI и Минимализм.

Тема и схема выбираются независимо друг от друга, по одному и тому же правилу — первое заданное значение сверху вниз:

ПриоритетТемаЦветовая схема
1theme.theme — фиксация, выбор в настройках скрытtheme.scheme — фиксация, выбор в настройках скрыт
2выбор пользователя из localStorage, если такая тема есть в спискевыбор пользователя из localStorage
3theme.defaultThemetheme.defaultScheme
4первая тема из спискаlight

Список тем формируется всегда, даже когда тема зафиксирована через theme.theme: он остаётся источником запасного значения и наполняет панель настроек там, где выбор доступен.

Совместимость со старыми конфигурациями: color.scheme читается как тема, color.theme — как схема, color.defaultScheme и color.defaultTheme — как значения по умолчанию для них же. Эти поля объявлены устаревшими и будут удалены в 9.0.0 — используйте раздел theme.

Структура файла темы

Файл темы состоит из четырёх частей:

  1. Палитра светлой схемы — цветовые рампы и семантические токены под [data-color-scheme='light'].
  2. Палитра тёмной схемы — те же токены под [data-color-scheme='dark'].
  3. Цветовые токены компонентов — --q-button-*, --q-input-*, --q-sidebar-* и т. д.
  4. Размерные токены — размеры, отступы, радиусы и типографика. Они не зависят от схемы и задаются один раз.

Токены именуются по шаблону --q-<группа>[-модификатор][-шаг|состояние]. Рампа каждого цвета содержит шаги от -0 (белый) до -1000 (чёрный), алиас без шага, полупрозрачные варианты -transparent-* и тени -shadow-*:

--q-primary-50: #e8f1f8;
--q-primary-500: #3b82f6;
--q-primary-900: #061725;
--q-primary: var(--q-primary-500);
--q-primary-transparent-100: rgba(29, 113, 184, 0.1);

Компонентные токены, как правило, ссылаются на семантический слой, а не на конкретные цвета — так тема остаётся согласованной:

--q-sidebar-bg: var(--q-content-background);
--q-sidebar-text: var(--q-content-text);
--q-sidebar-hover: var(--q-primary-500);
--q-sidebar-active-bg: var(--q-primary-transparent-100);
--q-sidebar-active-text: var(--q-primary-700);
--q-sidebar-border: var(--q-content-border);
--q-sidebar-icon: var(--q-base-500);
⚠️

Между светлой и тёмной схемами должны различаться только цвета. Размеры, отступы и типографику задавайте в общей части файла — иначе интерфейс будет «прыгать» при переключении схемы.

Полный перечень токенов смотрите прямо в файлах поставляемых тем — softui.css и minimalism.css пакета @diasoft/qpalette-themes. Это точный и всегда актуальный источник: набор токенов меняется вместе с версией Q.Palette.

Проверка результата

Откройте приложение и переключите тему в панели настроек. Убедитесь, что на элементе <html> выставлены ожидаемые data-color-theme и data-color-scheme, а в <link id="theme-file"> подставлен адрес вашего файла. Значения конкретных токенов удобно смотреть в инструментах разработчика, на селекторе html.

Если тема не применилась, проверьте: совпадает ли id в конфигурации с идентификатором в селекторах файла, доступен ли файл по своему адресу (вкладка Network), и не зафиксирована ли другая тема через theme.theme.