Summary
getComputedStyle().getPropertyValue('--x') returns the empty string for CSS custom properties declared in any source: stylesheet rules, inline style= declarations, and inherited values. After #2836, known CSS properties declared inline resolve correctly, but custom properties are excluded even on that path.
Reproduction
<!DOCTYPE html>
<html>
<head>
<style>
:root { --brand-color: #336699; }
.card { --card-pad: 16px; }
</style>
</head>
<body>
<div class="card" id="card" style="--inline-var: 4px"></div>
<script>
const root = getComputedStyle(document.documentElement);
const card = getComputedStyle(document.getElementById('card'));
console.log('ROOT_VAR=[' + root.getPropertyValue('--brand-color') + ']');
console.log('CLASS_VAR=[' + card.getPropertyValue('--card-pad') + ']');
console.log('INLINE_VAR=[' + card.getPropertyValue('--inline-var') + ']');
console.log('INHERITED_VAR=[' + card.getPropertyValue('--brand-color') + ']');
</script>
</body>
</html>
Observed (nightly 1.0.0-nightly.8643+fb7123cf, macOS aarch64, lightpanda fetch <url> --log_level debug):
ROOT_VAR=[]
CLASS_VAR=[]
INLINE_VAR=[]
INHERITED_VAR=[]
Chrome returns #336699, 16px, 4px, #336699.
Why it matters
Design-system-driven SPAs commonly build their theme at boot by reading design tokens through getComputedStyle (MUI themes built from CSS tokens, Tailwind v4 CSS-first tokens, most in-house design systems). When every --var resolves to "", the theme factory throws, React unmounts the tree, and the page renders empty - even though the JS itself runs fine in Lightpanda. Resolving custom properties would unblock this whole class of apps.
Proposed scope (following the direction in #2733)
#2733 deliberately left stylesheet-cascade resolution out of #2836, with a suggestion of making computation opt-in. Custom properties are a much smaller, self-contained slice of that problem:
- Per css-variables-1 §3.1 the computed value of a custom property is the declared token stream - no unit resolution, no layout, none of the resolved-value complexity that kept the full cascade out of scope.
- Resolution order: element inline style, then matching author rules by specificity/order, then inherit up the parent chain to
:root.
- Could sit behind an opt-in consistent with
--enable-external-stylesheets if the default-path cost is a concern.
var() substitution inside returned longhand values could be a separate follow-up.
Happy to work on a PR for this if the direction sounds right - from an initial read of StyleManager/CSSStyleProperties, the rule parsing and selector matching this needs are already in place.
Summary
getComputedStyle().getPropertyValue('--x')returns the empty string for CSS custom properties declared in any source: stylesheet rules, inlinestyle=declarations, and inherited values. After #2836, known CSS properties declared inline resolve correctly, but custom properties are excluded even on that path.Reproduction
Observed (nightly
1.0.0-nightly.8643+fb7123cf, macOS aarch64,lightpanda fetch <url> --log_level debug):Chrome returns
#336699,16px,4px,#336699.Why it matters
Design-system-driven SPAs commonly build their theme at boot by reading design tokens through
getComputedStyle(MUI themes built from CSS tokens, Tailwind v4 CSS-first tokens, most in-house design systems). When every--varresolves to"", the theme factory throws, React unmounts the tree, and the page renders empty - even though the JS itself runs fine in Lightpanda. Resolving custom properties would unblock this whole class of apps.Proposed scope (following the direction in #2733)
#2733 deliberately left stylesheet-cascade resolution out of #2836, with a suggestion of making computation opt-in. Custom properties are a much smaller, self-contained slice of that problem:
:root.--enable-external-stylesheetsif the default-path cost is a concern.var()substitution inside returned longhand values could be a separate follow-up.Happy to work on a PR for this if the direction sounds right - from an initial read of
StyleManager/CSSStyleProperties, the rule parsing and selector matching this needs are already in place.