Skip to content

blitz-dom: no CSSOM inline-style edit keeps both the value and the style attribute

Generated from repros/blitz-dom-cssom-inline-style-round-trip/README.md when the site was built at 7aa23e2.

niso implements element.style (setProperty, removeProperty, cssText and the property setters) on top of blitz-dom. Until cssom-inline-style it used blitz’s string API, BaseDocument::style_attr_set_property, and set the result as the style attribute, which blitz parses back. The serialization rounds numbers to six significant digits, so what the page set is not what cascades. WPT’s interpolation harness sets cubic-bezier(0, 0.23333333333333334, 1, 0.23333333333333334) to sample a transition at progress 0.3; after the round trip the curve is cubic-bezier(0, 0.233333, 1, 0.233333), the output is 0.29999975, and 15 subtests in css/css-ui/animation/caret-color-interpolation.html, css/css-transitions/animations/text-shadow-interpolation.html and css/css-transforms/animation/transform-interpolation-005.html got rgb(76, 49, 0) for rgb(77, 50, 0) and matrix3d values just off.

niso now keeps the parsed block itself (crates/niso-core/src/inline_style.rs): it edits the block with Stylo’s prepare_for_update/update, installs it as the element’s style_attribute, and writes the serialization into ElementData::attrs directly. To keep blitz’s restyle and selector invalidation for the change it first calls set_attribute(style, ""), which blitz parses (cheaply) before niso replaces both the attribute value and the block.

CSSOM (“update style attribute for”, CSSOM §6.7) sets the attribute to the declarations’ serialization without reparsing it: the declaration block keeps the values as parsed, and the attribute holds their serialization. blitz-dom offers two ways to edit inline style, and neither does both:

  • style_attr_set_property + set_attribute: the attribute is right, but blitz reparses it (DocumentMutator::set_attribute → ElementData::flush_style_attribute), so the cascade sees the rounded serialization.
  • set_style_property (DocumentMutator, BaseDocument, ElementData): the block keeps the value, but the style attribute is not updated at all, so getAttribute('style'), attribute selectors and serialization go stale.
style.transitionTimingFunction = 'cubic-bezier(0, 0.23333333333333334, 1, 0.23333333333333334)', eased at 0.5 (0.3 expected):
style_attr_set_property + set_attribute: 0.299999755, attribute "transition-timing-function: cubic-bezier(0, 0.233333, 1, 0.233333);"
set_style_property: 0.300000001, attribute None
Terminal window
CARGO_TARGET_DIR=~/.capy/work/repro-target cargo run

Exit status 1 while neither API keeps both, 0 once one does. On Ubuntu, blitz needs sudo apt-get install -y libfontconfig-dev pkg-config.

Same code on DioxusLabs/blitz main at 808ded9348289cac0a3b4940e69da21d5b918443 (2026-11-09): style_attr_set_property (packages/blitz-dom/src/resolved_style.rs:225) still returns only a string, set_attribute still reparses a style attribute (packages/blitz-dom/src/mutator.rs:412), and ElementData::set_style_property (packages/blitz-dom/src/node/element.rs:713) still leaves attrs alone. Not run against main itself.

None found in DioxusLabs/blitz issues or PRs for “style attribute”, “setProperty”, “CSSOM” or “style_attr_set_property”.

Title: No inline-style edit keeps both the parsed value and the style attribute

Body:

To implement element.style, an embedder needs CSSOM’s “update style attribute”: the element’s declaration block changes, and the style attribute becomes its serialization without being parsed back. blitz-dom has two ways in, and each keeps only half:

  • BaseDocument::style_attr_set_property returns the new attribute string; setting it with DocumentMutator::set_attribute reparses it, and the serialization rounds numbers to six significant digits. cubic-bezier(0, 0.23333333333333334, 1, 0.23333333333333334) cascades as cubic-bezier(0, 0.233333, 1, 0.233333), so the curve gives 0.29999975 at 0.5 instead of 0.3, and colors interpolated at that progress round down (WPT’s interpolation tests sample this way).
  • DocumentMutator::set_style_property keeps the parsed value but never updates the style attribute, so getAttribute('style'), [style] selectors and serialization see the old value.

Repro (blitz be60597; the same code is on main 808ded93):

let css = doc.style_attr_set_property("", "transition-timing-function", EASING, false).unwrap();
doc.mutate().set_attribute(a, style, &css); // cascades 0.233333
doc.mutate().set_style_property(b, "transition-timing-function", EASING);
doc.get_node(b).unwrap().attr(local_name!("style")); // None

Suggested fix: have set_style_property (or a new mutator method) also write the block’s serialization into the style attribute, snapshotting for invalidation as set_attribute does, without calling flush_style_attribute.