feat(core): theme service refactor (#DS-3003) - #1856
Conversation
|
Visit the preview URL for this PR (updated for commit 7211a09): https://koobiq-next--prs-1856-jmotwjcq.web.app (expires Mon, 10 Aug 2026 10:38:40 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: c9e37e518febda70d0317d07e8ceb35ac43c534c |
There was a problem hiding this comment.
Pull request overview
This PR refactors the core ThemeService (DS-3003) into a signal-based KbqThemeService. It introduces a built-in auto mode that follows the OS color scheme via matchMedia, adds out-of-the-box mode persistence through a swappable KBQ_THEME_STORE (default KbqThemeLocalStorageStore), and DI-based configuration via kbqThemeProvider()/KBQ_THEME_CONFIG. Backward compatibility is preserved: ThemeService remains as a deprecated alias, and current, KbqTheme.selected, setTheme()/getTheme() still work. Consumers across the docs app and docs-examples are migrated to the signal API, and the docs navbar's hand-rolled matchMedia/localStorage wiring is removed.
Changes:
- New signal-based
KbqThemeService(mode,resolvedMode,currentTheme,themes) withsetAuto()/toggle()/setMode(), internal OS-scheme handling, and DI config (kbqThemeProvider,KBQ_THEME_CONFIG,KBQ_THEME_STORE,KbqThemeLocalStorageStore). - Deprecated back-compat surface kept (
ThemeServicealias,current,selected,setTheme/getTheme); public API snapshot approved. - Migrated all consumers (navbar, welcome, docsearch, tokens-overview, theme-toggle, 7 docs-examples) to the signal API; added
theme.service.spec.tsand migration docs.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
packages/components/core/services/theme.service.ts |
New KbqThemeService, config/store tokens, deprecated ThemeService alias. |
packages/components/core/services/theme.service.spec.ts |
New unit tests for auto/mode/custom themes/persistence/SSR/deprecated shims. |
tools/public_api_guard/components/core.api.md |
Approved public API changes for the new/renamed symbols. |
apps/docs/src/app/config.ts |
Wires kbqThemeProvider({ storageKey: 'docs_theme' }) (see comment — format mismatch). |
apps/docs/src/app/components/navbar/navbar.component.ts / navbar.template.html |
Removes hand-rolled matchMedia/storage; drives dropdown off mode()/setMode(). |
apps/docs/src/app/components/welcome/welcome.component.ts |
Uses resolvedMode() computed instead of current observable. |
apps/docs/src/app/components/docsearch/docsearch.directive.ts |
Uses toObservable(resolvedMode) for the search theme. |
apps/docs/src/app/components/design-tokens-viewers/tokens-overview.ts / .spec.ts |
Recalculates via effect on resolvedMode(); test mocks matchMedia. |
packages/components-dev/theme-toggle.ts |
Dev toggle rewired to resolvedMode()/setMode() via effect. |
packages/docs-examples/** (7 files) |
Switched currentTheme to resolvedMode() computed; dropped unused rxjs imports. |
docs/guides/migration.en.md / migration.ru.md |
Adds a "13. Theme service review" migration section. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| private readonly storageKey = inject(KBQ_THEME_CONFIG).storageKey ?? KBQ_THEME_DEFAULT_CONFIG.storageKey; | ||
|
|
||
| getMode(): KbqThemeMode | string | null { | ||
| if (!this.isBrowser) return null; |
There was a problem hiding this comment.
зачем добавлять проверку isBrowser если ниже вызывается KBQ_WINDOW?
There was a problem hiding this comment.
Убрал, действительно не нужно
| }); | ||
|
|
||
| /** Configures `KbqThemeService` — registers custom themes, sets the initial mode, and how it's applied to the DOM. */ | ||
| export const kbqThemeProvider = (config: KbqThemeConfig): Provider => ({ |
There was a problem hiding this comment.
можно дать возможность пользователю переопределять только часть свойств
export const kbqThemeProvider = (config: Partial<KbqThemeConfig>): Provider => ({
provide: KBQ_THEME_CONFIG,
useValue: { ...KBQ_THEME_DEFAULT_CONFIG, ...config }
});в таком случае в коде ниже можно упростить проверки:
private readonly storageKey = inject(KBQ_THEME_CONFIG).storageKey;и здесь:
private readonly config: KbqThemeConfig<T> = inject(KBQ_THEME_CONFIG);| * (see `kbqThemeProvider()`). | ||
| */ | ||
| @Injectable({ providedIn: 'root' }) | ||
| export class KbqThemeLocalStorageStore implements KbqThemeStore { |
There was a problem hiding this comment.
для поддержки ssr можно было бы добавить сохранение текущей темы в cookie
| @@ -26,67 +50,221 @@ export enum KbqThemeSelector { | |||
| } | |||
|
|
|||
| export const KbqDefaultThemes: KbqTheme[] = [ | |||
There was a problem hiding this comment.
давай отметим как @docs-private, не совсем из кода понятно для чего эта константа
|
|
||
| current: BehaviorSubject<T> = new BehaviorSubject(null as T); | ||
| /** Currently selected mode. `'auto'` resolves to `light`/`dark` based on the OS color scheme. */ | ||
| readonly mode = signal<KbqThemeMode | string>(this.store.getMode() ?? this.config.mode); |
There was a problem hiding this comment.
почему mode в какой-то момент становится строкой? зачем в таком случае тип?
There was a problem hiding this comment.
я понимаю это так, что тема может быть кастомной, но мод может быть только 3х вариантов
| /** A theme registered with `KbqThemeService`. */ | ||
| export interface KbqTheme { | ||
| /** Unique name used to select the theme via `setMode()`. */ | ||
| name: string; |
There was a problem hiding this comment.
кажется что это не очень идея смешивать mode и name для темы, убиваем типизацию таким образом
mode будет полезен для настройки https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/light-dark например, если у него будет строгий тип
There was a problem hiding this comment.
Добавил colorScheme - отдельный сигнал для этого
| /** Registers a custom set of themes. */ | ||
| setThemes(items: T[]) { | ||
| this.themes = items; | ||
| this.themes.set(items); |
There was a problem hiding this comment.
было бы удобно настраивать список тем при помощи provider, чтобы при инициализации приложения они уже были доступны
Summary
Refactors
ThemeService(DS-3003): moves it to signals, adds a built-inautomode that follows the OS color scheme, and persists the selected mode tolocalStorageout of the box.ThemeServiceis kept as a deprecated alias so existing consumers don't break, and the newKbqThemeServiceis fully DI-configurable viakbqThemeProvider().List of notable changes
KbqThemeService— signal-based (mode,resolvedMode,currentTheme,themes) replacement forThemeService, withsetAuto()/toggle(), internalmatchMediahandling forautomode, andlocalStoragepersistence via a new swappableKBQ_THEME_STORE/KbqThemeLocalStorageStore(same pattern asKBQ_ACCORDION_STATE_STORE)kbqThemeProvider(config)/KBQ_THEME_CONFIGfor DI-based setup (themes,mode,storageKey,autoLight,autoDark) instead of imperativesetThemes()/setTheme()callsautoLight/autoDarkconfig soautomode resolves correctly against fully custom theme sets, not just the built-inlight/darknamesThemeService(deprecated alias ofKbqThemeService),KbqTheme.selected(deprecated, still synced), andcurrent/setTheme()/getTheme()(deprecated, still functional) for backward compatibility — nong updateschematic needed for the renamematchMedialistener andlocalStoragewiring innavbar.component.ts/navbar-property.ts— now just callssetAuto()/setMode(); configured withkbqThemeProvider({ storageKey: 'docs_theme' })inapps/docs/src/app/config.tsto preserve existing users' saved preference under the old keyThemeServiceconsumers (theme-toggle.ts,welcome.component.ts,tokens-overview.ts,docsearch.directive.ts, 7 docs-examples) to the signal APItheme.service.spec.ts— unit tests forautoresolution, mode selection, custom themes, persistence, SSR-safety, and the deprecated shimsdocs/guides/migration.en.md/migration.ru.mdwith a new "13. Theme service review (20.3.0)" section, and approved thecore.api.mdpublic API snapshotWhat should reviewers focus on?
ThemeService,current,selected,setTheme/getTheme) is worth keeping vs. a cleaner breakautoLight/autoDarkas the mechanism for custom themes to opt intoautomode — reasonable default, or should it be required when custom themes are registered?