Expose live computed CSS variables to scripts

This commit is contained in:
SGavrl committed 2026-07-31 08:26:08 +00:00
1 parent 2343681497
commit 5f67bad4fe
6 files changed
+264 -14

No files matched your search

+30 -1
View File
@@ -955,7 +955,9 @@ impl Stylesheet {
}
let registration_changed_props = resolved_props != *parent_props;
let has_own = !own.is_empty();
let mut own_names = std::collections::HashSet::new();
for (k, v) in own {
own_names.insert(k.clone());
let registration = self.registered_custom_properties.get(&k);
let set_initial = |props: &mut HashMap<String, String>| {
if let Some(initial) = registration.and_then(|entry| entry.initial_value.as_ref()) {
@@ -997,6 +999,29 @@ impl Stylesheet {
}
}
}
// Custom properties inherit their computed value, not their original
// token stream. Resolve only declarations won on this element against
// the complete same-element environment (so forward references work),
// then pass those substituted values to descendants. Re-resolving an
// inherited `--b:var(--a)` after a child overrides `--a` is observably
// wrong: browsers keep the parent's already-computed `--b`.
let resolution_environment = resolved_props.clone();
for name in own_names {
let Some(value) = resolution_environment.get(&name) else {
continue;
};
if let Some(computed) = substitute_var_value(value, &resolution_environment, 0) {
resolved_props.insert(name, computed);
} else if let Some(initial) = self
.registered_custom_properties
.get(&name)
.and_then(|entry| entry.initial_value.clone())
{
resolved_props.insert(name, initial);
} else {
resolved_props.remove(&name);
}
}
let effective = if has_own || registration_changed_props {
Some(resolved_props)
} else {
@@ -1419,7 +1444,11 @@ fn substitute_declarations(css: &str, props: &HashMap<String, String>) -> String
/// represents CSS's guaranteed-invalid value. Crucially, invalidity propagates
/// through an intermediate custom property so an outer var() can use its own
/// fallback (`--toggle:var(--missing) dark; color:var(--toggle,light)`).
fn substitute_var_value(input: &str, props: &HashMap<String, String>, depth: u8) -> Option<String> {
pub(crate) fn substitute_var_value(
input: &str,
props: &HashMap<String, String>,
depth: u8,
) -> Option<String> {
if depth > 16 {
return None;
}