fix: Theme class names for embed elements (#21275)

* refactor(embed-core): update theme class names for consistency

Standardize theme class names to use 'cal-element-embed-dark' and 'cal-element-embed-light' to improve maintainability and clarity.

* refactor(embed-core): replace theme class strings with constants

* fix theme not changing

---------

Co-authored-by: Tushar Bhatt <95581504+TusharBhatt1@users.noreply.github.com>
Co-authored-by: Hariom <hariombalhara@gmail.com>
This commit is contained in:
Rajdeep Das
2025-06-24 07:26:48 +00:00
committed by GitHub
co-authored by Tushar Bhatt Hariom
parent c215dc5864
commit e22d9a551c
5 changed files with 32 additions and 18 deletions
@@ -6,6 +6,7 @@ import type { Mock } from "vitest";
import { EmbedElement } from "./EmbedElement";
import inlineHTML from "./Inline/inlineHtml";
import { EMBED_DARK_THEME_CLASS, EMBED_LIGHT_THEME_CLASS } from "./constants";
import { getColorSchemeDarkQuery } from "./ui-utils";
(function defineEmbedTestElement() {
@@ -269,8 +270,8 @@ describe("EmbedElement", () => {
},
getSkeletonData: vi.fn(),
});
expect(element.classList.contains("dark")).toBe(true);
expect(element.classList.contains("light")).toBe(false);
expect(element.classList.contains(EMBED_DARK_THEME_CLASS)).toBe(true);
expect(element.classList.contains(EMBED_LIGHT_THEME_CLASS)).toBe(false);
});
it("should update theme class when system preference changes as long as embed theme is not set", () => {
@@ -279,8 +280,8 @@ describe("EmbedElement", () => {
});
getColorSchemeDarkQuery().dispatchEvent(buildMediaQueryListEvent({ type: "change", matches: true }));
expect(element.classList.contains("dark")).toBe(true);
expect(element.classList.contains("light")).toBe(false);
expect(element.classList.contains(EMBED_DARK_THEME_CLASS)).toBe(true);
expect(element.classList.contains(EMBED_LIGHT_THEME_CLASS)).toBe(false);
});
it("should not update theme on system color scheme change when embed theme is set", () => {
@@ -291,7 +292,7 @@ describe("EmbedElement", () => {
getSkeletonData: vi.fn(),
});
getColorSchemeDarkQuery().dispatchEvent(buildMediaQueryListEvent({ type: "change", matches: true }));
expect(element.classList.contains("light")).toBe(true);
expect(element.classList.contains(EMBED_LIGHT_THEME_CLASS)).toBe(true);
});
});
+16 -11
View File
@@ -1,3 +1,4 @@
import { EMBED_DARK_THEME_CLASS, EMBED_LIGHT_THEME_CLASS } from "./constants";
import type { EmbedThemeConfig, AllPossibleLayouts, BookerLayouts, EmbedPageType } from "./types";
import {
getThemeClassForEmbed,
@@ -157,9 +158,7 @@ export class EmbedElement extends HTMLElement {
this.isModal = data.isModal;
this.layout = this.getLayout();
this.theme = this.dataset.theme as EmbedThemeConfig | null;
this.themeClass = getThemeClassForEmbed({ theme: this.theme });
this.classList.add(this.themeClass);
this.setTheme(this.theme);
this.getSkeletonData = data.getSkeletonData;
this.boundResizeHandler = this.resizeHandler.bind(this);
this.boundPrefersDarkThemeChangedHandler = this.prefersDarkThemeChangedHandler.bind(this);
@@ -197,20 +196,26 @@ export class EmbedElement extends HTMLElement {
skeletonEl.innerHTML = skeletonContent;
}
public setTheme(theme: EmbedThemeConfig | null) {
const allPossibleThemeClasses = [EMBED_DARK_THEME_CLASS, EMBED_LIGHT_THEME_CLASS];
const newThemeClass = getThemeClassForEmbed({ theme });
if (newThemeClass === this.themeClass) {
return;
}
this.themeClass = newThemeClass;
this.classList.remove(...allPossibleThemeClasses);
this.classList.add(this.themeClass);
}
public prefersDarkThemeChangedHandler(e: MediaQueryListEvent) {
const isDarkPreferred = e.matches;
const allPossibleThemeClasses = ["dark", "light"];
if (isThemePreferenceProvided(this.theme)) {
// User has provided a theme preference, so we stick to that and don't react to system theme change
return;
}
const newThemeClass = getThemeClassForEmbed({
theme: isDarkPreferred ? "dark" : "light",
});
if (newThemeClass !== this.themeClass) {
this.classList.remove(...allPossibleThemeClasses);
this.classList.add(newThemeClass);
}
this.setTheme(isDarkPreferred ? "dark" : "light");
}
connectedCallback() {
// Make sure to show the loader initially.
@@ -1,3 +1,5 @@
import { EMBED_DARK_THEME_CLASS } from "./constants";
export const addAppCssVars = () => {
const calElements = ["cal-floating-button", "cal-modal-box", "cal-inline"];
try {
@@ -148,7 +150,7 @@ export const addAppCssVars = () => {
--cal-brand-text: hsla(0, 0%, 100%, 1); /* white */
}
${calElements.map((element) => `${element}.dark`).join(",")} {
${calElements.map((element) => `${element}.${EMBED_DARK_THEME_CLASS}`).join(",")} {
/* Background Standard */
--cal-bg-emphasis: hsla(0, 0%, 25%, 1); /* stone-700 */
--cal-bg: hsla(0, 0%, 6%, 1); /* stone-950 */
@@ -10,3 +10,8 @@ if (EMBED_MODAL_IFRAME_SLOT_STALE_TIME > EMBED_MODAL_IFRAME_FORCE_RELOAD_THRESHO
"EMBED_MODAL_IFRAME_SLOT_STALE_TIME must be less than EMBED_MODAL_IFRAME_FORCE_RELOAD_THRESHOLD_MS"
);
}
// These classes are applied to Embed Elements and thus are in the same scope as the embedding webpage.
// So, these classes need to be unique to us, to avoid accidental override
export const EMBED_LIGHT_THEME_CLASS = "cal-element-embed-light";
export const EMBED_DARK_THEME_CLASS = "cal-element-embed-dark";
+2 -1
View File
@@ -1,3 +1,4 @@
import { EMBED_DARK_THEME_CLASS, EMBED_LIGHT_THEME_CLASS } from "./constants";
import type { AllPossibleLayouts, EmbedThemeConfig } from "./types";
export function getMaxHeightForModal() {
@@ -32,7 +33,7 @@ function detectColorScheme() {
}
function getClassBasedOnTheme(theme: EmbedThemeConfig | undefined | null) {
return theme ?? "light";
return theme === "dark" ? EMBED_DARK_THEME_CLASS : EMBED_LIGHT_THEME_CLASS;
}
export function isThemePreferenceProvided(theme: EmbedThemeConfig | undefined | null) {