Тематизация
Дизайн-система поддерживает смену темы или настройку своей собственной темы программно, в том числе прямо в рантайме.
Как это устроено
Тема в CDS 4 — это набор семантических токенов, из которых при старте приложения генерируются CSS-переменные:
ThemeDefinition (TS) → --cds-color-* / --cds-* (CSS-переменные) → стили компонентов
↓
@theme (Tailwind CSS v4) → bg-surface-primary, rounded-200, …В переменных лежат готовые цвета — ровно в том виде, в каком они записаны в теме. Встроенные темы пишут цвета в oklch(). Прозрачность поверх токена задаётся через color-mix():
/* сгенерировано 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:
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);Опции темизации
createCds({
theme: {
defaultTheme: 'cdsLight',
prefix: 'cds',
themes: {},
variations: false,
utilities: true,
legacyClasses: false,
scoped: false,
scope: undefined,
stylesheetId: 'cds-theme-stylesheet',
cspNonce: undefined
}
});| Опция | Тип | По умолчанию | Назначение |
|---|---|---|---|
defaultTheme | ThemeName | string | 'cdsLight' | Активная тема при старте |
prefix | string | 'cds' | Префикс генерируемых CSS-переменных (--cds-color-*) |
themes | Record<string, ThemeDefinition> | штатные темы | Свои темы; мержатся со штатными |
variations | false | { colors: string[]; lighten: number; darken: number } | false | Автогенерация светлых/тёмных вариаций цветов |
utilities | boolean | true | Генерация утилитарных классов фона/текста по токенам темы |
legacyClasses | boolean | false | Легаси-классы CDS 3 (cds-bg-*, cds-text-*, cds-border-*) |
scoped | boolean | false | Не писать переменные в :root |
scope | string | — | Ограничить селекторы темы поддеревом (:where(<scope>)) |
stylesheetId | string | 'cds-theme-stylesheet' | id тега <style> с темой |
cspNonce | string | — | nonce для Content Security Policy |
Создание своей темы
Своя тема — это объект ThemeDefinition с полями dark, colors и variables. Указывать нужно только переопределения: недостающие токены наследуются из cdsLight / cdsDark.
Цвет можно записать в hex, rgb() или oklch() — в CSS-переменную значение попадёт как написано. Контрастный текст on-* и вариации CDS рассчитает для любого из этих форматов.
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:
<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. Учитывайте, что тема применится не только к самому компоненту, но и ко всем вложенным в него компонентам:
<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, поэтому утилиты переключаются вместе с темой автоматически:
<div class="bg-surface-primary text-content-primary p-4 rounded-200">…</div>Подключение описано в Tailwind CSS v4 + CDS 4.
CSS / 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 — так они гарантированно выиграют у стилей библиотеки:
@layer cds-overrides {
.cds-button {
border-radius: var(--cds-border-radius-300);
}
}Легаси-классы CDS 3
Утилитарные классы CDS 3 (cds-bg-*, cds-text-*, cds-border-*) в CDS 4 выключены по умолчанию. Чтобы их вернуть на время миграции:
import '@central-design-system/components/styles/legacy';
const cds = createCds({
theme: { legacyClasses: true }
});Отключение темизации
Передайте в опцию theme значение false. Это предотвратит генерацию CSS-переменных и классов тем и отключит возможность смены темы в рантайме — тег <style> вообще не будет создан.
import { createCds } from '@central-design-system/components';
const cds = createCds({
theme: false
});При отключённой темизации theme.isDisabled === true, а themeClasses возвращает undefined.
Интерфейс инстанса темы
useTheme() возвращает ThemeInstance — доступ к текущей теме и её настройкам.
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() — это удобно вне контекста компонента:
const cds = createCds({ theme: { defaultTheme: 'cdsLight' } });
cds.theme.global.name.value = 'cdsDark';