Skip to content

Commit

Permalink
LibWeb/CSS: Implement the light-dark color function
Browse files Browse the repository at this point in the history
  • Loading branch information
Gingeh committed Jan 6, 2025
1 parent b101f80 commit 0a10a7e
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 0 deletions.
1 change: 1 addition & 0 deletions Libraries/LibWeb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ set(SOURCES
CSS/StyleValues/CSSKeywordValue.cpp
CSS/StyleValues/CSSLabLike.cpp
CSS/StyleValues/CSSLCHLike.cpp
CSS/StyleValues/CSSLightDark.cpp
CSS/StyleValues/CSSRGB.cpp
CSS/StyleValues/DisplayStyleValue.cpp
CSS/StyleValues/EasingStyleValue.cpp
Expand Down
37 changes: 37 additions & 0 deletions Libraries/LibWeb/CSS/Parser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
#include <LibWeb/CSS/StyleValues/CSSLCHLike.h>
#include <LibWeb/CSS/StyleValues/CSSLabLike.h>
#include <LibWeb/CSS/StyleValues/CSSLightDark.h>
#include <LibWeb/CSS/StyleValues/CSSRGB.h>
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
Expand Down Expand Up @@ -3491,6 +3492,40 @@ RefPtr<CSSStyleValue> Parser::parse_color_function(TokenStream<ComponentValue>&
alpha.release_nonnull());
}

// https://drafts.csswg.org/css-color-5/#funcdef-light-dark
RefPtr<CSSStyleValue> Parser::parse_light_dark_color_value(TokenStream<ComponentValue>& outer_tokens)
{
auto transaction = outer_tokens.begin_transaction();

outer_tokens.discard_whitespace();
auto const& function_token = outer_tokens.consume_a_token();
if (!function_token.is_function("light-dark"sv))
return {};

auto inner_tokens = TokenStream { function_token.function().value };

inner_tokens.discard_whitespace();
auto light = parse_color_value(inner_tokens);
if (!light)
return {};

inner_tokens.discard_whitespace();
if (!inner_tokens.consume_a_token().is(Token::Type::Comma))
return {};

inner_tokens.discard_whitespace();
auto dark = parse_color_value(inner_tokens);
if (!dark)
return {};

inner_tokens.discard_whitespace();
if (inner_tokens.has_next_token())
return {};

transaction.commit();
return CSSLightDark::create(light.release_nonnull(), dark.release_nonnull());
}

// https://www.w3.org/TR/css-color-4/#color-syntax
RefPtr<CSSStyleValue> Parser::parse_color_value(TokenStream<ComponentValue>& tokens)
{
Expand Down Expand Up @@ -3522,6 +3557,8 @@ RefPtr<CSSStyleValue> Parser::parse_color_value(TokenStream<ComponentValue>& tok
return oklab;
if (auto oklch = parse_oklch_color_value(tokens))
return oklch;
if (auto light_dark = parse_light_dark_color_value(tokens))
return light_dark;

auto transaction = tokens.begin_transaction();
tokens.discard_whitespace();
Expand Down
1 change: 1 addition & 0 deletions Libraries/LibWeb/CSS/Parser/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ class Parser {
RefPtr<CSSStyleValue> parse_lch_color_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_oklch_color_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_color_function(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_light_dark_color_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_color_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_color_scheme_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_counter_value(TokenStream<ComponentValue>&);
Expand Down
1 change: 1 addition & 0 deletions Libraries/LibWeb/CSS/StyleValues/CSSColorValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class CSSColorValue : public CSSStyleValue {
Rec2020,
XYZD50,
XYZD65,
LightDark, // This is used by CSSLightDark for light-dark(..., ...).
};
ColorType color_type() const { return m_color_type; }

Expand Down
37 changes: 37 additions & 0 deletions Libraries/LibWeb/CSS/StyleValues/CSSLightDark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2025, Ladybird contributors
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include "CSSLightDark.h"
#include <LibWeb/Layout/Node.h>

namespace Web::CSS {

Color CSSLightDark::to_color(Optional<Layout::NodeWithStyle const&> node) const
{
if (node.has_value() && node.value().computed_values().color_scheme() == PreferredColorScheme::Dark)
return m_properties.dark->to_color(node);

return m_properties.light->to_color(node);
}

bool CSSLightDark::equals(CSSStyleValue const& other) const
{
if (type() != other.type())
return false;
auto const& other_color = other.as_color();
if (color_type() != other_color.color_type())
return false;
auto const& other_light_dark = verify_cast<CSSLightDark>(other_color);
return m_properties == other_light_dark.m_properties;
}

String CSSLightDark::to_string(SerializationMode mode) const
{
// FIXME: We don't have enough information to determine the computed value here.
return MUST(String::formatted("light-dark({}, {})", m_properties.light->to_string(mode), m_properties.dark->to_string(mode)));
}

}
43 changes: 43 additions & 0 deletions Libraries/LibWeb/CSS/StyleValues/CSSLightDark.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2025, Ladybird contributors
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <LibWeb/CSS/StyleValues/CSSColorValue.h>

namespace Web::CSS {

// https://drafts.csswg.org/css-color-5/#funcdef-light-dark
class CSSLightDark final : public CSSColorValue {
public:
virtual ~CSSLightDark() override = default;

static ValueComparingNonnullRefPtr<CSSLightDark> create(ValueComparingNonnullRefPtr<CSSStyleValue> light, ValueComparingNonnullRefPtr<CSSStyleValue> dark)
{
return AK::adopt_ref(*new (nothrow) CSSLightDark(move(light), move(dark)));
}

virtual bool equals(CSSStyleValue const&) const override;
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override;
virtual String to_string(SerializationMode) const override;

private:
CSSLightDark(ValueComparingNonnullRefPtr<CSSStyleValue> light, ValueComparingNonnullRefPtr<CSSStyleValue> dark)
: CSSColorValue(CSSColorValue::ColorType::LightDark)
, m_properties { .light = move(light), .dark = move(dark) }
{
}

struct Properties {
ValueComparingNonnullRefPtr<CSSStyleValue> light;
ValueComparingNonnullRefPtr<CSSStyleValue> dark;
bool operator==(Properties const&) const = default;
};

Properties m_properties;
};

} // Web::CSS
10 changes: 10 additions & 0 deletions Tests/LibWeb/Screenshot/expected/color-scheme-ref.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<style>
* {
margin: 0;
}

body {
background-color: white;
}
</style>
<img src="../images/color-scheme-ref.png">
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions Tests/LibWeb/Screenshot/input/color-scheme.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<link rel="match" href="../expected/color-scheme-ref.html" />
<style>
body>div>div {
padding: 1em;
background-color: Canvas;
}
</style>
<div style="display: flex">
<div style="color-scheme: light">
<div style="background: AccentColor; width: 10em; color: transparent">a</div>
<div style="background: AccentColorText; width: 10em; color: transparent">a</div>
<div style="background: ActiveText; width: 10em; color: transparent">a</div>
<div style="background: ButtonBorder; width: 10em; color: transparent">a</div>
<div style="background: ButtonFace; width: 10em; color: transparent">a</div>
<div style="background: ButtonText; width: 10em; color: transparent">a</div>
<div style="background: Canvas; width: 10em; color: transparent">a</div>
<div style="background: CanvasText; width: 10em; color: transparent">a</div>
<div style="background: Field; width: 10em; color: transparent">a</div>
<div style="background: FieldText; width: 10em; color: transparent">a</div>
<div style="background: GrayText; width: 10em; color: transparent">a</div>
<div style="background: Highlight; width: 10em; color: transparent">a</div>
<div style="background: HighlightText; width: 10em; color: transparent">a</div>
<div style="background: LinkText; width: 10em; color: transparent">a</div>
<div style="background: Mark; width: 10em; color: transparent">a</div>
<div style="background: MarkText; width: 10em; color: transparent">a</div>
<div style="background: SelectedItem; width: 10em; color: transparent">a</div>
<div style="background: SelectedItemText; width: 10em; color: transparent">a</div>
<div style="background: VisitedText; width: 10em; color: transparent">a</div>
<div><button style="width: 10rem">Button</button></div>
<div><button style="width: 10rem" disabled>Disabled</button></div>
<div><input style="width: 10rem" value="Input"></input></div>
<div><input type="checkbox"></input><input type="checkbox" checked></input></div>
<div><input type="checkbox" switch></input><input type="checkbox" switch checked></input></div>
<div><input type="radio" name="lightradio"></input><input type="radio" name="lightradio" checked></input></div>
<div><progress style="width: 10rem" value="70" max="100"></progress></div>
<div><input type="range" style="width: 10rem" value="70" max="100"></input></div>
<div style="color: light-dark(red,green)">light-dark(red,green)</div>
</div>
<div style="color-scheme: dark">
<div style="background: AccentColor; width: 10em; color: transparent">a</div>
<div style="background: AccentColorText; width: 10em; color: transparent">a</div>
<div style="background: ActiveText; width: 10em; color: transparent">a</div>
<div style="background: ButtonBorder; width: 10em; color: transparent">a</div>
<div style="background: ButtonFace; width: 10em; color: transparent">a</div>
<div style="background: ButtonText; width: 10em; color: transparent">a</div>
<div style="background: Canvas; width: 10em; color: transparent">a</div>
<div style="background: CanvasText; width: 10em; color: transparent">a</div>
<div style="background: Field; width: 10em; color: transparent">a</div>
<div style="background: FieldText; width: 10em; color: transparent">a</div>
<div style="background: GrayText; width: 10em; color: transparent">a</div>
<div style="background: Highlight; width: 10em; color: transparent">a</div>
<div style="background: HighlightText; width: 10em; color: transparent">a</div>
<div style="background: LinkText; width: 10em; color: transparent">a</div>
<div style="background: Mark; width: 10em; color: transparent">a</div>
<div style="background: MarkText; width: 10em; color: transparent">a</div>
<div style="background: SelectedItem; width: 10em; color: transparent">a</div>
<div style="background: SelectedItemText; width: 10em; color: transparent">a</div>
<div style="background: VisitedText; width: 10em; color: transparent">a</div>
<div><button style="width: 10rem">Button</button></div>
<div><button style="width: 10rem" disabled>Disabled</button></div>
<div><input style="width: 10rem" value="Input"></input></div>
<div><input type="checkbox"></input><input type="checkbox" checked></input></div>
<div><input type="checkbox" switch></input><input type="checkbox" switch checked></input></div>
<div><input type="radio" name="darkradio"></input><input type="radio" name="darkradio" checked></input></div>
<div><progress style="width: 10rem" value="70" max="100"></progress></div>
<div><input type="range" style="width: 10rem" value="70" max="100"></input></div>
<div style="color: light-dark(red,green)">light-dark(red,green)</div>
</div>
</div>

0 comments on commit 0a10a7e

Please sign in to comment.