Skip to content

Тематизация ​

Дизайн-система поддерживает смену темы или настройку своей собственной темы программно, в том числе прямо в рантайме.

Как это устроено ​

Тема в CDS 4 — это набор семантических токенов, из которых при старте приложения генерируются CSS-переменные:

ThemeDefinition (TS)  →  --cds-color-* / --cds-* (CSS-переменные)  →  стили компонентов
                                     ↓
                          @theme (Tailwind CSS v4)  →  bg-surface-primary, rounded-200, …

В переменных лежат готовые цвета — ровно в том виде, в каком они записаны в теме. Встроенные темы пишут цвета в oklch(). Прозрачность поверх токена задаётся через color-mix():

css
/* сгенерировано CDS */
:root {
  --cds-color-brand-primary: oklch(57.4% 0.16 254.98);
  --cds-color-surface-primary: oklch(100% 0 0);
  --cds-spacing-400: 1rem;
  --cds-border-radius-200: 0.25rem;
}

/* использование */
.my-block {
  background: var(--cds-color-surface-primary);
  border: var(--cds-border-100) solid color-mix(in srgb, var(--cds-color-brand-primary) 40%, transparent);
  padding: var(--cds-spacing-400);
}

В CDS 3 и ранних сборках CDS 4 в переменных лежали каналы RGB и цвет читался как rgb(var(--cds-color-x)). Такая запись больше не работает — см. миграцию.

Стили тем инжектятся в <style id="cds-theme-stylesheet"> и пересчитываются реактивно при смене темы.

Подключение темы ​

CDS 4 поставляет восемь готовых тем:

ТемаОписание
cdsLightсветлая тема, используется по умолчанию
cdsDarkтёмная тема
b2bLightсветлая тема для приложений B2B-Center
b2bDarkтёмная тема для приложений B2B-Center
lumaLightсветлая тема Luma
lumaDarkтёмная тема Luma
promoLightсветлая промо-тема
promoDarkтёмная промо-тема

По умолчанию используется cdsLight — подключать её не нужно.

Чтобы включить другую тему, передайте её название в опции createCds:

ts
import { createApp } from 'vue';
import '@central-design-system/components/styles';
import { createCds, components, directives } from '@central-design-system/components';

import App from './App.vue';

const cds = createCds({
  components,
  directives,
  theme: {
    defaultTheme: 'b2bLight'
  }
});

createApp(App).use(cds);

Опции темизации ​

ts
createCds({
  theme: {
    defaultTheme: 'cdsLight',
    prefix: 'cds',
    themes: {},
    variations: false,
    utilities: true,
    legacyClasses: false,
    scoped: false,
    scope: undefined,
    stylesheetId: 'cds-theme-stylesheet',
    cspNonce: undefined
  }
});
ОпцияТипПо умолчаниюНазначение
defaultThemeThemeName | string'cdsLight'Активная тема при старте
prefixstring'cds'Префикс генерируемых CSS-переменных (--cds-color-*)
themesRecord<string, ThemeDefinition>штатные темыСвои темы; мержатся со штатными
variationsfalse | { colors: string[]; lighten: number; darken: number }falseАвтогенерация светлых/тёмных вариаций цветов
utilitiesbooleantrueГенерация утилитарных классов фона/текста по токенам темы
legacyClassesbooleanfalseЛегаси-классы CDS 3 (cds-bg-*, cds-text-*, cds-border-*)
scopedbooleanfalseНе писать переменные в :root
scopestring—Ограничить селекторы темы поддеревом (:where(<scope>))
stylesheetIdstring'cds-theme-stylesheet'id тега <style> с темой
cspNoncestring—nonce для Content Security Policy

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

Своя тема — это объект ThemeDefinition с полями dark, colors и variables. Указывать нужно только переопределения: недостающие токены наследуются из cdsLight / cdsDark.

Цвет можно записать в hex, rgb() или oklch() — в CSS-переменную значение попадёт как написано. Контрастный текст on-* и вариации CDS рассчитает для любого из этих форматов.

ts
import { createApp } from 'vue';
import { createCds, components, directives } from '@central-design-system/components';

import type { ThemeDefinition } from '@central-design-system/components';

import App from './App.vue';

const myCustomGreenTheme: ThemeDefinition = {
  dark: false,
  colors: {
    // Основные цвета бренда новой темы
    'brand-primary': 'oklch(55.5% 0.1605 142.24)',
    'brand-primary-hover': '#3C9C2C',
    'brand-primary-active': '#50A946',
    'brand-primary-border': '#2C8926',
    'brand-primary-content': '#005005',
    'brand-secondary': '#DCF5D0',
    'brand-secondary-hover': '#A5DD98',
    'brand-secondary-active': '#97D283',
    'brand-secondary-border': '#BCE8AB',
    // Поверхности
    'page-primary': '#F2F8F0',
    'surface-secondary': '#E8F3E4'
  },
  // Переменные: типографика, отступы, скругления, тени
  variables: {
    'font-family': 'Roboto, sans-serif',
    'border-radius-200': '0.625rem'
  }
};

const cds = createCds({
  components,
  directives,
  theme: {
    defaultTheme: 'myCustomGreenTheme',
    themes: {
      myCustomGreenTheme
    }
  }
});

createApp(App).use(cds);

Для каждой зарегистрированной темы дополнительно генерируется класс .cds-theme--<name> — им можно включить тему на произвольном участке разметки.

Группы токенов ​

colors — семантические цвета:

  • brand-* — бренд (primary, secondary и их hover / active / border / content)
  • surface-*, page-*, fill-* — поверхности и заливки
  • content-* — текст, иконки, ссылки
  • line-* — границы, разделители, фокус и выделение
  • status-{success,warning,progress,info,danger,indication}-* — статусы
  • other-* — дополнительные акцентные палитры

variables — размерности: spacing-*, font-size-*, font-line-height-*, font-weight-*, border-*, border-radius-*, shadow-{x,y,blur}-*, font-family, base-unit.

Полный список токенов конкретной темы смотрите в исходниках пакета: @central-design-system/components/src/themes/cdsLight.ts.

Смена темы ​

Тему можно менять прямо в рантайме через composable useTheme:

vue
<script setup lang="ts">
import { useTheme } from '@central-design-system/components';

const theme = useTheme();

function toggleTheme(): void {
  theme.global.name.value = theme.global.current.value.dark ? 'cdsLight' : 'cdsDark';
}
</script>

<template>
  <cds-button @click="toggleTheme">Переключить тему</cds-button>
</template>

Также можно изменить тему конкретного компонента, передав в него проп theme. Учитывайте, что тема применится не только к самому компоненту, но и ко всем вложенным в него компонентам:

vue
<template>
  <cds-notification theme="b2bLight">
    <!-- кнопка будет использовать тему b2bLight -->
    <cds-button>Button</cds-button>
  </cds-notification>
</template>

Проп theme поддерживают компоненты-контейнеры и большинство визуальных компонентов: CdsApp, CdsLayout, CdsPage, CdsSurface, CdsCard, CdsDrawer, CdsModal, CdsOverlay, CdsButton, CdsField, CdsTable и другие.

Использование токенов темы в своём коде ​

Tailwind CSS v4 ​

Токены темы замаплены в @theme Tailwind, поэтому утилиты переключаются вместе с темой автоматически:

html
<div class="bg-surface-primary text-content-primary p-4 rounded-200">…</div>

Подключение описано в Tailwind CSS v4 + CDS 4.

CSS / SCSS ​

scss
.my-card {
  background: var(--cds-color-surface-elevated);
  color: var(--cds-color-content-primary);
  padding: var(--cds-spacing-500);
  border-radius: var(--cds-border-radius-300);
}

Переопределяя стили компонентов CDS, пишите их в слой cds-overrides — так они гарантированно выиграют у стилей библиотеки:

scss
@layer cds-overrides {
  .cds-button {
    border-radius: var(--cds-border-radius-300);
  }
}

Легаси-классы CDS 3 ​

Утилитарные классы CDS 3 (cds-bg-*, cds-text-*, cds-border-*) в CDS 4 выключены по умолчанию. Чтобы их вернуть на время миграции:

ts
import '@central-design-system/components/styles/legacy';

const cds = createCds({
  theme: { legacyClasses: true }
});

Отключение темизации ​

Передайте в опцию theme значение false. Это предотвратит генерацию CSS-переменных и классов тем и отключит возможность смены темы в рантайме — тег <style> вообще не будет создан.

ts
import { createCds } from '@central-design-system/components';

const cds = createCds({
  theme: false
});

При отключённой темизации theme.isDisabled === true, а themeClasses возвращает undefined.

Интерфейс инстанса темы ​

useTheme() возвращает ThemeInstance — доступ к текущей теме и её настройкам.

ts
interface ThemeInstance {
  /** Включена ли темизация (`false` при `theme: false`) */
  readonly isDisabled: boolean;

  /**
   * Raw-объект установленных тем
   * Можно добавлять и изменять темы
   */
  readonly themes: Ref<Record<string, InternalThemeDefinition>>;

  /**
   * Название текущей темы
   * Наследуется от родительского компонента
   */
  readonly name: Readonly<Ref<string>>;

  /**
   * Обработанный объект текущей темы
   * Включает уже автоматически сгенерированные цвета
   */
  readonly current: DeepReadonly<Ref<InternalThemeDefinition>>;
  readonly computedThemes: DeepReadonly<Ref<Record<string, InternalThemeDefinition>>>;

  /** Класс текущей темы — `cds-theme--<name>` */
  readonly themeClasses: Readonly<ComputedRef<string | undefined>>;

  /** Сгенерированный CSS текущей темы */
  readonly styles: Readonly<ComputedRef<string>>;

  readonly global: {
    /** Название текущей глобальной темы в приложении (writable) */
    readonly name: Ref<string>;

    /**
     * Обработанный объект текущей глобальной темы в приложении
     * Также можно получить из `theme.computedThemes.value[theme.global.name.value]`
     */
    readonly current: DeepReadonly<Ref<InternalThemeDefinition>>;
  };
}

Инстанс темы также доступен из объекта, возвращённого createCds() — это удобно вне контекста компонента:

ts
const cds = createCds({ theme: { defaultTheme: 'cdsLight' } });

cds.theme.global.name.value = 'cdsDark';