LibWeb: Replace webkit meter-state pseudo-elements with pseudo-classes

This also implements the `:high-value` and `:low-value` that are in the
spec.

Same note as before about this being based on the very-drafty CSS Forms
spec. In fact, some of this isn't even in that spec yet. Specifically,
the `:suboptimal-value` and `:even-less-good-value` names are undecided
and subject to change. However, it's clear that this is a pseudo-class
situation, not a pseudo-element one, so I think this is still an
improvement, as it allows styling of the `::fill` pseudo-element
regardless of what state it is in.

Relevant spec issue: https://github.com/openui/open-ui/issues/1130
This commit is contained in:
Sam Atkins 2025-03-18 16:56:41 +00:00
commit d5b9c39a98
Notes: github-actions[bot] 2025-03-19 10:10:57 +00:00
6 changed files with 44 additions and 34 deletions

View file

@ -122,21 +122,20 @@ meter {
border: 1px solid rgba(0, 0, 0, 0.5);
}
&::-webkit-meter-optimum-value {
&::fill {
display: block;
height: 100%;
}
&:optimal-value::fill {
background-color: hsl(141, 53%, 53%);
}
&::-webkit-meter-suboptimum-value {
display: block;
height: 100%;
&:suboptimal-value::fill {
background-color: hsl(48, 100%, 67%);
}
&::-webkit-meter-even-less-good-value {
display: block;
height: 100%;
&:even-less-good-value::fill {
background-color: hsl(348, 100%, 61%);
}
}

View file

@ -26,6 +26,9 @@
"enabled": {
"argument": ""
},
"even-less-good-value": {
"argument": ""
},
"first-child": {
"argument": ""
},
@ -44,6 +47,9 @@
"has": {
"argument": "<relative-selector-list>"
},
"high-value": {
"argument": ""
},
"host": {
"argument": "<compound-selector>?"
},
@ -74,6 +80,9 @@
"local-link": {
"argument": ""
},
"low-value": {
"argument": ""
},
"modal": {
"argument": ""
},
@ -104,6 +113,9 @@
"open": {
"argument": ""
},
"optimal-value": {
"argument": ""
},
"popover-open": {
"argument": ""
},
@ -134,6 +146,9 @@
"stalled": {
"argument": ""
},
"suboptimal-value": {
"argument": ""
},
"target": {
"argument": ""
},

View file

@ -554,12 +554,6 @@ StringView Selector::PseudoElement::name(Selector::PseudoElement::Type pseudo_el
return "fill"sv;
case Selector::PseudoElement::Type::Thumb:
return "thumb"sv;
case Selector::PseudoElement::Type::MeterEvenLessGoodValue:
return "-webkit-meter-even-less-good-value"sv;
case Selector::PseudoElement::Type::MeterOptimumValue:
return "-webkit-meter-optimum-value"sv;
case Selector::PseudoElement::Type::MeterSuboptimumValue:
return "-webkit-meter-suboptimum-value"sv;
case Selector::PseudoElement::Type::Placeholder:
return "placeholder"sv;
case Selector::PseudoElement::Type::Selection:
@ -595,12 +589,6 @@ Optional<Selector::PseudoElement> Selector::PseudoElement::from_string(FlyString
return Selector::PseudoElement { Selector::PseudoElement::Type::Fill };
} else if (name.equals_ignoring_ascii_case("thumb"sv)) {
return Selector::PseudoElement { Selector::PseudoElement::Type::Thumb };
} else if (name.equals_ignoring_ascii_case("-webkit-meter-even-less-good-value"sv)) {
return Selector::PseudoElement { Selector::PseudoElement::Type::MeterEvenLessGoodValue };
} else if (name.equals_ignoring_ascii_case("-webkit-meter-optimum-value"sv)) {
return Selector::PseudoElement { Selector::PseudoElement::Type::MeterOptimumValue };
} else if (name.equals_ignoring_ascii_case("-webkit-meter-suboptimum-value"sv)) {
return Selector::PseudoElement { Selector::PseudoElement::Type::MeterSuboptimumValue };
} else if (name.equals_ignoring_ascii_case("placeholder"sv)) {
return Selector::PseudoElement { Selector::PseudoElement::Type::Placeholder };
} else if (name.equals_ignoring_ascii_case("selection"sv)) {

View file

@ -33,9 +33,6 @@ public:
Track,
Fill,
Thumb,
MeterEvenLessGoodValue,
MeterOptimumValue,
MeterSuboptimumValue,
Placeholder,
Selection,
Backdrop,

View file

@ -23,6 +23,7 @@
#include <LibWeb/HTML/HTMLHtmlElement.h>
#include <LibWeb/HTML/HTMLInputElement.h>
#include <LibWeb/HTML/HTMLMediaElement.h>
#include <LibWeb/HTML/HTMLMeterElement.h>
#include <LibWeb/HTML/HTMLProgressElement.h>
#include <LibWeb/HTML/HTMLSelectElement.h>
#include <LibWeb/HTML/HTMLTextAreaElement.h>
@ -423,6 +424,13 @@ static inline bool matches_host_pseudo_class(GC::Ref<DOM::Element const> element
return true;
}
static bool matches_optimal_value_pseudo_class(DOM::Element const& element, HTML::HTMLMeterElement::ValueState desired_state)
{
if (auto* meter = as_if<HTML::HTMLMeterElement>(element))
return meter->value_state() == desired_state;
return false;
}
static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClassSelector const& pseudo_class, DOM::Element const& element, GC::Ptr<DOM::Element const> shadow_host, MatchContext& context, GC::Ptr<DOM::ParentNode const> scope, SelectorKind selector_kind)
{
switch (pseudo_class.type) {
@ -502,6 +510,20 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
return element.matches_checked_pseudo_class();
case CSS::PseudoClass::Indeterminate:
return matches_indeterminate_pseudo_class(element);
case CSS::PseudoClass::HighValue:
if (auto* meter = as_if<HTML::HTMLMeterElement>(element))
return meter->value() > meter->high();
return false;
case CSS::PseudoClass::LowValue:
if (auto* meter = as_if<HTML::HTMLMeterElement>(element))
return meter->value() < meter->low();
return false;
case CSS::PseudoClass::OptimalValue:
return matches_optimal_value_pseudo_class(element, HTML::HTMLMeterElement::ValueState::Optimal);
case CSS::PseudoClass::SuboptimalValue:
return matches_optimal_value_pseudo_class(element, HTML::HTMLMeterElement::ValueState::Suboptimal);
case CSS::PseudoClass::EvenLessGoodValue:
return matches_optimal_value_pseudo_class(element, HTML::HTMLMeterElement::ValueState::EvenLessGood);
case CSS::PseudoClass::Defined:
return element.is_defined();
case CSS::PseudoClass::Has:

View file

@ -201,6 +201,7 @@ void HTMLMeterElement::create_shadow_tree_if_needed()
MUST(shadow_root->append_child(*meter_bar_element));
m_meter_value_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
m_meter_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::Type::Fill);
MUST(meter_bar_element->append_child(*m_meter_value_element));
update_meter_value_element();
}
@ -242,18 +243,6 @@ void HTMLMeterElement::update_meter_value_element()
if (!m_meter_value_element)
return;
switch (m_cached_value_state) {
case ValueState::Optimal:
m_meter_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::Type::MeterOptimumValue);
break;
case ValueState::Suboptimal:
m_meter_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::Type::MeterSuboptimumValue);
break;
case ValueState::EvenLessGood:
m_meter_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::Type::MeterEvenLessGoodValue);
break;
}
double position = (value - min) / (max - min) * 100;
MUST(m_meter_value_element->style_for_bindings()->set_property(CSS::PropertyID::Width, MUST(String::formatted("{}%", position))));
}