diff --git a/angular.json b/angular.json index 33c08f198..06233e750 100644 --- a/angular.json +++ b/angular.json @@ -22,7 +22,12 @@ ], "assets": [ "src/assets", - "src/favicon.ico" + "src/favicon.ico", + { + "glob": "igniteui-{angular,angular-dark,fluent-light,fluent-dark,bootstrap-light,bootstrap-dark,indigo-light,indigo-dark}.css", + "input": "node_modules/igniteui-angular/styles", + "output": "assets/themes" + } ], "styles": [ "src/styles.scss" diff --git a/live-editing/configs/InputGroupConfigGenerator.ts b/live-editing/configs/InputGroupConfigGenerator.ts index 068c11973..7f083c345 100644 --- a/live-editing/configs/InputGroupConfigGenerator.ts +++ b/live-editing/configs/InputGroupConfigGenerator.ts @@ -18,9 +18,6 @@ export class InputGroupConfigGenerator implements IConfigGenerator { configs.push(new Config({ component: 'InputGroupSample1Component', - additionalFiles: [ - "/src/app/data-entries/input-group/base-input.component.ts" - ], appConfig: BaseAppConfig, shortenComponentPathBy: "/data-entries/input-group/" })); @@ -28,9 +25,6 @@ export class InputGroupConfigGenerator implements IConfigGenerator { // input group sample 2 configs.push(new Config({ component: 'InputGroupSample2Component', - additionalFiles: [ - "/src/app/data-entries/input-group/base-input.component.ts" - ], appConfig: BaseAppConfig, shortenComponentPathBy: "/data-entries/input-group/" })); @@ -38,9 +32,6 @@ export class InputGroupConfigGenerator implements IConfigGenerator { // input group sample 5 configs.push(new Config({ component: 'InputGroupSample5Component', - additionalFiles: [ - "/src/app/data-entries/input-group/base-input.component.ts" - ], appConfig: BaseAppConfig, shortenComponentPathBy: "/data-entries/input-group/" })); @@ -48,16 +39,13 @@ export class InputGroupConfigGenerator implements IConfigGenerator { // reactive forms configs.push(new Config({ component: 'ReactiveFormsSampleComponent', - additionalFiles: [ - "/src/app/data-entries/input-group/base-input.component.ts" - ], appConfig: BaseAppConfig, shortenComponentPathBy: "/data-entries/" })); configs.push(new Config({ component: 'InputGroupStyleComponent', - additionalFiles: ["/src/app/data-entries/input-group/base-input.component.ts", "/src/app/data-entries/input-group/input-group-styling/layout.scss"], + additionalFiles: ["/src/app/data-entries/input-group/input-group-styling/layout.scss"], appConfig: BaseAppConfig, shortenComponentPathBy: "/data-entries/" })); @@ -71,9 +59,6 @@ export class InputGroupConfigGenerator implements IConfigGenerator { // template-driven form validation configs.push(new Config({ component: 'TemplateDrivenFormValidationComponent', - additionalFiles: [ - "/src/app/data-entries/input-group/base-input.component.ts" - ], appConfig: BaseAppConfig, shortenComponentPathBy: "/data-entries/" })); @@ -81,9 +66,6 @@ export class InputGroupConfigGenerator implements IConfigGenerator { // reactive form validation configs.push(new Config({ component: 'ReactiveFormValidationComponent', - additionalFiles: [ - "/src/app/data-entries/input-group/base-input.component.ts" - ], appConfig: BaseAppConfig, shortenComponentPathBy: "/data-entries/" })); @@ -91,9 +73,6 @@ export class InputGroupConfigGenerator implements IConfigGenerator { // reactive form custom validation configs.push(new Config({ component: 'ReactiveFormCustomValidationComponent', - additionalFiles: [ - "/src/app/data-entries/input-group/base-input.component.ts" - ], appConfig: BaseAppConfig, shortenComponentPathBy: "/data-entries/" })); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 104654744..10970e42e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,6 +1,7 @@ import { Component, HostListener, OnInit, DOCUMENT, inject } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { ThemeService } from './services/theme.service'; @Component({ selector: 'app-root', @@ -10,50 +11,22 @@ import { RouterOutlet } from '@angular/router'; }) export class AppComponent implements OnInit { private document = inject(DOCUMENT); + private themeService = inject(ThemeService); public title = 'Samples'; - private theme = 'default-theme'; - private styleElem: HTMLStyleElement; - private typefacesLoaded = ['Titillium Web', 'Roboto']; - private typefaceUrl = 'https://fonts.googleapis.com/css?family='; public ngOnInit() { - this.createThemeStyle(); + this.document.body.classList.add('custom-body'); + this.themeService.init(); } - // eslint-disable-next-line @typescript-eslint/member-ordering + /** + * Samples are embedded in the documentation as iframes and the theming + * widget shown next to each of them posts the visitor's theme selection + * down into the sample. Anything that is not such a message is left alone. + */ @HostListener('window:message', ['$event']) protected onMessage(e: MessageEvent) { - if (e.origin === e.data.origin && typeof e.data.themeStyle === 'string') { - this.styleElem.textContent = e.data.themeStyle; - const typeface = window.getComputedStyle(this.document.body).fontFamily.replace(/[\'\"]/g, ''); - if (!(typeface.match(/,/g) || []).length && - !this.typefacesLoaded.includes(typeface)) { - this.typefacesLoaded.push(typeface); - this.createTypefaceLink(typeface); - } - } else if (e.origin === e.data.origin && typeof e.data.theme === 'string') { - this.document.body.classList.remove(this.theme); - this.document.body.classList.add(e.data.theme); - this.theme = e.data.theme; - } - } - - private createTypefaceLink(typeface: string) { - let typefaceElem = this.document.getElementById('igniteui-theme-typeface') as HTMLLinkElement; - if (!typefaceElem) { - typefaceElem = this.document.createElement('link'); - typefaceElem.rel = 'stylesheet'; - typefaceElem.id = 'igniteui-theme-typeface'; - this.document.head.appendChild(typefaceElem); - } - typefaceElem.href = this.typefaceUrl + typeface.split(' ').join('+'); - } - - private createThemeStyle() { - this.styleElem = this.document.createElement('style'); - this.styleElem.id = 'igniteui-theme'; - this.document.head.appendChild(this.styleElem); - this.document.body.classList.add('custom-body'); + this.themeService.handleMessage(e); } } diff --git a/src/app/data-entries/input-group/base-input.component.ts b/src/app/data-entries/input-group/base-input.component.ts deleted file mode 100644 index f329a3291..000000000 --- a/src/app/data-entries/input-group/base-input.component.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Directive, HostListener, QueryList, ViewChildren } from '@angular/core'; -import { IgxInputGroupComponent } from 'igniteui-angular/input-group'; - -@Directive({ - // eslint-disable-next-line @angular-eslint/directive-selector - selector: 'input-group-sample-base' -}) -// eslint-disable-next-line @angular-eslint/directive-class-suffix -export class BaseInputGroupSampleComponent { - @ViewChildren(IgxInputGroupComponent) public inputGroups: QueryList; - public type = null; - - @HostListener('window:message', ['$event']) - protected onMessage(e: MessageEvent) { - if (e.origin === e.data.origin && e.data.themeName) { - this.applyInputTypeForTheme(e.data.themeName); - } - } - - private applyInputTypeForTheme(theme: string) { - const themeName = theme.split('-')[1]; - switch (themeName) { - case 'theme': this.setInputType('line'); break; - case 'fluent': this.setInputType('fluent'); break; - case 'bootstrap': this.setInputType('bootstrap'); break; - default: break; - } - } - - private setInputType(type) { - if (this.inputGroups && this.inputGroups.length > 0) { - this.inputGroups.forEach(x => { - if (!(x.isTypeBox || x.isTypeBorder || x.isTypeSearch)) { - x.type = type; - } - }); - } - } -} diff --git a/src/app/data-entries/input-group/input-group-sample-1/input-group-sample-1.component.ts b/src/app/data-entries/input-group/input-group-sample-1/input-group-sample-1.component.ts index 1c0931481..5009fa11a 100644 --- a/src/app/data-entries/input-group/input-group-sample-1/input-group-sample-1.component.ts +++ b/src/app/data-entries/input-group/input-group-sample-1/input-group-sample-1.component.ts @@ -1,5 +1,4 @@ import { Component } from '@angular/core'; -import { BaseInputGroupSampleComponent } from '../base-input.component'; import { FormsModule } from '@angular/forms'; import { IgxInputDirective, IgxInputGroupComponent, IgxLabelDirective } from 'igniteui-angular/input-group'; @@ -9,4 +8,4 @@ import { IgxInputDirective, IgxInputGroupComponent, IgxLabelDirective } from 'ig templateUrl: './input-group-sample-1.component.html', imports: [FormsModule, IgxInputGroupComponent, IgxInputDirective, IgxLabelDirective] }) -export class InputGroupSample1Component extends BaseInputGroupSampleComponent { } +export class InputGroupSample1Component { } diff --git a/src/app/data-entries/input-group/input-group-sample-2/input-group-sample-2.component.ts b/src/app/data-entries/input-group/input-group-sample-2/input-group-sample-2.component.ts index 42124f2ff..41ab30601 100644 --- a/src/app/data-entries/input-group/input-group-sample-2/input-group-sample-2.component.ts +++ b/src/app/data-entries/input-group/input-group-sample-2/input-group-sample-2.component.ts @@ -1,5 +1,4 @@ import { Component } from '@angular/core'; -import { BaseInputGroupSampleComponent } from '../base-input.component'; import { FormsModule } from '@angular/forms'; import { IgxInputDirective, IgxInputGroupComponent, IgxLabelDirective } from 'igniteui-angular/input-group'; @@ -9,4 +8,4 @@ import { IgxInputDirective, IgxInputGroupComponent, IgxLabelDirective } from 'ig templateUrl: './input-group-sample-2.component.html', imports: [FormsModule, IgxInputGroupComponent, IgxInputDirective, IgxLabelDirective] }) -export class InputGroupSample2Component extends BaseInputGroupSampleComponent { } +export class InputGroupSample2Component { } diff --git a/src/app/data-entries/input-group/input-group-sample-3/input-group-sample-3.component.ts b/src/app/data-entries/input-group/input-group-sample-3/input-group-sample-3.component.ts index dca99a7b2..f90dbf292 100644 --- a/src/app/data-entries/input-group/input-group-sample-3/input-group-sample-3.component.ts +++ b/src/app/data-entries/input-group/input-group-sample-3/input-group-sample-3.component.ts @@ -1,5 +1,4 @@ import { Component } from '@angular/core'; -import { BaseInputGroupSampleComponent } from '../base-input.component'; import { FormsModule } from '@angular/forms'; import { IgxInputDirective, IgxInputGroupComponent, IgxLabelDirective, IgxPrefixDirective, IgxSuffixDirective } from 'igniteui-angular/input-group'; import { IgxIconComponent } from 'igniteui-angular/icon'; @@ -10,4 +9,4 @@ import { IgxIconComponent } from 'igniteui-angular/icon'; templateUrl: './input-group-sample-3.component.html', imports: [FormsModule, IgxInputGroupComponent, IgxPrefixDirective, IgxLabelDirective, IgxInputDirective, IgxIconComponent, IgxSuffixDirective] }) -export class InputGroupSample3Component extends BaseInputGroupSampleComponent { } +export class InputGroupSample3Component { } diff --git a/src/app/data-entries/input-group/input-group-sample-4/input-group-sample-4.component.ts b/src/app/data-entries/input-group/input-group-sample-4/input-group-sample-4.component.ts index f6d6c66cc..8ceed9523 100644 --- a/src/app/data-entries/input-group/input-group-sample-4/input-group-sample-4.component.ts +++ b/src/app/data-entries/input-group/input-group-sample-4/input-group-sample-4.component.ts @@ -1,5 +1,4 @@ import { Component, OnInit } from '@angular/core'; -import { BaseInputGroupSampleComponent } from '../base-input.component'; import { FormsModule } from '@angular/forms'; import { IgxHintDirective, IgxInputDirective, IgxInputGroupComponent, IgxLabelDirective, IgxPrefixDirective, IgxSuffixDirective } from 'igniteui-angular/input-group'; import { IgxIconComponent } from 'igniteui-angular/icon'; @@ -10,4 +9,4 @@ import { IgxIconComponent } from 'igniteui-angular/icon'; templateUrl: './input-group-sample-4.component.html', imports: [FormsModule, IgxInputGroupComponent, IgxPrefixDirective, IgxLabelDirective, IgxInputDirective, IgxSuffixDirective, IgxIconComponent, IgxHintDirective] }) -export class InputGroupSample4Component extends BaseInputGroupSampleComponent { } +export class InputGroupSample4Component { } diff --git a/src/app/data-entries/input-group/input-group-sample-5/input-group-sample-5.component.ts b/src/app/data-entries/input-group/input-group-sample-5/input-group-sample-5.component.ts index 569216794..0164e575f 100644 --- a/src/app/data-entries/input-group/input-group-sample-5/input-group-sample-5.component.ts +++ b/src/app/data-entries/input-group/input-group-sample-5/input-group-sample-5.component.ts @@ -1,5 +1,4 @@ import { Component } from '@angular/core'; -import { BaseInputGroupSampleComponent } from '../base-input.component'; import { IGX_INPUT_GROUP_TYPE, IgxHintDirective, IgxInputDirective, IgxInputGroupComponent, IgxLabelDirective, IgxPrefixDirective, IgxSuffixDirective } from 'igniteui-angular/input-group'; import { IgxIconComponent } from 'igniteui-angular/icon'; import { FormsModule } from '@angular/forms'; @@ -12,4 +11,4 @@ import { FormsModule } from '@angular/forms'; providers: [{ provide: IGX_INPUT_GROUP_TYPE, useValue: 'box' }], imports: [FormsModule, IgxInputGroupComponent, IgxInputDirective, IgxLabelDirective, IgxSuffixDirective, IgxIconComponent, IgxHintDirective, IgxPrefixDirective] }) -export class InputGroupSample5Component extends BaseInputGroupSampleComponent { } +export class InputGroupSample5Component { } diff --git a/src/app/data-entries/input-group/input-group-sample-6/input-group-sample-6.component.ts b/src/app/data-entries/input-group/input-group-sample-6/input-group-sample-6.component.ts index b2ca7951e..97c99f079 100644 --- a/src/app/data-entries/input-group/input-group-sample-6/input-group-sample-6.component.ts +++ b/src/app/data-entries/input-group/input-group-sample-6/input-group-sample-6.component.ts @@ -1,5 +1,4 @@ import { Component } from '@angular/core'; -import { BaseInputGroupSampleComponent } from '../base-input.component'; import { FormsModule } from '@angular/forms'; import { IgxSelectComponent, IgxSelectGroupComponent, IgxSelectItemComponent, IgxSelectToggleIconDirective } from 'igniteui-angular/select'; import { IgxHintDirective, IgxInputDirective, IgxInputGroupComponent, IgxLabelDirective, IgxPrefixDirective, IgxSuffixDirective } from 'igniteui-angular/input-group'; @@ -16,7 +15,7 @@ import { IgxButtonDirective, IgxRippleDirective } from 'igniteui-angular/directi templateUrl: './input-group-sample-6.component.html', imports: [FormsModule, IgxSelectComponent, IgxSelectGroupComponent, IgxSelectItemComponent, IgxLabelDirective, IgxSelectToggleIconDirective, IgxIconComponent, IgxInputGroupComponent, IgxInputDirective, IgxSuffixDirective, IgxPrefixDirective, IgxHintDirective, IgxComboComponent, IgxDatePickerComponent, IgxPickerToggleComponent, IgxTimePickerComponent, IgxButtonDirective, IgxRippleDirective] }) -export class InputGroupSample6Component extends BaseInputGroupSampleComponent { +export class InputGroupSample6Component { public user = { dateTime: new Date(), email: undefined, diff --git a/src/app/services/theme.service.ts b/src/app/services/theme.service.ts new file mode 100644 index 000000000..2887b3d93 --- /dev/null +++ b/src/app/services/theme.service.ts @@ -0,0 +1,231 @@ +import { isPlatformBrowser } from '@angular/common'; +import { DOCUMENT, Injectable, PLATFORM_ID, inject, signal } from '@angular/core'; +import { THEME_TOKEN, type IgxTheme } from 'igniteui-angular/core'; + +/** + * Message type posted by the documentation site's theming widget into the + * sample iframe. See `ThemeService.handleMessage`. + */ +export const THEME_MESSAGE = 'igd-sample-theme'; + +/** Color modes the theming widget can request. */ +export type ThemeMode = 'light' | 'dark' | 'system'; + +/** `system` resolved against the OS preference. */ +export type ColorScheme = 'light' | 'dark'; + +/** + * Prebuilt Ignite UI stylesheets, one per theme/scheme pair. They are copied + * out of `igniteui-angular/styles` into `assets/themes` at build time - see the + * `assets` section of angular.json. + */ +const THEME_STYLESHEETS: Record> = { + material: { light: 'igniteui-angular.css', dark: 'igniteui-angular-dark.css' }, + fluent: { light: 'igniteui-fluent-light.css', dark: 'igniteui-fluent-dark.css' }, + bootstrap: { light: 'igniteui-bootstrap-light.css', dark: 'igniteui-bootstrap-dark.css' }, + indigo: { light: 'igniteui-indigo-light.css', dark: 'igniteui-indigo-dark.css' } +}; + +const THEMES_PATH = 'assets/themes/'; + +/** Id of the `` in index.html that carries the active theme. */ +const THEME_LINK_ID = 'igniteui-theme'; + +const TYPEFACE_URL = 'https://fonts.googleapis.com/css?family='; +const TYPEFACE_WEIGHTS = ':300,400,600,700'; + +/** + * Font families that never need to be fetched: generic CSS keywords and the + * system fonts the Bootstrap theme is built on. + */ +const LOCAL_FAMILIES = new Set([ + 'sans-serif', 'serif', 'monospace', 'cursive', 'fantasy', + 'system-ui', 'ui-sans-serif', 'ui-serif', 'ui-monospace', + 'segoe ui', 'helvetica', 'helvetica neue', 'arial', 'roboto' +]); + +const isTheme = (value: unknown): value is IgxTheme => + typeof value === 'string' && value in THEME_STYLESHEETS; + +const isMode = (value: unknown): value is ThemeMode => + value === 'light' || value === 'dark' || value === 'system'; + +/** + * Swaps the Ignite UI theme of the samples application at runtime. + * + * Samples are embedded in the documentation as iframes and the site's theming + * widget posts the visitor's selection into them, so the whole application has + * to be able to move between the Material, Fluent, Bootstrap and Indigo themes + * (each in a light and a dark variant) without reloading. That is done by + * pointing a single `` at one of the prebuilt stylesheets instead of + * compiling a single theme into `styles.scss`. + */ +@Injectable({ providedIn: 'root' }) +export class ThemeService { + private document = inject(DOCUMENT); + private themeToken = inject(THEME_TOKEN); + private isBrowser = isPlatformBrowser(inject(PLATFORM_ID)); + + /** The selected theme. */ + public readonly theme = signal('material'); + + /** The selected color mode, `system` included. */ + public readonly mode = signal('light'); + + /** The color mode actually in effect, with `system` already resolved. */ + public readonly colorScheme = signal('light'); + + private prefersDark: MediaQueryList; + private typefaces = new Set(); + + /** + * Applies the initial theme and starts following the OS color preference. + * Safe to call more than once. + */ + public init(): void { + if (this.isBrowser && !this.prefersDark) { + this.prefersDark = this.document.defaultView?.matchMedia('(prefers-color-scheme: dark)'); + // Only relevant while the widget is on `system`; `apply` ignores it otherwise. + this.prefersDark?.addEventListener('change', () => this.apply()); + } + + this.apply(); + } + + /** + * Handles a `message` event coming from the documentation site. + * + * The widget posts `{ type: 'igd-sample-theme', theme, themeName, mode }` + * once when the iframe loads and again on every change. The message is + * trusted for its shape rather than for its origin - the documentation is + * served from a number of hosts, and the only thing acted upon is a choice + * between the four known themes and the three known modes. + * + * @returns whether the message was recognized and applied. + */ + public handleMessage(event: MessageEvent): boolean { + const data = event.data; + + if (!data || (data.type !== THEME_MESSAGE && data.event !== THEME_MESSAGE)) { + return false; + } + + return this.set(data.theme ?? data.themeName, data.mode); + } + + /** + * Switches to `theme` / `mode`. Unknown or missing values leave the + * corresponding part of the selection untouched. + * + * @returns whether anything was recognized. + */ + public set(theme?: unknown, mode?: unknown): boolean { + let known = false; + + if (isTheme(theme)) { + this.theme.set(theme); + known = true; + } + + if (isMode(mode)) { + this.mode.set(mode); + known = true; + } + + if (known) { + this.apply(); + } + + return known; + } + + private apply(): void { + if (!this.isBrowser) { + return; + } + + const theme = this.theme(); + const scheme = this.resolveScheme(); + this.colorScheme.set(scheme); + + // Exposed for samples that need to react to the selection. Deliberately + // not used to paint a page background: the documentation's Sample + // component owns the surface behind the iframe. + const root = this.document.documentElement; + root.dataset.igTheme = theme; + root.dataset.igMode = scheme; + + const href = THEMES_PATH + THEME_STYLESHEETS[theme][scheme]; + const link = this.themeLink(); + + if (!link.getAttribute('href')?.endsWith(href)) { + link.addEventListener('load', () => this.loadTypeface(), { once: true }); + link.href = href; + } + + // Components such as igx-input-group render differently per theme and + // take the theme from this token rather than from the stylesheet. + this.themeToken.set(theme); + } + + private resolveScheme(): ColorScheme { + const mode = this.mode(); + + if (mode !== 'system') { + return mode; + } + + return this.prefersDark?.matches ? 'dark' : 'light'; + } + + private themeLink(): HTMLLinkElement { + let link = this.document.getElementById(THEME_LINK_ID) as HTMLLinkElement; + + if (!link) { + link = this.document.createElement('link'); + link.id = THEME_LINK_ID; + link.rel = 'stylesheet'; + // Ahead of everything else in the head, so that the application's + // own styles keep overriding the theme as they do in index.html. + this.document.head.insertBefore(link, this.document.head.firstChild); + } + + return link; + } + + /** + * Each theme comes with its own typeface. Once a theme stylesheet is in + * place, take the first family off `--ig-font-family` and pick it up from + * Google Fonts unless it is one the browser already has. + */ + private loadTypeface(): void { + const view = this.document.defaultView; + + if (!view) { + return; + } + + const family = view.getComputedStyle(this.document.body) + .getPropertyValue('--ig-font-family') + .split(',')[0] + .trim() + .replace(/['"]/g, ''); + + if (!family || family.startsWith('-') || LOCAL_FAMILIES.has(family.toLowerCase())) { + return; + } + + if (this.typefaces.has(family)) { + return; + } + + this.typefaces.add(family); + + // A link per family, so that switching back to an earlier theme does + // not have to fetch its typeface again. + const link = this.document.createElement('link'); + link.rel = 'stylesheet'; + link.href = TYPEFACE_URL + family.split(' ').join('+') + TYPEFACE_WEIGHTS; + this.document.head.appendChild(link); + } +} diff --git a/src/index.html b/src/index.html index 9920eced3..65e26031d 100644 --- a/src/index.html +++ b/src/index.html @@ -12,6 +12,12 @@ + + + diff --git a/src/styles.scss b/src/styles.scss index 8c144ff6d..03a2dd70b 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -5,6 +5,14 @@ @use 'app-layout'; @import "tailwindcss"; +// The Ignite UI theme itself is deliberately NOT compiled into this file. Every +// sample is embedded in the documentation next to a theming widget that can +// switch it between the Material, Fluent, Bootstrap and Indigo themes in light +// or dark, so the theme is loaded as a swappable stylesheet instead - see the +// `igniteui-theme` link in index.html, the `assets/themes` entry in angular.json +// and `src/app/services/theme.service.ts`. A theme compiled in here would sit +// later in the cascade and win over the one the visitor picked. + // Specifies large size for all components to match the previous defaults // This may not be needed for your project. Please consult https://www.infragistics.com/products/ignite-ui-angular/angular/components/general/update-guide for more details. :root { @@ -19,6 +27,13 @@ body { margin: 0; } +// Nothing here paints a page background per color mode on purpose. The sample +// is embedded in an iframe and the documentation's Sample component draws the +// surface behind it from the same mode the theming widget posts down here, so +// the iframe is left transparent for it to show through. `ThemeService` still +// reflects `data-ig-theme` / `data-ig-mode` onto the root element for samples +// that need to react to the selection themselves. + body.custom-body:has( app-badge-overview, app-badge-styling, @@ -34,16 +49,8 @@ body.custom-body:has( background: transparent; } -@include core(); -@include typography(); -@include theme( - $palette: $palette, - $schema: $schema -); - -:root { - @include palette($palette); -} +// `core()`, `typography()`, `theme()` and the root palette all ship inside the +// prebuilt stylesheets loaded through the `igniteui-theme` link (see above). .light-theme { @include light-theme($palette);