Skip to content

Commit bf7138d

Browse files
authored
fix(theming): Resolve initial theme using computed styles (#2061)
Use the computed styles of the document element to determine the initial theme when no theme is explicitly set. Removed the old logic where theme was resolved from reading through the document stylesheets. Closes #2060
1 parent ed36f79 commit bf7138d

2 files changed

Lines changed: 26 additions & 76 deletions

File tree

src/theming/config.spec.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { aTimeout, expect, oneEvent } from '@open-wc/testing';
1+
import { expect, oneEvent } from '@open-wc/testing';
22

33
import { configureTheme, getTheme } from './config.js';
44
import { CHANGE_THEME_EVENT } from './theming-event.js';
@@ -18,21 +18,6 @@ describe('Theming Config', () => {
1818
sheet.remove();
1919
});
2020

21-
it('does not throw for CORS', async () => {
22-
const link = document.createElement('link');
23-
Object.assign(link, {
24-
rel: 'stylesheet',
25-
href: 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css',
26-
});
27-
28-
document.head.append(link);
29-
await aTimeout(1000);
30-
31-
expect(() => getAllCssVariables()).not.to.throw();
32-
33-
link.remove();
34-
});
35-
3621
it('should set the theme and raise event with the new theme', async () => {
3722
const theme = 'material';
3823
const themeVariant = 'light';

src/theming/utils.ts

Lines changed: 25 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,41 @@
11
import { isServer } from 'lit';
22

3-
function isStyleRule(rule: CSSRule): rule is CSSStyleRule {
4-
return rule != null && 'style' in rule;
5-
}
6-
3+
/**
4+
* Converts a CSS custom property name to a camelCase JavaScript key.
5+
* Removes leading '--' and converts kebab-case to camelCase.
6+
* @param key - The CSS variable name (e.g., '--my-color')
7+
* @returns The JavaScript key (e.g., 'myColor')
8+
*/
79
function cssKeyToJsKey(key: string): string {
10+
// Regex: matches '--' at start OR '-x' pattern to convert to camelCase
811
return key.replace(/^--|-./g, (match) => {
912
return match.startsWith('--') ? '' : match.charAt(1).toUpperCase();
1013
});
1114
}
1215

13-
function getAllCssVariableNames(): Set<string> {
14-
const cssVars = new Set<string>();
15-
const styleSheets = Array.from(document.styleSheets);
16-
17-
for (const sheet of styleSheets) {
18-
let rules: CSSRuleList | undefined;
19-
20-
// Potential CORS or access errors
21-
try {
22-
rules = sheet.cssRules;
23-
} catch {
24-
continue;
25-
}
26-
27-
if (!rules) {
28-
continue;
29-
}
30-
31-
for (const rule of Array.from(rules)) {
32-
if (isStyleRule(rule)) {
33-
const length = rule.style.length;
34-
35-
for (let i = 0; i < length; i++) {
36-
const style = rule.style[i];
37-
38-
if (style.startsWith('--')) {
39-
cssVars.add(style);
40-
}
41-
}
42-
}
43-
}
44-
}
45-
46-
return cssVars;
47-
}
48-
49-
function getElementCssVariables(
50-
allCssVars: Set<string>,
51-
element: HTMLElement,
52-
pseudo?: string
53-
): Record<string, string> {
54-
const cssVars: Record<string, string> = {};
55-
const styles = getComputedStyle(element, pseudo);
56-
57-
for (const key of allCssVars) {
58-
const value = styles.getPropertyValue(key);
16+
function getCssVariables(): Record<string, string> {
17+
const rootStyles = getComputedStyle(document.documentElement);
18+
const result: Record<string, string> = {};
5919

60-
if (value) {
61-
cssVars[cssKeyToJsKey(key)] = value.trim();
20+
for (const key of Array.from(rootStyles)) {
21+
if (key.startsWith('--')) {
22+
result[cssKeyToJsKey(key)] = rootStyles.getPropertyValue(key).trim();
6223
}
6324
}
6425

65-
return cssVars;
26+
return result;
6627
}
6728

29+
/**
30+
* Retrieves all CSS custom properties from the document root element.
31+
* Property names are converted from kebab-case to camelCase.
32+
*
33+
* @returns An object mapping camelCase property names to their values.
34+
* Returns an empty object in SSR environments.
35+
* @example
36+
* // CSS: --my-primary-color: #ff0000;
37+
* // Returns: { myPrimaryColor: '#ff0000' }
38+
*/
6839
export function getAllCssVariables(): Record<string, string> {
69-
/* c8 ignore next 2 */
70-
return isServer
71-
? {}
72-
: getElementCssVariables(
73-
getAllCssVariableNames(),
74-
document.documentElement
75-
);
40+
return isServer ? {} : getCssVariables();
7641
}

0 commit comments

Comments
 (0)