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); border: 1px solid rgba(0, 0, 0, 0.5);
} }
&::-webkit-meter-optimum-value { &::fill {
display: block; display: block;
height: 100%; height: 100%;
}
&:optimal-value::fill {
background-color: hsl(141, 53%, 53%); background-color: hsl(141, 53%, 53%);
} }
&::-webkit-meter-suboptimum-value { &:suboptimal-value::fill {
display: block;
height: 100%;
background-color: hsl(48, 100%, 67%); background-color: hsl(48, 100%, 67%);
} }
&::-webkit-meter-even-less-good-value { &:even-less-good-value::fill {
display: block;
height: 100%;
background-color: hsl(348, 100%, 61%); background-color: hsl(348, 100%, 61%);
} }
} }

View file

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

View file

@ -554,12 +554,6 @@ StringView Selector::PseudoElement::name(Selector::PseudoElement::Type pseudo_el
return "fill"sv; return "fill"sv;
case Selector::PseudoElement::Type::Thumb: case Selector::PseudoElement::Type::Thumb:
return "thumb"sv; 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: case Selector::PseudoElement::Type::Placeholder:
return "placeholder"sv; return "placeholder"sv;
case Selector::PseudoElement::Type::Selection: case Selector::PseudoElement::Type::Selection:
@ -595,12 +589,6 @@ Optional<Selector::PseudoElement> Selector::PseudoElement::from_string(FlyString
return Selector::PseudoElement { Selector::PseudoElement::Type::Fill }; return Selector::PseudoElement { Selector::PseudoElement::Type::Fill };
} else if (name.equals_ignoring_ascii_case("thumb"sv)) { } else if (name.equals_ignoring_ascii_case("thumb"sv)) {
return Selector::PseudoElement { Selector::PseudoElement::Type::Thumb }; 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)) { } else if (name.equals_ignoring_ascii_case("placeholder"sv)) {
return Selector::PseudoElement { Selector::PseudoElement::Type::Placeholder }; return Selector::PseudoElement { Selector::PseudoElement::Type::Placeholder };
} else if (name.equals_ignoring_ascii_case("selection"sv)) { } else if (name.equals_ignoring_ascii_case("selection"sv)) {

View file

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

View file

@ -23,6 +23,7 @@
#include <LibWeb/HTML/HTMLHtmlElement.h> #include <LibWeb/HTML/HTMLHtmlElement.h>
#include <LibWeb/HTML/HTMLInputElement.h> #include <LibWeb/HTML/HTMLInputElement.h>
#include <LibWeb/HTML/HTMLMediaElement.h> #include <LibWeb/HTML/HTMLMediaElement.h>
#include <LibWeb/HTML/HTMLMeterElement.h>
#include <LibWeb/HTML/HTMLProgressElement.h> #include <LibWeb/HTML/HTMLProgressElement.h>
#include <LibWeb/HTML/HTMLSelectElement.h> #include <LibWeb/HTML/HTMLSelectElement.h>
#include <LibWeb/HTML/HTMLTextAreaElement.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; 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) 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) { switch (pseudo_class.type) {
@ -502,6 +510,20 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
return element.matches_checked_pseudo_class(); return element.matches_checked_pseudo_class();
case CSS::PseudoClass::Indeterminate: case CSS::PseudoClass::Indeterminate:
return matches_indeterminate_pseudo_class(element); 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: case CSS::PseudoClass::Defined:
return element.is_defined(); return element.is_defined();
case CSS::PseudoClass::Has: 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)); 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 = 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)); MUST(meter_bar_element->append_child(*m_meter_value_element));
update_meter_value_element(); update_meter_value_element();
} }
@ -242,18 +243,6 @@ void HTMLMeterElement::update_meter_value_element()
if (!m_meter_value_element) if (!m_meter_value_element)
return; 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; double position = (value - min) / (max - min) * 100;
MUST(m_meter_value_element->style_for_bindings()->set_property(CSS::PropertyID::Width, MUST(String::formatted("{}%", position)))); MUST(m_meter_value_element->style_for_bindings()->set_property(CSS::PropertyID::Width, MUST(String::formatted("{}%", position))));
} }