This page covers how StyleComputer resolves CSS rules into ComputedProperties for each DOM element, including the intermediate CascadedProperties representation, the cascade order, inheritance, the StyleScope rule-caching system, and how Web Animations integrate to override computed values.
For CSS parsing (text → rule objects), see CSS Parsing (4.3). For selector matching internals, see CSS Selector Engine (4.4). For StyleValue types and calc(), see CSS Values and Calculations (4.5). For how the layout engine consumes computed styles, see Layout System (4.6). For style and layout invalidation triggered by DOM mutations, see DOM Implementation (4.1).
Every DOM element that participates in rendering has a ComputedProperties object that stores its final resolved CSS property values. StyleComputer, owned by Document, is responsible for producing those objects.
At a high level, style computation:
CascadedProperties intermediateComputedProperties objectStyleComputer (Libraries/LibWeb/CSS/StyleComputer.h76-78) is a GC-allocated cell. Each Document owns exactly one, allocated in the Document constructor:
m_style_computer = realm.heap().allocate<CSS::StyleComputer>(*this)
m_style_scope(*this)
Libraries/LibWeb/DOM/Document.cpp498-508
The two primary public entry points are:
| Method | Returns | Purpose |
|---|---|---|
compute_style(AbstractElement) | GC::Ref<ComputedProperties> | Full element style |
compute_pseudo_element_style_if_needed(AbstractElement, ...) | GC::Ptr<ComputedProperties> | Pseudo-element style if active |
Document also exposes a convenience accessor document.style_computer() used throughout the rendering engine.
Sources: Libraries/LibWeb/CSS/StyleComputer.h76-120 Libraries/LibWeb/DOM/Document.h263-264
Diagram: Style Computation Pipeline (class/method view)
Sources: Libraries/LibWeb/CSS/StyleComputer.cpp243-535 Libraries/LibWeb/CSS/StyleComputer.h102-110
collect_matching_rules (Libraries/LibWeb/CSS/StyleComputer.cpp243-387) is called once per cascade origin. It:
rule_cache_for_cascade_origin() to get the appropriate RuleCache from the StyleScopeRuleCache::for_each_matching_rules() to iterate candidates indexed by the element's identifiersshould_reject_with_ancestor_filter)SelectorEngine::matches() on each remaining candidateShadow DOM is handled by selecting caches from the appropriate ShadowRoot scope and by special-casing slotted (rule_cache->slotted_rules) and ::part() rules (rule_cache->part_rules).
StyleComputer maintains a CountingBloomFilter<u8, 14> (Libraries/LibWeb/CSS/StyleComputer.h28-74) that tracks the IDs, class names, and tag names of ancestor elements as the tree is traversed (push_ancestor / pop_ancestor). Selectors referencing identifiers absent from the filter are rejected without invoking the full selector engine.
Sources: Libraries/LibWeb/CSS/StyleComputer.cpp243-387 Libraries/LibWeb/CSS/StyleComputer.h96-100
sort_matching_rules (Libraries/LibWeb/CSS/StyleComputer.cpp389-403) sorts the collected matching rules using a three-key comparator:
Selector::specificity() — lower specificity firststyle_sheet_index — earlier sheets before later sheetsrule_index — earlier rules before later rules within a sheetLower-priority entries are written first in cascade_declarations, so later (higher-priority) entries overwrite them.
Before producing ComputedProperties, the style computer populates a CascadedProperties object. cascade_declarations (Libraries/LibWeb/CSS/StyleComputer.cpp446-535) iterates matching rules in priority order and calls one of:
cascaded_properties.set_property(property_id, value, important, cascade_origin, layer_name, declaration) — writes or overwrites a property entrycascaded_properties.revert_property(...) — handles the revert keywordcascaded_properties.revert_layer_property(...) — handles revert-layerEach entry in CascadedProperties records: the StyleValue, whether it is !important, its CascadeOrigin, and the @layer name (if any). This allows later phases to implement revert and revert-layer correctly.
Before writing to CascadedProperties, for_each_property_expanding_shorthands (Libraries/LibWeb/CSS/StyleComputer.cpp405-444) recursively expands shorthand properties (e.g. margin) into their longhands (margin-top, margin-right, margin-bottom, margin-left). Shorthands containing unresolved substitution functions (e.g. var()) are handled with PendingSubstitutionStyleValue.
Logical properties (e.g. margin-block-start) are mapped to their physical counterparts (e.g. margin-top) within cascade_declarations using map_logical_alias_to_physical_property, driven by the element's writing mode context.
Sources: Libraries/LibWeb/CSS/StyleComputer.cpp446-535
CSS cascade priority follows this order (weakest to strongest):
| # | Origin | Importance |
|---|---|---|
| 1 | User-Agent | Normal |
| 2 | User | Normal |
| 3 | Author (in @layer) | Normal |
| 4 | Author (unlayered) | Normal |
| 5 | CSS Animations | — |
| 6 | Author | !important |
| 7 | User | !important |
| 8 | User-Agent | !important |
| 9 | CSS Transitions | — (highest) |
In ComputedProperties::property() (Libraries/LibWeb/CSS/ComputedProperties.cpp212-224), the animated-value override logic is:
The WithAnimationsApplied parameter lets callers opt out of animated overrides (e.g. when resolving keyframe values themselves).
ComputedProperties (Libraries/LibWeb/CSS/ComputedProperties.h43-254) is a GC-allocated cell that holds the fully resolved style for one element or pseudo-element.
Diagram: ComputedProperties Internal Structure
m_property_values is a fixed-size array indexed by PropertyID, covering all longhand properties. m_animated_property_values is a HashMap that overlays animated and transition values without touching the base cascade result.
The property() method (Libraries/LibWeb/CSS/ComputedProperties.cpp212-224) is the single read point used by layout, painting, and tests.
Sources: Libraries/LibWeb/CSS/ComputedProperties.h43-120 Libraries/LibWeb/CSS/ComputedProperties.cpp146-224
Properties declared with "inherited": true in Properties.json propagate from parent to child when not explicitly set. Properties with "inherited": false fall back to their "initial" value.
StyleComputer provides two utilities for reading inheritable values from the parent element:
| Method | Purpose |
|---|---|
get_non_animated_inherit_value(PropertyID, AbstractElement) | Returns the parent's base (non-animated) value |
get_animated_inherit_value(PropertyID, AbstractElement) | Returns the parent's animated value if present |
Libraries/LibWeb/CSS/StyleComputer.h82-87
StyleScope (Libraries/LibWeb/CSS/StyleScope.h) is a member of Document and each ShadowRoot. It owns the rule caches that StyleComputer queries during rule collection.
Diagram: StyleScope and RuleCache Relationships
rule_cache_for_cascade_origin() (Libraries/LibWeb/CSS/StyleComputer.cpp167-189) selects the RuleCaches for a given CascadeOrigin (UserAgent, User, or Author), then picks either main or a specific layer's RuleCache. The cache is built lazily by build_rule_cache_if_needed() the first time it is needed.
Sources: Libraries/LibWeb/CSS/StyleComputer.cpp167-189 Libraries/LibWeb/DOM/Document.cpp498-508
Each CSS property's cascade behavior is driven by metadata in Libraries/LibWeb/CSS/Properties.json. Key fields used during style computation:
| Field | Values | Meaning |
|---|---|---|
inherited | true / false | Whether value propagates to children |
initial | string | Default value used when unset |
animation-type | by-computed-value, discrete, none, repeatable-list, custom | How the property interpolates during animations |
requires-computation | always, cascaded-value, non-inherited-value, never | When the computed value must be recalculated |
affects-layout | true / false | Whether a value change triggers layout |
affects-stacking-context | true / false | Whether value creates a stacking context |
longhands | array | Constituent longhands of a shorthand |
logical-alias-for | object | Maps logical property to its physical target |
multiplicity | coordinating-list, list | For list-valued properties |
Sources: Libraries/LibWeb/CSS/Properties.json260-410
Diagram: Animation System Class Hierarchy
Sources: Libraries/LibWeb/Animations/KeyframeEffect.cpp1-50 Libraries/LibWeb/CSS/CSSAnimation.h Libraries/LibWeb/CSS/CSSTransition.h
StyleComputer::collect_animation_into (Libraries/LibWeb/CSS/StyleComputer.cpp578-632) applies one KeyframeEffect's current state to a ComputedProperties object. Steps:
effect->transformed_progress() — returns a Optional<double> in [0.0, 1.0] based on the current timeline time and timing function; nullopt if the animation is inactiveeffect->key_frame_set()->keyframes_by_key — a red-black tree keyed by integer progress (progress × AnimationKeyFrameKeyScaleFactor)find_largest_not_above_iteratorprogress_in_keyframe = (progress - keyframe_start) / (keyframe_end - keyframe_start)StyleValue using the CSS interpolation layer (CSS::Interpolation, driven by the property's animation-type from Properties.json)ComputedProperties via set_animated_property(property_id, interpolated_value, ...)If a property is absent from one of the two surrounding keyframes, the missing value is resolved from the element's base ComputedProperties.
Sources: Libraries/LibWeb/CSS/StyleComputer.cpp578-632
When StyleComputer encounters an animation-name property on an element:
CSSKeyframesRule from the StyleScopeCSSAnimation bound to the elementCSSAnimation creates a KeyframeEffect populated with keyframe data from the CSSKeyframesRuleCSSAnimation is registered on the element via Animatable::associate_with_animation()collect_animation_into is called for each active KeyframeEffectEasing functions are used for both animation-timing-function and transition-timing-function. They may also appear per-keyframe inside @keyframes blocks.
EasingStyleValue (Libraries/LibWeb/CSS/StyleValues/EasingStyleValue.h) is the StyleValue subtype for easing functions. The EasingFunction enum (Libraries/LibWeb/CSS/EasingFunction.h) lists the supported types.
TransitionProperties (Libraries/LibWeb/CSS/ComputedProperties.h30-36) bundles transition parameters used when starting a new CSSTransition:
Supported easing categories (from Properties.json animation-timing-function definition):
| Category | Examples |
|---|---|
| Named keywords | ease, ease-in, ease-out, ease-in-out, linear, step-start, step-end |
cubic-bezier(x1, y1, x2, y2) | Custom cubic Bézier |
steps(n, jump-term) | Step functions |
linear(stop-list) | Piecewise linear |
Sources: Libraries/LibWeb/CSS/Properties.json399-410 Libraries/LibWeb/CSS/ComputedProperties.h30-36
CSSTransition is a subclass of Animation. When StyleComputer detects that a property's computed value has changed and a matching transition-property / transition-duration is set, it creates a CSSTransition with:
TransitionPropertiesTransition-originated animated values are tagged with AnimatedPropertyResultOfTransition::Yes (Libraries/LibWeb/CSS/ComputedProperties.h38-41), which allows them to override !important base values (unlike regular animation values, which cannot).
StyleComputer provides two methods that the invalidation system uses to determine which subtree elements need re-styling after a DOM change:
| Method | Purpose |
|---|---|
invalidation_set_for_properties(properties, StyleScope) | Returns the InvalidationSet (set of subtree features) that must be re-checked when the given CSS properties change |
invalidation_property_used_in_has_selector(property, StyleScope) | Returns true if the property name appears inside a :has() selector, requiring broader re-evaluation |
Libraries/LibWeb/CSS/StyleComputer.cpp199-241
The StyleScope::m_style_invalidation_data stores precomputed maps: ids_used_in_has_selectors, class_names_used_in_has_selectors, attribute_names_used_in_has_selectors, and tag_names_used_in_has_selectors.
Diagram: End-to-End Data Flow from CSS Source to Layout
Sources: Libraries/LibWeb/CSS/StyleComputer.cpp167-632 Libraries/LibWeb/CSS/ComputedProperties.cpp146-224 Libraries/LibWeb/CSS/ComputedProperties.h43-254
Refresh this wiki