mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-22 09:12:13 -05:00
LibWeb/CSS: Implement the color-scheme CSS property
This commit is contained in:
parent
89296b88a0
commit
ce5cd012b9
Notes:
github-actions[bot]
2025-01-08 11:19:41 +00:00
Author: https://github.com/Gingeh Commit: https://github.com/LadybirdBrowser/ladybird/commit/ce5cd012b99 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3146 Reviewed-by: https://github.com/AtkinsSJ ✅
36 changed files with 618 additions and 370 deletions
|
@ -4,7 +4,6 @@
|
|||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: rgb(23, 23, 23);
|
||||
--separator: dimgray;
|
||||
--separator-accent: rgb(57, 57, 57);
|
||||
--tab-controls: rgb(57, 57, 57);
|
||||
|
@ -30,7 +29,6 @@
|
|||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--background: white;
|
||||
--separator: lightgray;
|
||||
--separator-accent: white;
|
||||
--tab-controls: rgb(229, 229, 229);
|
||||
|
@ -54,7 +52,7 @@
|
|||
}
|
||||
|
||||
html {
|
||||
background-color: var(--background);
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -186,7 +184,7 @@ body {
|
|||
left: 0;
|
||||
right: 0;
|
||||
background-color: var(--tab-controls);
|
||||
border-top: 2px solid var(--background);
|
||||
border-top: 2px solid Canvas;
|
||||
display: flex;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,8 @@
|
|||
<meta charset="UTF-8">
|
||||
<title>New Tab</title>
|
||||
<style>
|
||||
/* FIXME: We should be able to remove the HTML style when "color-scheme" is supported */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: rgb(20, 20, 20);
|
||||
}
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
|
|
@ -4,15 +4,8 @@
|
|||
<meta charset="UTF-8">
|
||||
<title>Index of %path%</title>
|
||||
<style>
|
||||
/* FIXME: We should be able to remove the HTML style when "color-scheme" is supported */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: rgb(20, 20, 20);
|
||||
color: rgb(235, 235, 235);
|
||||
}
|
||||
a {
|
||||
color: cornflowerblue;
|
||||
}
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
header {
|
||||
|
|
|
@ -4,12 +4,8 @@
|
|||
<meta charset="UTF-8">
|
||||
<title>Error!</title>
|
||||
<style>
|
||||
/* FIXME: We should be able to remove the HTML style when "color-scheme" is supported */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: rgb(20, 20, 20);
|
||||
color: rgb(235, 235, 235);
|
||||
}
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
|
|
@ -4,12 +4,8 @@
|
|||
<meta charset="UTF-8">
|
||||
<title>About %browser_name%</title>
|
||||
<style>
|
||||
/* FIXME: We should be able to remove the HTML style when "color-scheme" is supported */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: rgb(20, 20, 20);
|
||||
color: rgb(235, 235, 235);
|
||||
}
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
img {
|
||||
float: left;
|
||||
|
|
|
@ -126,6 +126,7 @@ set(SOURCES
|
|||
CSS/StyleValues/BasicShapeStyleValue.cpp
|
||||
CSS/StyleValues/BorderRadiusStyleValue.cpp
|
||||
CSS/StyleValues/CalculatedStyleValue.cpp
|
||||
CSS/StyleValues/ColorSchemeStyleValue.cpp
|
||||
CSS/StyleValues/ConicGradientStyleValue.cpp
|
||||
CSS/StyleValues/ContentStyleValue.cpp
|
||||
CSS/StyleValues/CounterDefinitionsStyleValue.cpp
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/CSSColorValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ConicGradientStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.h>
|
||||
|
@ -116,6 +117,12 @@ CSSColorValue const& CSSStyleValue::as_color() const
|
|||
return static_cast<CSSColorValue const&>(*this);
|
||||
}
|
||||
|
||||
ColorSchemeStyleValue const& CSSStyleValue::as_color_scheme() const
|
||||
{
|
||||
VERIFY(is_color_scheme());
|
||||
return static_cast<ColorSchemeStyleValue const&>(*this);
|
||||
}
|
||||
|
||||
ConicGradientStyleValue const& CSSStyleValue::as_conic_gradient() const
|
||||
{
|
||||
VERIFY(is_conic_gradient());
|
||||
|
|
|
@ -96,6 +96,7 @@ public:
|
|||
BorderRadius,
|
||||
Calculated,
|
||||
Color,
|
||||
ColorScheme,
|
||||
ConicGradient,
|
||||
Content,
|
||||
Counter,
|
||||
|
@ -178,6 +179,10 @@ public:
|
|||
CSSColorValue const& as_color() const;
|
||||
CSSColorValue& as_color() { return const_cast<CSSColorValue&>(const_cast<CSSStyleValue const&>(*this).as_color()); }
|
||||
|
||||
bool is_color_scheme() const { return type() == Type::ColorScheme; }
|
||||
ColorSchemeStyleValue const& as_color_scheme() const;
|
||||
ColorSchemeStyleValue& as_color_scheme() { return const_cast<ColorSchemeStyleValue&>(const_cast<CSSStyleValue const&>(*this).as_color_scheme()); }
|
||||
|
||||
bool is_conic_gradient() const { return type() == Type::ConicGradient; }
|
||||
ConicGradientStyleValue const& as_conic_gradient() const;
|
||||
ConicGradientStyleValue& as_conic_gradient() { return const_cast<ConicGradientStyleValue&>(const_cast<CSSStyleValue const&>(*this).as_conic_gradient()); }
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <LibWeb/CSS/ComputedProperties.h>
|
||||
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CounterStyleValue.h>
|
||||
|
@ -228,6 +229,34 @@ Color ComputedProperties::color_or_fallback(CSS::PropertyID id, Layout::NodeWith
|
|||
return value.to_color(node);
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-color-adjust-1/#determine-the-used-color-scheme
|
||||
CSS::PreferredColorScheme ComputedProperties::color_scheme(CSS::PreferredColorScheme preferred_scheme) const
|
||||
{
|
||||
// To determine the used color scheme of an element:
|
||||
auto const& scheme_value = property(CSS::PropertyID::ColorScheme).as_color_scheme();
|
||||
|
||||
// 1. If the user’s preferred color scheme, as indicated by the prefers-color-scheme media feature,
|
||||
// is present among the listed color schemes, and is supported by the user agent,
|
||||
// that’s the element’s used color scheme.
|
||||
if (preferred_scheme != CSS::PreferredColorScheme::Auto && scheme_value.schemes().contains_slow(preferred_color_scheme_to_string(preferred_scheme)))
|
||||
return preferred_scheme;
|
||||
|
||||
// 2. Otherwise, if the user has indicated an overriding preference for their chosen color scheme,
|
||||
// and the only keyword is not present in color-scheme for the element,
|
||||
// the user agent must override the color scheme with the user’s preferred color scheme.
|
||||
// See § 2.3 Overriding the Color Scheme.
|
||||
// FIXME: We don't currently support setting an "overriding preference" for color schemes.
|
||||
|
||||
// 3. Otherwise, if the user agent supports at least one of the listed color schemes,
|
||||
// the used color scheme is the first supported color scheme in the list.
|
||||
auto first_supported = scheme_value.schemes().first_matching([](auto scheme) { return preferred_color_scheme_from_string(scheme) != CSS::PreferredColorScheme::Auto; });
|
||||
if (first_supported.has_value())
|
||||
return preferred_color_scheme_from_string(first_supported.value());
|
||||
|
||||
// 4. Otherwise, the used color scheme is the browser default. (Same as normal.)
|
||||
return CSS::PreferredColorScheme::Light;
|
||||
}
|
||||
|
||||
NonnullRefPtr<Gfx::Font const> ComputedProperties::font_fallback(bool monospace, bool bold, float point_size)
|
||||
{
|
||||
if (monospace && bold)
|
||||
|
|
|
@ -74,6 +74,7 @@ public:
|
|||
Optional<LengthPercentage> length_percentage(CSS::PropertyID) const;
|
||||
LengthBox length_box(CSS::PropertyID left_id, CSS::PropertyID top_id, CSS::PropertyID right_id, CSS::PropertyID bottom_id, const CSS::Length& default_value) const;
|
||||
Color color_or_fallback(CSS::PropertyID, Layout::NodeWithStyle const&, Color fallback) const;
|
||||
CSS::PreferredColorScheme color_scheme(CSS::PreferredColorScheme) const;
|
||||
Optional<CSS::TextAnchor> text_anchor() const;
|
||||
Optional<CSS::TextAlign> text_align() const;
|
||||
Optional<CSS::TextJustify> text_justify() const;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <LibWeb/CSS/GridTrackSize.h>
|
||||
#include <LibWeb/CSS/LengthBox.h>
|
||||
#include <LibWeb/CSS/PercentageOr.h>
|
||||
#include <LibWeb/CSS/PreferredColorScheme.h>
|
||||
#include <LibWeb/CSS/Ratio.h>
|
||||
#include <LibWeb/CSS/Size.h>
|
||||
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
|
||||
|
@ -76,6 +77,7 @@ public:
|
|||
static CSS::CaptionSide caption_side() { return CSS::CaptionSide::Top; }
|
||||
static CSS::Clear clear() { return CSS::Clear::None; }
|
||||
static CSS::Clip clip() { return CSS::Clip::make_auto(); }
|
||||
static CSS::PreferredColorScheme color_scheme() { return CSS::PreferredColorScheme::Auto; }
|
||||
static CSS::ContentVisibility content_visibility() { return CSS::ContentVisibility::Visible; }
|
||||
static CSS::Cursor cursor() { return CSS::Cursor::Auto; }
|
||||
static CSS::WhiteSpace white_space() { return CSS::WhiteSpace::Normal; }
|
||||
|
@ -351,6 +353,7 @@ public:
|
|||
CSS::CaptionSide caption_side() const { return m_inherited.caption_side; }
|
||||
CSS::Clear clear() const { return m_noninherited.clear; }
|
||||
CSS::Clip clip() const { return m_noninherited.clip; }
|
||||
CSS::PreferredColorScheme color_scheme() const { return m_inherited.color_scheme; }
|
||||
CSS::ContentVisibility content_visibility() const { return m_inherited.content_visibility; }
|
||||
CSS::Cursor cursor() const { return m_inherited.cursor; }
|
||||
CSS::ContentData content() const { return m_noninherited.content; }
|
||||
|
@ -543,6 +546,7 @@ protected:
|
|||
CSS::Length border_spacing_vertical { InitialValues::border_spacing() };
|
||||
CSS::CaptionSide caption_side { InitialValues::caption_side() };
|
||||
Color color { InitialValues::color() };
|
||||
CSS::PreferredColorScheme color_scheme { InitialValues::color_scheme() };
|
||||
Optional<Color> accent_color {};
|
||||
Color webkit_text_fill_color { InitialValues::color() };
|
||||
CSS::ContentVisibility content_visibility { InitialValues::content_visibility() };
|
||||
|
@ -724,6 +728,7 @@ public:
|
|||
void set_border_spacing_vertical(CSS::Length border_spacing_vertical) { m_inherited.border_spacing_vertical = border_spacing_vertical; }
|
||||
void set_caption_side(CSS::CaptionSide caption_side) { m_inherited.caption_side = caption_side; }
|
||||
void set_color(Color color) { m_inherited.color = color; }
|
||||
void set_color_scheme(CSS::PreferredColorScheme color_scheme) { m_inherited.color_scheme = color_scheme; }
|
||||
void set_clip(CSS::Clip const& clip) { m_noninherited.clip = clip; }
|
||||
void set_content(ContentData const& content) { m_noninherited.content = content; }
|
||||
void set_content_visibility(CSS::ContentVisibility content_visibility) { m_inherited.content_visibility = content_visibility; }
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
html {
|
||||
font-family: serif;
|
||||
color: CanvasText;
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -54,15 +53,12 @@ button, input[type=submit], input[type=button], input[type=reset], select, ::fil
|
|||
border: 1px solid ButtonBorder;
|
||||
color: ButtonText;
|
||||
cursor: default;
|
||||
/* FIXME: For some reason this filter is required for the :hover style to work */
|
||||
filter: contrast(100%);
|
||||
}
|
||||
|
||||
button:disabled, input[type=submit]:disabled, input[type=button]:disabled, input[type=reset]:disabled, select:disabled {
|
||||
/* FIXME: There isn't a <system-color> keyword for this, so this is a slightly lightened
|
||||
* version of our light ButtonFace color. Once we support `color-scheme: dark`
|
||||
* we'll need to use a different color for that.
|
||||
*/
|
||||
background-color: #e5e0d7;
|
||||
color: GrayText;
|
||||
filter: contrast(70%);
|
||||
}
|
||||
|
||||
input[type=image] {
|
||||
|
@ -70,11 +66,7 @@ input[type=image] {
|
|||
}
|
||||
|
||||
button:hover, input[type=submit]:hover, input[type=button]:hover, input[type=reset]:hover, select:hover {
|
||||
/* FIXME: There isn't a <system-color> keyword for this, so this is a slightly lightened
|
||||
* version of our light ButtonFace color. Once we support `color-scheme: dark`
|
||||
* we'll need to use a different color for that.
|
||||
*/
|
||||
background-color: #e5e0d7;
|
||||
filter: contrast(70%);
|
||||
}
|
||||
|
||||
option {
|
||||
|
@ -95,7 +87,7 @@ input[type=range]::-webkit-slider-runnable-track {
|
|||
height: 4px;
|
||||
width: 100%;
|
||||
margin-top: 6px;
|
||||
background-color: Background;
|
||||
background-color: AccentColorText;
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
|
@ -143,7 +135,7 @@ progress::-webkit-progress-bar, progress::-webkit-progress-value {
|
|||
height: 100%;
|
||||
}
|
||||
progress::-webkit-progress-bar {
|
||||
background-color: Background;
|
||||
background-color: AccentColorText;
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
|
@ -862,6 +854,8 @@ progress {
|
|||
/* https://github.com/whatwg/html/pull/9546
|
||||
*/
|
||||
input[type=checkbox][switch] {
|
||||
/* FIXME: Workaround until we can properly style dark switches */
|
||||
color-scheme: light;
|
||||
appearance: none;
|
||||
height: 1em;
|
||||
width: 1.8em;
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/CSSLCHLike.h>
|
||||
#include <LibWeb/CSS/StyleValues/CSSLabLike.h>
|
||||
#include <LibWeb/CSS/StyleValues/CSSRGB.h>
|
||||
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CounterStyleValue.h>
|
||||
|
@ -3612,6 +3613,71 @@ RefPtr<CSSStyleValue> Parser::parse_color_value(TokenStream<ComponentValue>& tok
|
|||
return {};
|
||||
}
|
||||
|
||||
RefPtr<CSSStyleValue> Parser::parse_color_scheme_value(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
// normal | [ light | dark | <custom-ident> ]+ && only?
|
||||
|
||||
// normal
|
||||
{
|
||||
auto transaction = tokens.begin_transaction();
|
||||
tokens.discard_whitespace();
|
||||
if (tokens.consume_a_token().is_ident("normal"sv)) {
|
||||
if (tokens.has_next_token())
|
||||
return {};
|
||||
transaction.commit();
|
||||
return ColorSchemeStyleValue::normal();
|
||||
}
|
||||
}
|
||||
|
||||
bool only = false;
|
||||
Vector<String> schemes;
|
||||
|
||||
// only? && (..)
|
||||
{
|
||||
auto transaction = tokens.begin_transaction();
|
||||
tokens.discard_whitespace();
|
||||
if (tokens.consume_a_token().is_ident("only"sv)) {
|
||||
only = true;
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
|
||||
// [ light | dark | <custom-ident> ]+
|
||||
tokens.discard_whitespace();
|
||||
while (tokens.has_next_token()) {
|
||||
auto transaction = tokens.begin_transaction();
|
||||
|
||||
// The 'normal', 'light', 'dark', and 'only' keywords are not valid <custom-ident>s in this property.
|
||||
// Note: only 'normal' is blacklisted here because 'light' and 'dark' aren't parsed differently and 'only' is checked for afterwards
|
||||
auto ident = parse_custom_ident_value(tokens, { "normal"sv });
|
||||
if (!ident)
|
||||
return {};
|
||||
|
||||
if (ident->custom_ident() == "only"_fly_string)
|
||||
break;
|
||||
|
||||
schemes.append(ident->custom_ident().to_string());
|
||||
tokens.discard_whitespace();
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
// (..) && only?
|
||||
if (!only) {
|
||||
auto transaction = tokens.begin_transaction();
|
||||
tokens.discard_whitespace();
|
||||
if (tokens.consume_a_token().is_ident("only"sv)) {
|
||||
only = true;
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
|
||||
tokens.discard_whitespace();
|
||||
if (tokens.has_next_token() || schemes.is_empty())
|
||||
return {};
|
||||
|
||||
return ColorSchemeStyleValue::create(schemes, only);
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-lists-3/#counter-functions
|
||||
RefPtr<CSSStyleValue> Parser::parse_counter_value(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
|
@ -8460,6 +8526,10 @@ Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue>> Parser::parse_css_value(Prope
|
|||
if (auto parsed_value = parse_shadow_value(tokens, AllowInsetKeyword::Yes); parsed_value && !tokens.has_next_token())
|
||||
return parsed_value.release_nonnull();
|
||||
return ParseError::SyntaxError;
|
||||
case PropertyID::ColorScheme:
|
||||
if (auto parsed_value = parse_color_scheme_value(tokens); parsed_value && !tokens.has_next_token())
|
||||
return parsed_value.release_nonnull();
|
||||
return ParseError::SyntaxError;
|
||||
case PropertyID::Columns:
|
||||
if (auto parsed_value = parse_columns_value(tokens); parsed_value && !tokens.has_next_token())
|
||||
return parsed_value.release_nonnull();
|
||||
|
|
|
@ -292,6 +292,7 @@ private:
|
|||
RefPtr<CSSStyleValue> parse_oklch_color_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<CSSStyleValue> parse_color_function(TokenStream<ComponentValue>&);
|
||||
RefPtr<CSSStyleValue> parse_color_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<CSSStyleValue> parse_color_scheme_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<CSSStyleValue> parse_counter_value(TokenStream<ComponentValue>&);
|
||||
enum class AllowReversed {
|
||||
No,
|
||||
|
|
|
@ -866,6 +866,12 @@
|
|||
"hashless-hex-color"
|
||||
]
|
||||
},
|
||||
"color-scheme": {
|
||||
"affects-layout": false,
|
||||
"animation-type": "discrete",
|
||||
"inherited": true,
|
||||
"initial": "normal"
|
||||
},
|
||||
"column-count": {
|
||||
"animation-type": "by-computed-value",
|
||||
"inherited": false,
|
||||
|
|
|
@ -141,16 +141,19 @@ Color CSSKeywordValue::to_color(Optional<Layout::NodeWithStyle const&> node) con
|
|||
return node->computed_values().color();
|
||||
}
|
||||
|
||||
// First, handle <system-color>s, since they don't require a node.
|
||||
PreferredColorScheme scheme = PreferredColorScheme::Light;
|
||||
if (node.has_value()) {
|
||||
scheme = node->computed_values().color_scheme();
|
||||
}
|
||||
|
||||
// First, handle <system-color>s, since they don't strictly require a node.
|
||||
// https://www.w3.org/TR/css-color-4/#css-system-colors
|
||||
// https://www.w3.org/TR/css-color-4/#deprecated-system-colors
|
||||
switch (keyword()) {
|
||||
case Keyword::Accentcolor:
|
||||
return SystemColor::accent_color();
|
||||
return SystemColor::accent_color(scheme);
|
||||
case Keyword::Accentcolortext:
|
||||
return SystemColor::accent_color_text();
|
||||
case Keyword::Activetext:
|
||||
return SystemColor::active_text();
|
||||
return SystemColor::accent_color_text(scheme);
|
||||
case Keyword::Buttonborder:
|
||||
case Keyword::Activeborder:
|
||||
case Keyword::Inactiveborder:
|
||||
|
@ -159,14 +162,14 @@ Color CSSKeywordValue::to_color(Optional<Layout::NodeWithStyle const&> node) con
|
|||
case Keyword::Threedlightshadow:
|
||||
case Keyword::Threedshadow:
|
||||
case Keyword::Windowframe:
|
||||
return SystemColor::button_border();
|
||||
return SystemColor::button_border(scheme);
|
||||
case Keyword::Buttonface:
|
||||
case Keyword::Buttonhighlight:
|
||||
case Keyword::Buttonshadow:
|
||||
case Keyword::Threedface:
|
||||
return SystemColor::button_face();
|
||||
return SystemColor::button_face(scheme);
|
||||
case Keyword::Buttontext:
|
||||
return SystemColor::button_text();
|
||||
return SystemColor::button_text(scheme);
|
||||
case Keyword::Canvas:
|
||||
case Keyword::Appworkspace:
|
||||
case Keyword::Background:
|
||||
|
@ -175,35 +178,33 @@ Color CSSKeywordValue::to_color(Optional<Layout::NodeWithStyle const&> node) con
|
|||
case Keyword::Menu:
|
||||
case Keyword::Scrollbar:
|
||||
case Keyword::Window:
|
||||
return SystemColor::canvas();
|
||||
return SystemColor::canvas(scheme);
|
||||
case Keyword::Canvastext:
|
||||
case Keyword::Activecaption:
|
||||
case Keyword::Captiontext:
|
||||
case Keyword::Infotext:
|
||||
case Keyword::Menutext:
|
||||
case Keyword::Windowtext:
|
||||
return SystemColor::canvas_text();
|
||||
return SystemColor::canvas_text(scheme);
|
||||
case Keyword::Field:
|
||||
return SystemColor::field();
|
||||
return SystemColor::field(scheme);
|
||||
case Keyword::Fieldtext:
|
||||
return SystemColor::field_text();
|
||||
return SystemColor::field_text(scheme);
|
||||
case Keyword::Graytext:
|
||||
case Keyword::Inactivecaptiontext:
|
||||
return SystemColor::gray_text();
|
||||
return SystemColor::gray_text(scheme);
|
||||
case Keyword::Highlight:
|
||||
return SystemColor::highlight();
|
||||
return SystemColor::highlight(scheme);
|
||||
case Keyword::Highlighttext:
|
||||
return SystemColor::highlight_text();
|
||||
return SystemColor::highlight_text(scheme);
|
||||
case Keyword::Mark:
|
||||
return SystemColor::mark();
|
||||
return SystemColor::mark(scheme);
|
||||
case Keyword::Marktext:
|
||||
return SystemColor::mark_text();
|
||||
return SystemColor::mark_text(scheme);
|
||||
case Keyword::Selecteditem:
|
||||
return SystemColor::selected_item();
|
||||
return SystemColor::selected_item(scheme);
|
||||
case Keyword::Selecteditemtext:
|
||||
return SystemColor::selected_item_text();
|
||||
case Keyword::Visitedtext:
|
||||
return SystemColor::visited_text();
|
||||
return SystemColor::selected_item_text(scheme);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -213,9 +214,19 @@ Color CSSKeywordValue::to_color(Optional<Layout::NodeWithStyle const&> node) con
|
|||
return Color::Black;
|
||||
}
|
||||
|
||||
auto& document = node->document();
|
||||
if (keyword() == Keyword::LibwebLink || keyword() == Keyword::Linktext)
|
||||
return document.normal_link_color();
|
||||
auto const& document = node->document();
|
||||
|
||||
switch (keyword()) {
|
||||
case Keyword::LibwebLink:
|
||||
case Keyword::Linktext:
|
||||
return document.normal_link_color().value_or(SystemColor::link_text(scheme));
|
||||
case Keyword::Visitedtext:
|
||||
return document.visited_link_color().value_or(SystemColor::visited_text(scheme));
|
||||
case Keyword::Activetext:
|
||||
return document.active_link_color().value_or(SystemColor::active_text(scheme));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
auto palette = document.page().palette();
|
||||
switch (keyword()) {
|
||||
|
|
34
Libraries/LibWeb/CSS/StyleValues/ColorSchemeStyleValue.cpp
Normal file
34
Libraries/LibWeb/CSS/StyleValues/ColorSchemeStyleValue.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Ladybird contributors
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "ColorSchemeStyleValue.h"
|
||||
#include <LibWeb/CSS/Serialize.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
String ColorSchemeStyleValue::to_string(SerializationMode) const
|
||||
{
|
||||
if (schemes().is_empty())
|
||||
return "normal"_string;
|
||||
|
||||
StringBuilder builder;
|
||||
bool first = true;
|
||||
for (auto const& scheme : schemes()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
builder.append(' ');
|
||||
}
|
||||
builder.append(serialize_an_identifier(scheme));
|
||||
}
|
||||
|
||||
if (only())
|
||||
builder.append(" only"sv);
|
||||
|
||||
return MUST(builder.to_string());
|
||||
}
|
||||
|
||||
}
|
44
Libraries/LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h
Normal file
44
Libraries/LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Ladybird contributors
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/CSS/CSSStyleValue.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
class ColorSchemeStyleValue final : public StyleValueWithDefaultOperators<ColorSchemeStyleValue> {
|
||||
public:
|
||||
static ValueComparingNonnullRefPtr<ColorSchemeStyleValue> create(Vector<String> schemes, bool only)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ColorSchemeStyleValue(move(schemes), only));
|
||||
}
|
||||
static ValueComparingNonnullRefPtr<ColorSchemeStyleValue> normal()
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ColorSchemeStyleValue({}, false));
|
||||
}
|
||||
virtual ~ColorSchemeStyleValue() override = default;
|
||||
|
||||
Vector<String> const& schemes() const { return m_properties.schemes; }
|
||||
bool const& only() const { return m_properties.only; }
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
|
||||
bool properties_equal(ColorSchemeStyleValue const& other) const { return m_properties == other.m_properties; }
|
||||
|
||||
private:
|
||||
ColorSchemeStyleValue(Vector<String> schemes, bool only)
|
||||
: StyleValueWithDefaultOperators(Type::ColorScheme)
|
||||
, m_properties { .schemes = move(schemes), .only = only }
|
||||
{
|
||||
}
|
||||
|
||||
struct Properties {
|
||||
Vector<String> schemes;
|
||||
bool only;
|
||||
bool operator==(Properties const&) const = default;
|
||||
} m_properties;
|
||||
};
|
||||
}
|
|
@ -8,98 +8,134 @@
|
|||
|
||||
namespace Web::CSS::SystemColor {
|
||||
|
||||
Color accent_color()
|
||||
Color accent_color(PreferredColorScheme)
|
||||
{
|
||||
return Color(61, 174, 233);
|
||||
}
|
||||
|
||||
Color accent_color_text()
|
||||
Color accent_color_text(PreferredColorScheme)
|
||||
{
|
||||
return Color(255, 255, 255);
|
||||
}
|
||||
|
||||
Color active_text()
|
||||
Color active_text(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(213, 97, 82);
|
||||
}
|
||||
return Color(255, 0, 0);
|
||||
}
|
||||
|
||||
Color button_border()
|
||||
Color button_border(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(72, 72, 72);
|
||||
}
|
||||
return Color(128, 128, 128);
|
||||
}
|
||||
|
||||
Color button_face()
|
||||
Color button_face(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(32, 29, 25);
|
||||
}
|
||||
return Color(212, 208, 200);
|
||||
}
|
||||
|
||||
Color button_text()
|
||||
Color button_text(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(235, 235, 235);
|
||||
}
|
||||
return Color(0, 0, 0);
|
||||
}
|
||||
|
||||
Color canvas()
|
||||
Color canvas(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(20, 20, 20);
|
||||
}
|
||||
return Color(255, 255, 255);
|
||||
}
|
||||
|
||||
Color canvas_text()
|
||||
Color canvas_text(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(235, 235, 235);
|
||||
}
|
||||
return Color(0, 0, 0);
|
||||
}
|
||||
|
||||
Color field()
|
||||
Color field(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(32, 29, 25);
|
||||
}
|
||||
return Color(255, 255, 255);
|
||||
}
|
||||
|
||||
Color field_text()
|
||||
Color field_text(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(235, 235, 235);
|
||||
}
|
||||
return Color(0, 0, 0);
|
||||
}
|
||||
|
||||
Color gray_text()
|
||||
Color gray_text(PreferredColorScheme)
|
||||
{
|
||||
return Color(128, 128, 128);
|
||||
}
|
||||
|
||||
Color highlight()
|
||||
Color highlight(PreferredColorScheme)
|
||||
{
|
||||
return Color(61, 174, 233);
|
||||
}
|
||||
|
||||
Color highlight_text()
|
||||
Color highlight_text(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(20, 20, 20);
|
||||
}
|
||||
return Color(255, 255, 255);
|
||||
}
|
||||
|
||||
Color link_text()
|
||||
Color link_text(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(100, 149, 237);
|
||||
}
|
||||
return Color(0, 0, 238);
|
||||
}
|
||||
|
||||
Color mark()
|
||||
Color mark(PreferredColorScheme)
|
||||
{
|
||||
return Color(255, 255, 0);
|
||||
}
|
||||
|
||||
Color mark_text()
|
||||
Color mark_text(PreferredColorScheme)
|
||||
{
|
||||
return Color(0, 0, 0);
|
||||
}
|
||||
|
||||
Color selected_item()
|
||||
Color selected_item(PreferredColorScheme)
|
||||
{
|
||||
return Color(61, 174, 233);
|
||||
}
|
||||
|
||||
Color selected_item_text()
|
||||
Color selected_item_text(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(20, 20, 20);
|
||||
}
|
||||
return Color(255, 255, 255);
|
||||
}
|
||||
|
||||
Color visited_text()
|
||||
Color visited_text(PreferredColorScheme scheme)
|
||||
{
|
||||
if (scheme == PreferredColorScheme::Dark) {
|
||||
return Color(156, 113, 212);
|
||||
}
|
||||
return Color(85, 26, 139);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,29 +7,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibWeb/CSS/PreferredColorScheme.h>
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#css-system-colors
|
||||
namespace Web::CSS::SystemColor {
|
||||
|
||||
// FIXME: Provide colors for `color-scheme: dark` once we support that.
|
||||
Color accent_color();
|
||||
Color accent_color_text();
|
||||
Color active_text();
|
||||
Color button_border();
|
||||
Color button_face();
|
||||
Color button_text();
|
||||
Color canvas();
|
||||
Color canvas_text();
|
||||
Color field();
|
||||
Color field_text();
|
||||
Color gray_text();
|
||||
Color highlight();
|
||||
Color highlight_text();
|
||||
Color link_text();
|
||||
Color mark();
|
||||
Color mark_text();
|
||||
Color selected_item();
|
||||
Color selected_item_text();
|
||||
Color visited_text();
|
||||
|
||||
Color accent_color(PreferredColorScheme);
|
||||
Color accent_color_text(PreferredColorScheme);
|
||||
Color active_text(PreferredColorScheme);
|
||||
Color button_border(PreferredColorScheme);
|
||||
Color button_face(PreferredColorScheme);
|
||||
Color button_text(PreferredColorScheme);
|
||||
Color canvas(PreferredColorScheme);
|
||||
Color canvas_text(PreferredColorScheme);
|
||||
Color field(PreferredColorScheme);
|
||||
Color field_text(PreferredColorScheme);
|
||||
Color gray_text(PreferredColorScheme);
|
||||
Color highlight(PreferredColorScheme);
|
||||
Color highlight_text(PreferredColorScheme);
|
||||
Color link_text(PreferredColorScheme);
|
||||
Color mark(PreferredColorScheme);
|
||||
Color mark_text(PreferredColorScheme);
|
||||
Color selected_item(PreferredColorScheme);
|
||||
Color selected_item_text(PreferredColorScheme);
|
||||
Color visited_text(PreferredColorScheme);
|
||||
}
|
||||
|
|
|
@ -1769,25 +1769,19 @@ void Document::release_events()
|
|||
// Do nothing
|
||||
}
|
||||
|
||||
Color Document::normal_link_color() const
|
||||
Optional<Color> Document::normal_link_color() const
|
||||
{
|
||||
if (m_normal_link_color.has_value())
|
||||
return m_normal_link_color.value();
|
||||
return CSS::SystemColor::link_text();
|
||||
return m_normal_link_color;
|
||||
}
|
||||
|
||||
Color Document::active_link_color() const
|
||||
Optional<Color> Document::active_link_color() const
|
||||
{
|
||||
if (m_active_link_color.has_value())
|
||||
return m_active_link_color.value();
|
||||
return CSS::SystemColor::active_text();
|
||||
return m_active_link_color;
|
||||
}
|
||||
|
||||
Color Document::visited_link_color() const
|
||||
Optional<Color> Document::visited_link_color() const
|
||||
{
|
||||
if (m_visited_link_color.has_value())
|
||||
return m_visited_link_color.value();
|
||||
return CSS::SystemColor::visited_text();
|
||||
return m_visited_link_color;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#relevant-settings-object
|
||||
|
@ -5912,13 +5906,38 @@ RefPtr<Painting::DisplayList> Document::record_display_list(PaintConfig config)
|
|||
auto display_list = Painting::DisplayList::create();
|
||||
Painting::DisplayListRecorder display_list_recorder(display_list);
|
||||
|
||||
// https://drafts.csswg.org/css-color-adjust-1/#color-scheme-effect
|
||||
// On the root element, the used color scheme additionally must affect the surface color of the canvas, and the viewport’s scrollbars.
|
||||
auto color_scheme = CSS::PreferredColorScheme::Light;
|
||||
if (auto* html_element = this->html_element(); html_element && html_element->layout_node()) {
|
||||
if (html_element->layout_node()->computed_values().color_scheme() == CSS::PreferredColorScheme::Dark)
|
||||
color_scheme = CSS::PreferredColorScheme::Dark;
|
||||
}
|
||||
|
||||
// .. in the case of embedded documents typically rendered over a transparent canvas
|
||||
// (such as provided via an HTML iframe element), if the used color scheme of the element
|
||||
// and the used color scheme of the embedded document’s root element do not match,
|
||||
// then the UA must use an opaque canvas of the Canvas color appropriate to the
|
||||
// embedded document’s used color scheme instead of a transparent canvas.
|
||||
bool opaque_canvas = false;
|
||||
if (auto container_element = navigable()->container(); container_element && container_element->layout_node()) {
|
||||
auto container_scheme = container_element->layout_node()->computed_values().color_scheme();
|
||||
if (container_scheme == CSS::PreferredColorScheme::Auto)
|
||||
container_scheme = CSS::PreferredColorScheme::Light;
|
||||
|
||||
opaque_canvas = container_scheme != color_scheme;
|
||||
}
|
||||
|
||||
if (config.canvas_fill_rect.has_value()) {
|
||||
display_list_recorder.fill_rect(config.canvas_fill_rect.value(), CSS::SystemColor::canvas());
|
||||
display_list_recorder.fill_rect(config.canvas_fill_rect.value(), CSS::SystemColor::canvas(color_scheme));
|
||||
}
|
||||
|
||||
auto viewport_rect = page().css_to_device_rect(this->viewport_rect());
|
||||
Gfx::IntRect bitmap_rect { {}, viewport_rect.size().to_type<int>() };
|
||||
|
||||
if (opaque_canvas)
|
||||
display_list_recorder.fill_rect(bitmap_rect, CSS::SystemColor::canvas(color_scheme));
|
||||
|
||||
display_list_recorder.fill_rect(bitmap_rect, background_color());
|
||||
if (!paintable()) {
|
||||
VERIFY_NOT_REACHED();
|
||||
|
|
|
@ -233,13 +233,13 @@ public:
|
|||
Color background_color() const;
|
||||
Vector<CSS::BackgroundLayerData> const* background_layers() const;
|
||||
|
||||
Color normal_link_color() const;
|
||||
Optional<Color> normal_link_color() const;
|
||||
void set_normal_link_color(Color);
|
||||
|
||||
Color active_link_color() const;
|
||||
Optional<Color> active_link_color() const;
|
||||
void set_active_link_color(Color);
|
||||
|
||||
Color visited_link_color() const;
|
||||
Optional<Color> visited_link_color() const;
|
||||
void set_visited_link_color(Color);
|
||||
|
||||
void update_style();
|
||||
|
|
|
@ -116,6 +116,7 @@ class BasicShapeStyleValue;
|
|||
class BorderRadiusStyleValue;
|
||||
class CalculatedStyleValue;
|
||||
class Clip;
|
||||
class ColorSchemeStyleValue;
|
||||
class ConicGradientStyleValue;
|
||||
class ContentStyleValue;
|
||||
class CounterDefinitionsStyleValue;
|
||||
|
|
|
@ -1181,8 +1181,7 @@ void HTMLInputElement::computed_properties_changed()
|
|||
if (!appearance.has_value() || *appearance == CSS::Appearance::None)
|
||||
return;
|
||||
|
||||
auto palette = document().page().palette();
|
||||
auto accent_color = palette.color(ColorRole::Accent).to_string();
|
||||
auto accent_color = MUST(String::from_utf8(CSS::string_from_keyword(CSS::Keyword::Accentcolor)));
|
||||
|
||||
auto const& accent_color_property = computed_properties()->property(CSS::PropertyID::AccentColor);
|
||||
if (accent_color_property.has_color())
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <LibWeb/Bindings/HTMLProgressElementPrototype.h>
|
||||
#include <LibWeb/CSS/ComputedProperties.h>
|
||||
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/ElementFactory.h>
|
||||
|
@ -132,8 +133,7 @@ void HTMLProgressElement::update_progress_value_element()
|
|||
|
||||
void HTMLProgressElement::computed_properties_changed()
|
||||
{
|
||||
auto palette = document().page().palette();
|
||||
auto accent_color = palette.color(ColorRole::Accent).to_string();
|
||||
auto accent_color = MUST(String::from_utf8(CSS::string_from_keyword(CSS::Keyword::Accentcolor)));
|
||||
|
||||
auto const& accent_color_property = computed_properties()->property(CSS::PropertyID::AccentColor);
|
||||
if (accent_color_property.has_color())
|
||||
|
|
|
@ -328,7 +328,15 @@ void NodeWithStyle::apply_style(const CSS::ComputedProperties& computed_style)
|
|||
{
|
||||
auto& computed_values = mutable_computed_values();
|
||||
|
||||
// NOTE: color must be set first to ensure currentColor can be resolved in other properties (e.g. background-color).
|
||||
// NOTE: color-scheme must be set first to ensure system colors can be resolved correctly.
|
||||
auto preferred_color_scheme = document().page().preferred_color_scheme();
|
||||
// FIXME: We can't just check for Auto because page().preferred_color_scheme() returns garbage data after startup.
|
||||
if (preferred_color_scheme != CSS::PreferredColorScheme::Dark && preferred_color_scheme != CSS::PreferredColorScheme::Light) {
|
||||
preferred_color_scheme = document().page().palette().is_dark() ? CSS::PreferredColorScheme::Dark : CSS::PreferredColorScheme::Light;
|
||||
}
|
||||
computed_values.set_color_scheme(computed_style.color_scheme(preferred_color_scheme));
|
||||
|
||||
// NOTE: color must be set second to ensure currentColor can be resolved in other properties (e.g. background-color).
|
||||
computed_values.set_color(computed_style.color_or_fallback(CSS::PropertyID::Color, *this, CSS::InitialValues::color()));
|
||||
|
||||
// NOTE: We have to be careful that font-related properties get set in the right order.
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#include <AK/CircularQueue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
|
||||
|
@ -579,7 +580,7 @@ Optional<BordersData> borders_data_for_outline(Layout::Node const& layout_node,
|
|||
if (outline_style == CSS::OutlineStyle::Auto) {
|
||||
// `auto` lets us do whatever we want for the outline. 2px of the link colour seems reasonable.
|
||||
line_style = CSS::LineStyle::Dotted;
|
||||
outline_color = layout_node.document().normal_link_color();
|
||||
outline_color = CSS::CSSKeywordValue::create(CSS::Keyword::Linktext)->to_color(*static_cast<Layout::NodeWithStyle const*>(&layout_node));
|
||||
outline_width = 2;
|
||||
} else {
|
||||
line_style = CSS::keyword_to_line_style(CSS::to_keyword(outline_style)).value_or(CSS::LineStyle::None);
|
||||
|
|
|
@ -72,10 +72,8 @@ void CheckBoxPaintable::paint(PaintContext& context, PaintPhase phase) const
|
|||
auto checkbox_rect = context.enclosing_device_rect(absolute_rect()).to_type<int>();
|
||||
auto checkbox_radius = checkbox_rect.width() / 5;
|
||||
|
||||
auto& palette = context.palette();
|
||||
|
||||
auto shade = [&](Color color, float amount) {
|
||||
return InputColors::get_shade(color, amount, palette.is_dark());
|
||||
return InputColors::get_shade(color, amount, computed_values().color_scheme());
|
||||
};
|
||||
|
||||
auto modify_color = [&](Color color) {
|
||||
|
@ -84,7 +82,7 @@ void CheckBoxPaintable::paint(PaintContext& context, PaintPhase phase) const
|
|||
return color;
|
||||
};
|
||||
|
||||
auto input_colors = compute_input_colors(palette, computed_values().accent_color());
|
||||
auto input_colors = compute_input_colors(computed_values().color_scheme(), computed_values().accent_color());
|
||||
|
||||
auto increase_contrast = [&](Color color, Color background) {
|
||||
auto constexpr min_contrast = 2;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <AK/Optional.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibWeb/CSS/SystemColor.h>
|
||||
|
||||
namespace Web::Painting {
|
||||
|
||||
|
@ -25,24 +26,24 @@ struct InputColors {
|
|||
Color background_color(bool enabled) { return enabled ? base : light_gray; }
|
||||
Color border_color(bool enabled) { return enabled ? gray : mid_gray; }
|
||||
|
||||
static Color get_shade(Color color, float amount, bool is_dark_theme)
|
||||
static Color get_shade(Color color, float amount, CSS::PreferredColorScheme color_scheme)
|
||||
{
|
||||
return color.mixed_with(is_dark_theme ? Color::Black : Color::White, amount);
|
||||
auto base_color = CSS::SystemColor::canvas(color_scheme);
|
||||
return color.mixed_with(base_color, amount);
|
||||
}
|
||||
};
|
||||
|
||||
static InputColors compute_input_colors(Palette const& palette, Optional<Color> accent_color)
|
||||
static InputColors compute_input_colors(CSS::PreferredColorScheme color_scheme, Optional<Color> accent_color)
|
||||
{
|
||||
// These shades have been picked to work well for all themes and have enough variation to paint
|
||||
// all input states (disabled, enabled, checked, etc).
|
||||
bool dark_theme = palette.is_dark();
|
||||
auto base_text_color = palette.color(ColorRole::BaseText);
|
||||
auto accent = accent_color.value_or(palette.color(ColorRole::Accent));
|
||||
auto base = InputColors::get_shade(base_text_color.inverted(), 0.8f, dark_theme);
|
||||
auto dark_gray = InputColors::get_shade(base_text_color, 0.3f, dark_theme);
|
||||
auto gray = InputColors::get_shade(dark_gray, 0.4f, dark_theme);
|
||||
auto mid_gray = InputColors::get_shade(gray, 0.3f, dark_theme);
|
||||
auto light_gray = InputColors::get_shade(mid_gray, 0.3f, dark_theme);
|
||||
auto base_text_color = CSS::SystemColor::canvas_text(color_scheme);
|
||||
auto accent = accent_color.value_or(CSS::SystemColor::accent_color(color_scheme));
|
||||
auto base = InputColors::get_shade(base_text_color.inverted(), 0.8f, color_scheme);
|
||||
auto dark_gray = InputColors::get_shade(base_text_color, 0.3f, color_scheme);
|
||||
auto gray = InputColors::get_shade(dark_gray, 0.4f, color_scheme);
|
||||
auto mid_gray = InputColors::get_shade(gray, 0.3f, color_scheme);
|
||||
auto light_gray = InputColors::get_shade(mid_gray, 0.3f, color_scheme);
|
||||
return InputColors {
|
||||
.accent = accent,
|
||||
.base = base,
|
||||
|
|
|
@ -692,10 +692,10 @@ void paint_text_fragment(PaintContext& context, TextPaintable const& paintable,
|
|||
|
||||
auto selection_rect = context.enclosing_device_rect(fragment.selection_rect()).to_type<int>();
|
||||
if (!selection_rect.is_empty()) {
|
||||
painter.fill_rect(selection_rect, CSS::SystemColor::highlight());
|
||||
painter.fill_rect(selection_rect, CSS::SystemColor::highlight(paintable.computed_values().color_scheme()));
|
||||
DisplayListRecorderStateSaver saver(painter);
|
||||
painter.add_clip_rect(selection_rect);
|
||||
painter.draw_text_run(baseline_start, *glyph_run, CSS::SystemColor::highlight_text(), fragment_absolute_device_rect.to_type<int>(), scale, fragment.orientation());
|
||||
painter.draw_text_run(baseline_start, *glyph_run, CSS::SystemColor::highlight_text(paintable.computed_values().color_scheme()), fragment_absolute_device_rect.to_type<int>(), scale, fragment.orientation());
|
||||
}
|
||||
|
||||
paint_text_decoration(context, paintable, fragment);
|
||||
|
|
|
@ -49,9 +49,8 @@ void RadioButtonPaintable::paint(PaintContext& context, PaintPhase phase) const
|
|||
|
||||
auto const& radio_button = static_cast<HTML::HTMLInputElement const&>(layout_box().dom_node());
|
||||
|
||||
auto& palette = context.palette();
|
||||
bool enabled = layout_box().dom_node().enabled();
|
||||
auto input_colors = compute_input_colors(palette, computed_values().accent_color());
|
||||
auto input_colors = compute_input_colors(computed_values().color_scheme(), computed_values().accent_color());
|
||||
|
||||
auto background_color = input_colors.background_color(enabled);
|
||||
auto accent = input_colors.accent;
|
||||
|
@ -71,7 +70,7 @@ void RadioButtonPaintable::paint(PaintContext& context, PaintPhase phase) const
|
|||
return input_colors.mid_gray;
|
||||
auto color = radio_color();
|
||||
if (being_pressed())
|
||||
color = InputColors::get_shade(color, 0.3f, palette.is_dark());
|
||||
color = InputColors::get_shade(color, 0.3f, computed_values().color_scheme());
|
||||
return color;
|
||||
}();
|
||||
|
||||
|
|
|
@ -130,12 +130,6 @@ String ProcessManager::generate_html()
|
|||
<title>Task Manager</title>
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/* FIXME: We should be able to remove the HTML style when "color-scheme" is supported */
|
||||
html {
|
||||
background-color: rgb(30, 30, 30);
|
||||
color: white;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background: rgb(57, 57, 57);
|
||||
}
|
||||
|
@ -147,6 +141,10 @@ String ProcessManager::generate_html()
|
|||
}
|
||||
}
|
||||
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
|
|
@ -79,13 +79,6 @@ String highlight_source(URL::URL const& url, URL::URL const& base_url, StringVie
|
|||
|
||||
constexpr inline StringView HTML_HIGHLIGHTER_STYLE = R"~~~(
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/* FIXME: We should be able to remove the HTML style when "color-scheme" is supported */
|
||||
html {
|
||||
background-color: rgb(30, 30, 30);
|
||||
color: white;
|
||||
counter-reset: line;
|
||||
}
|
||||
|
||||
:root {
|
||||
--comment-color: lightgreen;
|
||||
--keyword-color: orangered;
|
||||
|
@ -111,6 +104,10 @@ constexpr inline StringView HTML_HIGHLIGHTER_STYLE = R"~~~(
|
|||
}
|
||||
}
|
||||
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
.html {
|
||||
font-size: 10pt;
|
||||
font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
All supported properties and their default values exposed from CSSStyleDeclaration from getComputedStyle:
|
||||
'cssText': ''
|
||||
'length': '212'
|
||||
'length': '213'
|
||||
'parentRule': 'null'
|
||||
'cssFloat': 'none'
|
||||
'WebkitAlignContent': 'normal'
|
||||
|
@ -250,6 +250,8 @@ All supported properties and their default values exposed from CSSStyleDeclarati
|
|||
'clipRule': 'nonzero'
|
||||
'clip-rule': 'nonzero'
|
||||
'color': 'rgb(0, 0, 0)'
|
||||
'colorScheme': 'normal'
|
||||
'color-scheme': 'normal'
|
||||
'columnCount': 'auto'
|
||||
'column-count': 'auto'
|
||||
'columnGap': 'normal'
|
||||
|
|
|
@ -7,211 +7,212 @@ All properties associated with getComputedStyle(document.body):
|
|||
"4": "caption-side",
|
||||
"5": "clip-rule",
|
||||
"6": "color",
|
||||
"7": "cursor",
|
||||
"8": "direction",
|
||||
"9": "fill",
|
||||
"10": "fill-opacity",
|
||||
"11": "fill-rule",
|
||||
"12": "font-family",
|
||||
"13": "font-feature-settings",
|
||||
"14": "font-language-override",
|
||||
"15": "font-size",
|
||||
"16": "font-style",
|
||||
"17": "font-variant-alternates",
|
||||
"18": "font-variant-caps",
|
||||
"19": "font-variant-east-asian",
|
||||
"20": "font-variant-emoji",
|
||||
"21": "font-variant-ligatures",
|
||||
"22": "font-variant-numeric",
|
||||
"23": "font-variant-position",
|
||||
"24": "font-variation-settings",
|
||||
"25": "font-weight",
|
||||
"26": "font-width",
|
||||
"27": "image-rendering",
|
||||
"28": "letter-spacing",
|
||||
"29": "line-height",
|
||||
"30": "list-style-image",
|
||||
"31": "list-style-position",
|
||||
"32": "list-style-type",
|
||||
"33": "math-depth",
|
||||
"34": "math-shift",
|
||||
"35": "math-style",
|
||||
"36": "pointer-events",
|
||||
"37": "quotes",
|
||||
"38": "stroke",
|
||||
"39": "stroke-dasharray",
|
||||
"40": "stroke-dashoffset",
|
||||
"41": "stroke-linecap",
|
||||
"42": "stroke-linejoin",
|
||||
"43": "stroke-miterlimit",
|
||||
"44": "stroke-opacity",
|
||||
"45": "stroke-width",
|
||||
"46": "tab-size",
|
||||
"47": "text-align",
|
||||
"48": "text-anchor",
|
||||
"49": "text-decoration-line",
|
||||
"50": "text-indent",
|
||||
"51": "text-justify",
|
||||
"52": "text-shadow",
|
||||
"53": "text-transform",
|
||||
"54": "visibility",
|
||||
"55": "white-space",
|
||||
"56": "word-break",
|
||||
"57": "word-spacing",
|
||||
"58": "word-wrap",
|
||||
"59": "writing-mode",
|
||||
"60": "align-content",
|
||||
"61": "align-items",
|
||||
"62": "align-self",
|
||||
"63": "animation-delay",
|
||||
"64": "animation-direction",
|
||||
"65": "animation-duration",
|
||||
"66": "animation-fill-mode",
|
||||
"67": "animation-iteration-count",
|
||||
"68": "animation-name",
|
||||
"69": "animation-play-state",
|
||||
"70": "animation-timing-function",
|
||||
"71": "appearance",
|
||||
"72": "aspect-ratio",
|
||||
"73": "backdrop-filter",
|
||||
"74": "background-attachment",
|
||||
"75": "background-clip",
|
||||
"76": "background-color",
|
||||
"77": "background-image",
|
||||
"78": "background-origin",
|
||||
"79": "background-position-x",
|
||||
"80": "background-position-y",
|
||||
"81": "background-repeat",
|
||||
"82": "background-size",
|
||||
"83": "border-bottom-color",
|
||||
"84": "border-bottom-left-radius",
|
||||
"85": "border-bottom-right-radius",
|
||||
"86": "border-bottom-style",
|
||||
"87": "border-bottom-width",
|
||||
"88": "border-left-color",
|
||||
"89": "border-left-style",
|
||||
"90": "border-left-width",
|
||||
"91": "border-right-color",
|
||||
"92": "border-right-style",
|
||||
"93": "border-right-width",
|
||||
"94": "border-top-color",
|
||||
"95": "border-top-left-radius",
|
||||
"96": "border-top-right-radius",
|
||||
"97": "border-top-style",
|
||||
"98": "border-top-width",
|
||||
"99": "bottom",
|
||||
"100": "box-shadow",
|
||||
"101": "box-sizing",
|
||||
"102": "clear",
|
||||
"103": "clip",
|
||||
"104": "clip-path",
|
||||
"105": "column-count",
|
||||
"106": "column-gap",
|
||||
"107": "column-span",
|
||||
"108": "column-width",
|
||||
"109": "content",
|
||||
"110": "content-visibility",
|
||||
"111": "counter-increment",
|
||||
"112": "counter-reset",
|
||||
"113": "counter-set",
|
||||
"114": "cx",
|
||||
"115": "cy",
|
||||
"116": "display",
|
||||
"117": "filter",
|
||||
"118": "flex-basis",
|
||||
"119": "flex-direction",
|
||||
"120": "flex-grow",
|
||||
"121": "flex-shrink",
|
||||
"122": "flex-wrap",
|
||||
"123": "float",
|
||||
"124": "grid-auto-columns",
|
||||
"125": "grid-auto-flow",
|
||||
"126": "grid-auto-rows",
|
||||
"127": "grid-column-end",
|
||||
"128": "grid-column-start",
|
||||
"129": "grid-row-end",
|
||||
"130": "grid-row-start",
|
||||
"131": "grid-template-areas",
|
||||
"132": "grid-template-columns",
|
||||
"133": "grid-template-rows",
|
||||
"134": "height",
|
||||
"135": "inline-size",
|
||||
"136": "inset-block-end",
|
||||
"137": "inset-block-start",
|
||||
"138": "inset-inline-end",
|
||||
"139": "inset-inline-start",
|
||||
"140": "justify-content",
|
||||
"141": "justify-items",
|
||||
"142": "justify-self",
|
||||
"143": "left",
|
||||
"144": "margin-block-end",
|
||||
"145": "margin-block-start",
|
||||
"146": "margin-bottom",
|
||||
"147": "margin-inline-end",
|
||||
"148": "margin-inline-start",
|
||||
"149": "margin-left",
|
||||
"150": "margin-right",
|
||||
"151": "margin-top",
|
||||
"152": "mask",
|
||||
"153": "mask-image",
|
||||
"154": "mask-type",
|
||||
"155": "max-height",
|
||||
"156": "max-inline-size",
|
||||
"157": "max-width",
|
||||
"158": "min-height",
|
||||
"159": "min-inline-size",
|
||||
"160": "min-width",
|
||||
"161": "object-fit",
|
||||
"162": "object-position",
|
||||
"163": "opacity",
|
||||
"164": "order",
|
||||
"165": "outline-color",
|
||||
"166": "outline-offset",
|
||||
"167": "outline-style",
|
||||
"168": "outline-width",
|
||||
"169": "overflow-x",
|
||||
"170": "overflow-y",
|
||||
"171": "padding-block-end",
|
||||
"172": "padding-block-start",
|
||||
"173": "padding-bottom",
|
||||
"174": "padding-inline-end",
|
||||
"175": "padding-inline-start",
|
||||
"176": "padding-left",
|
||||
"177": "padding-right",
|
||||
"178": "padding-top",
|
||||
"179": "position",
|
||||
"180": "r",
|
||||
"181": "right",
|
||||
"182": "rotate",
|
||||
"183": "row-gap",
|
||||
"184": "rx",
|
||||
"185": "ry",
|
||||
"186": "scale",
|
||||
"187": "scrollbar-gutter",
|
||||
"188": "scrollbar-width",
|
||||
"189": "stop-color",
|
||||
"190": "stop-opacity",
|
||||
"191": "table-layout",
|
||||
"192": "text-decoration-color",
|
||||
"193": "text-decoration-style",
|
||||
"194": "text-decoration-thickness",
|
||||
"195": "text-overflow",
|
||||
"196": "top",
|
||||
"197": "transform",
|
||||
"198": "transform-box",
|
||||
"199": "transform-origin",
|
||||
"200": "transition-delay",
|
||||
"201": "transition-duration",
|
||||
"202": "transition-property",
|
||||
"203": "transition-timing-function",
|
||||
"204": "translate",
|
||||
"205": "unicode-bidi",
|
||||
"206": "user-select",
|
||||
"207": "vertical-align",
|
||||
"208": "width",
|
||||
"209": "x",
|
||||
"210": "y",
|
||||
"211": "z-index"
|
||||
"7": "color-scheme",
|
||||
"8": "cursor",
|
||||
"9": "direction",
|
||||
"10": "fill",
|
||||
"11": "fill-opacity",
|
||||
"12": "fill-rule",
|
||||
"13": "font-family",
|
||||
"14": "font-feature-settings",
|
||||
"15": "font-language-override",
|
||||
"16": "font-size",
|
||||
"17": "font-style",
|
||||
"18": "font-variant-alternates",
|
||||
"19": "font-variant-caps",
|
||||
"20": "font-variant-east-asian",
|
||||
"21": "font-variant-emoji",
|
||||
"22": "font-variant-ligatures",
|
||||
"23": "font-variant-numeric",
|
||||
"24": "font-variant-position",
|
||||
"25": "font-variation-settings",
|
||||
"26": "font-weight",
|
||||
"27": "font-width",
|
||||
"28": "image-rendering",
|
||||
"29": "letter-spacing",
|
||||
"30": "line-height",
|
||||
"31": "list-style-image",
|
||||
"32": "list-style-position",
|
||||
"33": "list-style-type",
|
||||
"34": "math-depth",
|
||||
"35": "math-shift",
|
||||
"36": "math-style",
|
||||
"37": "pointer-events",
|
||||
"38": "quotes",
|
||||
"39": "stroke",
|
||||
"40": "stroke-dasharray",
|
||||
"41": "stroke-dashoffset",
|
||||
"42": "stroke-linecap",
|
||||
"43": "stroke-linejoin",
|
||||
"44": "stroke-miterlimit",
|
||||
"45": "stroke-opacity",
|
||||
"46": "stroke-width",
|
||||
"47": "tab-size",
|
||||
"48": "text-align",
|
||||
"49": "text-anchor",
|
||||
"50": "text-decoration-line",
|
||||
"51": "text-indent",
|
||||
"52": "text-justify",
|
||||
"53": "text-shadow",
|
||||
"54": "text-transform",
|
||||
"55": "visibility",
|
||||
"56": "white-space",
|
||||
"57": "word-break",
|
||||
"58": "word-spacing",
|
||||
"59": "word-wrap",
|
||||
"60": "writing-mode",
|
||||
"61": "align-content",
|
||||
"62": "align-items",
|
||||
"63": "align-self",
|
||||
"64": "animation-delay",
|
||||
"65": "animation-direction",
|
||||
"66": "animation-duration",
|
||||
"67": "animation-fill-mode",
|
||||
"68": "animation-iteration-count",
|
||||
"69": "animation-name",
|
||||
"70": "animation-play-state",
|
||||
"71": "animation-timing-function",
|
||||
"72": "appearance",
|
||||
"73": "aspect-ratio",
|
||||
"74": "backdrop-filter",
|
||||
"75": "background-attachment",
|
||||
"76": "background-clip",
|
||||
"77": "background-color",
|
||||
"78": "background-image",
|
||||
"79": "background-origin",
|
||||
"80": "background-position-x",
|
||||
"81": "background-position-y",
|
||||
"82": "background-repeat",
|
||||
"83": "background-size",
|
||||
"84": "border-bottom-color",
|
||||
"85": "border-bottom-left-radius",
|
||||
"86": "border-bottom-right-radius",
|
||||
"87": "border-bottom-style",
|
||||
"88": "border-bottom-width",
|
||||
"89": "border-left-color",
|
||||
"90": "border-left-style",
|
||||
"91": "border-left-width",
|
||||
"92": "border-right-color",
|
||||
"93": "border-right-style",
|
||||
"94": "border-right-width",
|
||||
"95": "border-top-color",
|
||||
"96": "border-top-left-radius",
|
||||
"97": "border-top-right-radius",
|
||||
"98": "border-top-style",
|
||||
"99": "border-top-width",
|
||||
"100": "bottom",
|
||||
"101": "box-shadow",
|
||||
"102": "box-sizing",
|
||||
"103": "clear",
|
||||
"104": "clip",
|
||||
"105": "clip-path",
|
||||
"106": "column-count",
|
||||
"107": "column-gap",
|
||||
"108": "column-span",
|
||||
"109": "column-width",
|
||||
"110": "content",
|
||||
"111": "content-visibility",
|
||||
"112": "counter-increment",
|
||||
"113": "counter-reset",
|
||||
"114": "counter-set",
|
||||
"115": "cx",
|
||||
"116": "cy",
|
||||
"117": "display",
|
||||
"118": "filter",
|
||||
"119": "flex-basis",
|
||||
"120": "flex-direction",
|
||||
"121": "flex-grow",
|
||||
"122": "flex-shrink",
|
||||
"123": "flex-wrap",
|
||||
"124": "float",
|
||||
"125": "grid-auto-columns",
|
||||
"126": "grid-auto-flow",
|
||||
"127": "grid-auto-rows",
|
||||
"128": "grid-column-end",
|
||||
"129": "grid-column-start",
|
||||
"130": "grid-row-end",
|
||||
"131": "grid-row-start",
|
||||
"132": "grid-template-areas",
|
||||
"133": "grid-template-columns",
|
||||
"134": "grid-template-rows",
|
||||
"135": "height",
|
||||
"136": "inline-size",
|
||||
"137": "inset-block-end",
|
||||
"138": "inset-block-start",
|
||||
"139": "inset-inline-end",
|
||||
"140": "inset-inline-start",
|
||||
"141": "justify-content",
|
||||
"142": "justify-items",
|
||||
"143": "justify-self",
|
||||
"144": "left",
|
||||
"145": "margin-block-end",
|
||||
"146": "margin-block-start",
|
||||
"147": "margin-bottom",
|
||||
"148": "margin-inline-end",
|
||||
"149": "margin-inline-start",
|
||||
"150": "margin-left",
|
||||
"151": "margin-right",
|
||||
"152": "margin-top",
|
||||
"153": "mask",
|
||||
"154": "mask-image",
|
||||
"155": "mask-type",
|
||||
"156": "max-height",
|
||||
"157": "max-inline-size",
|
||||
"158": "max-width",
|
||||
"159": "min-height",
|
||||
"160": "min-inline-size",
|
||||
"161": "min-width",
|
||||
"162": "object-fit",
|
||||
"163": "object-position",
|
||||
"164": "opacity",
|
||||
"165": "order",
|
||||
"166": "outline-color",
|
||||
"167": "outline-offset",
|
||||
"168": "outline-style",
|
||||
"169": "outline-width",
|
||||
"170": "overflow-x",
|
||||
"171": "overflow-y",
|
||||
"172": "padding-block-end",
|
||||
"173": "padding-block-start",
|
||||
"174": "padding-bottom",
|
||||
"175": "padding-inline-end",
|
||||
"176": "padding-inline-start",
|
||||
"177": "padding-left",
|
||||
"178": "padding-right",
|
||||
"179": "padding-top",
|
||||
"180": "position",
|
||||
"181": "r",
|
||||
"182": "right",
|
||||
"183": "rotate",
|
||||
"184": "row-gap",
|
||||
"185": "rx",
|
||||
"186": "ry",
|
||||
"187": "scale",
|
||||
"188": "scrollbar-gutter",
|
||||
"189": "scrollbar-width",
|
||||
"190": "stop-color",
|
||||
"191": "stop-opacity",
|
||||
"192": "table-layout",
|
||||
"193": "text-decoration-color",
|
||||
"194": "text-decoration-style",
|
||||
"195": "text-decoration-thickness",
|
||||
"196": "text-overflow",
|
||||
"197": "top",
|
||||
"198": "transform",
|
||||
"199": "transform-box",
|
||||
"200": "transform-origin",
|
||||
"201": "transition-delay",
|
||||
"202": "transition-duration",
|
||||
"203": "transition-property",
|
||||
"204": "transition-timing-function",
|
||||
"205": "translate",
|
||||
"206": "unicode-bidi",
|
||||
"207": "user-select",
|
||||
"208": "vertical-align",
|
||||
"209": "width",
|
||||
"210": "x",
|
||||
"211": "y",
|
||||
"212": "z-index"
|
||||
}
|
||||
All properties associated with document.body.style by default:
|
||||
{}
|
||||
|
|
|
@ -5,6 +5,7 @@ border-spacing: 0px
|
|||
caption-side: top
|
||||
clip-rule: nonzero
|
||||
color: rgb(0, 0, 0)
|
||||
color-scheme: normal
|
||||
cursor: auto
|
||||
direction: ltr
|
||||
fill: rgb(0, 0, 0)
|
||||
|
@ -132,7 +133,7 @@ grid-row-start: auto
|
|||
grid-template-areas: none
|
||||
grid-template-columns: auto
|
||||
grid-template-rows: auto
|
||||
height: 2278px
|
||||
height: 2295px
|
||||
inline-size: auto
|
||||
inset-block-end: auto
|
||||
inset-block-start: auto
|
||||
|
|
Loading…
Reference in a new issue