LibWeb/CSS: Implement the color-scheme CSS property

This commit is contained in:
Gingeh 2025-01-02 12:59:09 +11:00 committed by Sam Atkins
parent 89296b88a0
commit ce5cd012b9
Notes: github-actions[bot] 2025-01-08 11:19:41 +00:00
36 changed files with 618 additions and 370 deletions

View file

@ -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;
}

View file

@ -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);
}
color-scheme: light dark;
}
</style>
</head>

View file

@ -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;
}
color-scheme: light dark;
}
header {

View file

@ -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);
}
color-scheme: light dark;
}
h1 {

View file

@ -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);
}
color-scheme: light dark;
}
img {
float: left;

View file

@ -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

View file

@ -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());

View file

@ -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()); }

View file

@ -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 users 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,
// thats the elements 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 users 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)

View file

@ -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;

View file

@ -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; }

View file

@ -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;

View file

@ -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();

View file

@ -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,

View file

@ -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,

View file

@ -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()) {

View 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());
}
}

View 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;
};
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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 viewports 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 documents root element do not match,
// then the UA must use an opaque canvas of the Canvas color appropriate to the
// embedded documents 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();

View file

@ -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();

View file

@ -116,6 +116,7 @@ class BasicShapeStyleValue;
class BorderRadiusStyleValue;
class CalculatedStyleValue;
class Clip;
class ColorSchemeStyleValue;
class ConicGradientStyleValue;
class ContentStyleValue;
class CounterDefinitionsStyleValue;

View file

@ -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())

View file

@ -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())

View file

@ -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.

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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);

View file

@ -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;
}();

View file

@ -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;

View file

@ -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;

View file

@ -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'

View file

@ -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:
{}

View file

@ -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