You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
getComputedStyle() returns the empty string for every CSS property except display and visibility. This affects declarations from both sources: stylesheet rules never reach the computed style, and even inline style= declarations — which are correctly readable through el.style — come back as "" through the computed-style object. Per CSSOM §resolved values, getComputedStyle() must return the resolved value of every supported property; Chrome returns "uppercase" / "700" for the fixture below, and returns the property's initial value ("none", "400") when nothing matches.
Downstream CDP clients use getComputedStyle for far more than visibility: text-transform-aware text matching, font-weight assertions, color extraction, accessibility tree heuristics. With every property except display/visibility resolving to "", all of those silently degrade.
Today's behavior
A CDP client evaluating getComputedStyle(el).textTransform via Runtime.evaluate gets "" — whether the declaration comes from a stylesheet rule (#styled { text-transform: uppercase }) or from the element's own style attribute. The same inline declaration reads fine via el.style.textTransform. Only display and visibility are special-cased to consult the stylesheet cascade.
sequenceDiagram
participant Client as CDP Client
participant LP as Lightpanda
participant SM as StyleManager
Client->>LP: Runtime.evaluate getComputedStyle textTransform
LP->>SM: computed branch consults cascade
SM-->>LP: only display and visibility are resolved
LP-->>Client: empty string for every other property
Note over LP,SM: src/browser/webapi/css/CSSStyleDeclaration.zig getPropertyValue
Loading
Expected behavior
Per CSSOM §dom-window-getcomputedstyle, the returned declaration block exposes the resolved value of supported properties. Chrome resolves the full cascade: author stylesheet rules by specificity, then inline declarations, then inheritance/initial values.
sequenceDiagram
participant Client as CDP Client
participant LP as Lightpanda
participant SM as StyleManager
Client->>LP: Runtime.evaluate getComputedStyle textTransform
LP->>SM: computed branch consults cascade
SM-->>LP: matched declarations resolved by specificity
LP-->>Client: uppercase as in Chrome
Loading
Scope question (before any PR)
The current architecture explains the gap: StyleManager tracks matched rules as three booleans (display_none / visibility_hidden / opacity_zero) rather than retaining the matched declarations, and CSSStyleDeclaration.getPropertyValue's _is_computed branch special-cases exactly those two properties. Generalizing means deciding how much cascade to keep:
Per-property extension — keep the boolean-style fast path, add the handful of properties downstream clients consult most (text-transform, font-weight, color, background-color, opacity, ...), resolved from matched rules by specificity. Cheap, but each property is a new special case.
General declaration retention — StyleManager stores the full declaration list per matched rule; getPropertyValue resolves any property by walking matches in specificity/source order, falling back to inline, then to a small initial-value table. Bigger change, no per-property whack-a-mole.
Also worth deciding: whether unmatched properties should keep returning "" or synthesize initial values ("none", "400") as Chrome does — the latter is what the spec's resolved-value text implies.
Happy to implement either direction — filing this first to agree on the approach before writing code.
Reproducer
Self-contained: one HTML fixture + one shell script + a small Node CDP driver (only dependency: npm install --no-save ws, installed automatically).
Expected: all four lines resolve, prints PASS: getComputedStyle resolves cascade properties. and exits 0.
Chrome control (same fixture, chrome-headless-shell): textTransform: "uppercase", fontWeight: "700" for the stylesheet element.
Likely fix location
src/browser/webapi/css/CSSStyleDeclaration.zig — getPropertyValue: the _is_computed branch special-cases only display/visibility.
src/browser/StyleManager.zig — the cascade walk reduces matched rules to three booleans (display_none/visibility_hidden/opacity_zero); resolving other properties needs the matched declarations retained in some form (see scope question above).
Environment
Lightpanda: nightly 1.0.0-nightly.6736+63665f81 (macOS aarch64); special-casing confirmed in source on main
Summary
getComputedStyle()returns the empty string for every CSS property exceptdisplayandvisibility. This affects declarations from both sources: stylesheet rules never reach the computed style, and even inlinestyle=declarations — which are correctly readable throughel.style— come back as""through the computed-style object. Per CSSOM §resolved values,getComputedStyle()must return the resolved value of every supported property; Chrome returns"uppercase"/"700"for the fixture below, and returns the property's initial value ("none","400") when nothing matches.Downstream CDP clients use
getComputedStylefor far more than visibility: text-transform-aware text matching, font-weight assertions, color extraction, accessibility tree heuristics. With every property exceptdisplay/visibilityresolving to"", all of those silently degrade.Today's behavior
A CDP client evaluating
getComputedStyle(el).textTransformviaRuntime.evaluategets""— whether the declaration comes from a stylesheet rule (#styled { text-transform: uppercase }) or from the element's ownstyleattribute. The same inline declaration reads fine viael.style.textTransform. Onlydisplayandvisibilityare special-cased to consult the stylesheet cascade.sequenceDiagram participant Client as CDP Client participant LP as Lightpanda participant SM as StyleManager Client->>LP: Runtime.evaluate getComputedStyle textTransform LP->>SM: computed branch consults cascade SM-->>LP: only display and visibility are resolved LP-->>Client: empty string for every other property Note over LP,SM: src/browser/webapi/css/CSSStyleDeclaration.zig getPropertyValueExpected behavior
Per CSSOM §dom-window-getcomputedstyle, the returned declaration block exposes the resolved value of supported properties. Chrome resolves the full cascade: author stylesheet rules by specificity, then inline declarations, then inheritance/initial values.
sequenceDiagram participant Client as CDP Client participant LP as Lightpanda participant SM as StyleManager Client->>LP: Runtime.evaluate getComputedStyle textTransform LP->>SM: computed branch consults cascade SM-->>LP: matched declarations resolved by specificity LP-->>Client: uppercase as in ChromeScope question (before any PR)
The current architecture explains the gap:
StyleManagertracks matched rules as three booleans (display_none/visibility_hidden/opacity_zero) rather than retaining the matched declarations, andCSSStyleDeclaration.getPropertyValue's_is_computedbranch special-cases exactly those two properties. Generalizing means deciding how much cascade to keep:text-transform,font-weight,color,background-color,opacity, ...), resolved from matched rules by specificity. Cheap, but each property is a new special case.StyleManagerstores the full declaration list per matched rule;getPropertyValueresolves any property by walking matches in specificity/source order, falling back to inline, then to a small initial-value table. Bigger change, no per-property whack-a-mole.Also worth deciding: whether unmatched properties should keep returning
""or synthesize initial values ("none","400") as Chrome does — the latter is what the spec's resolved-value text implies.Happy to implement either direction — filing this first to agree on the approach before writing code.
Reproducer
Self-contained: one HTML fixture + one shell script + a small Node CDP driver (only dependency:
npm install --no-save ws, installed automatically).repro.html:repro.sh:probe.js:cdp.js(raw-ws CDP helper: connect + createTarget + attach + eval)Run:
bash repro.sh1.0.0-nightly.6736+63665f81):PASS: getComputedStyle resolves cascade properties.and exits 0.Chrome control (same fixture, chrome-headless-shell):
textTransform: "uppercase",fontWeight: "700"for the stylesheet element.Likely fix location
src/browser/webapi/css/CSSStyleDeclaration.zig—getPropertyValue: the_is_computedbranch special-cases onlydisplay/visibility.src/browser/StyleManager.zig— the cascade walk reduces matched rules to three booleans (display_none/visibility_hidden/opacity_zero); resolving other properties needs the matched declarations retained in some form (see scope question above).Environment
1.0.0-nightly.6736+63665f81(macOS aarch64); special-casing confirmed in source onmainws(Node 26)