Improve modern layout and viewport rendering
This commit is contained in:
1 parent
aff0172987
commit
10b8492116
22 files changed
+5978
-450
No files matched your search
@@ -0,0 +1,92 @@
|
||||
Copyright (c) 2016 The Inter Project Authors (https://github.com/rsms/inter)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION AND CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
@@ -0,0 +1,14 @@
|
||||
obscura-vf-test.woff2.b64
|
||||
|
||||
Source: Inter variable Roman Latin webfont served by https://vuejs.org/
|
||||
Upstream: https://github.com/rsms/inter
|
||||
Upstream version recorded in the font: 4.001;git-9221beed3
|
||||
License: SIL Open Font License 1.1 (see LICENSE-OFL-1.1.txt)
|
||||
|
||||
The test fixture was subset with fontTools 4.29.1 to U+0020, U+0041,
|
||||
U+004D, U+0061, and U+006D while retaining the opsz and wght variation axes.
|
||||
All family, full, PostScript, unique, and named-instance records were renamed
|
||||
from the OFL reserved name to "Obscura VF Test" / "ObscuraVFTest" variants.
|
||||
The original copyright attribution remains in the font's copyright record.
|
||||
|
||||
The WOFF2 is stored as base64 text so repository edits remain reviewable.
|
||||
@@ -0,0 +1,65 @@
|
||||
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
|
||||
@@ -930,6 +930,164 @@ fn non_stretched_auto_grid_item_shrink_wraps_inside_grid_area() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn percentage_max_width_clamps_grid_item_intrinsic_size() {
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
body { margin: 0 }
|
||||
#grid { display: grid; width: 1200px }
|
||||
#item { max-width: 100% }
|
||||
#gallery { display: flex; gap: 8px }
|
||||
.column { flex: none; height: 20px }
|
||||
</style>
|
||||
<div id="grid">
|
||||
<div id="item">
|
||||
<div id="gallery">
|
||||
<div class="column" style="width:303px"></div>
|
||||
<div class="column" style="width:200px"></div>
|
||||
<div class="column" style="width:500px"></div>
|
||||
<div class="column" style="width:200px"></div>
|
||||
<div class="column" style="width:293px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (1280.0, 200.0));
|
||||
let grid = layout.rects[&tree.get_element_by_id("grid").unwrap()];
|
||||
let item = layout.rects[&tree.get_element_by_id("item").unwrap()];
|
||||
let gallery = layout.rects[&tree.get_element_by_id("gallery").unwrap()];
|
||||
assert!(
|
||||
(grid.width - 1200.0).abs() < 0.01
|
||||
&& (item.width - 1200.0).abs() < 0.01
|
||||
&& (gallery.width - 1200.0).abs() < 0.01,
|
||||
"max-width:100% must clamp the grid item while its flex contents overflow: \
|
||||
grid={grid:?} item={item:?} gallery={gallery:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn named_area_shorthand_survives_end_longhand_override() {
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
body { margin: 0 }
|
||||
#grid {
|
||||
display: grid;
|
||||
width: 1200px;
|
||||
grid-template-columns:
|
||||
[extended-full-start] 16px
|
||||
[full-start] 200px
|
||||
[content-start] 768px
|
||||
[content-end] 200px
|
||||
[full-end] 16px
|
||||
[extended-full-end];
|
||||
}
|
||||
#grid > * { grid-column: content }
|
||||
#hero { grid-column-end: extended-full-end }
|
||||
</style>
|
||||
<div id="grid"><div id="hero"></div></div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (1280.0, 200.0));
|
||||
let hero = layout.rects[&tree.get_element_by_id("hero").unwrap()];
|
||||
assert!(
|
||||
(hero.x - 216.0).abs() < 0.01 && (hero.width - 984.0).abs() < 0.01,
|
||||
"the end longhand must retain the shorthand's named content start: {hero:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_width_flex_button_keeps_native_intrinsic_sizing() {
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
body { margin: 0 }
|
||||
#box { width: 600px }
|
||||
button {
|
||||
display: flex;
|
||||
margin: 0 auto;
|
||||
padding: 19px 24px;
|
||||
border: 2px solid;
|
||||
gap: 6px;
|
||||
font-size: 24px;
|
||||
}
|
||||
button::before { content: ""; width: 1em; height: 1em }
|
||||
</style>
|
||||
<div id="box"><button id="search">Search</button></div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (800.0, 200.0));
|
||||
let button = layout.rects[&tree.get_element_by_id("search").unwrap()];
|
||||
assert!(
|
||||
button.width > 120.0
|
||||
&& button.width < 220.0
|
||||
&& (button.x - (600.0 - button.width) / 2.0).abs() < 0.01,
|
||||
"the auto-width flex button should shrink-wrap and center: {button:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flex_auto_margin_absorbs_space_before_justify_content() {
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
body { margin: 0 }
|
||||
#bar {
|
||||
display: flex;
|
||||
width: 1280px;
|
||||
gap: 16px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
#crumb { width: 42px; height: 20px; margin: 0 auto 0 0 }
|
||||
#theme { width: 91px; height: 20px }
|
||||
#language { width: 131px; height: 20px }
|
||||
</style>
|
||||
<div id="bar">
|
||||
<div id="crumb"></div>
|
||||
<div id="theme"></div>
|
||||
<div id="language"></div>
|
||||
</div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (1280.0, 200.0));
|
||||
let rect = |id| layout.rects[&tree.get_element_by_id(id).unwrap()];
|
||||
let crumb = rect("crumb");
|
||||
let theme = rect("theme");
|
||||
let language = rect("language");
|
||||
assert!(
|
||||
(crumb.x - 0.0).abs() < 0.01
|
||||
&& (theme.x - 1042.0).abs() < 0.01
|
||||
&& (language.x - 1149.0).abs() < 0.01,
|
||||
"main-axis auto margin must consume free space before justify-content: \
|
||||
crumb={crumb:?} theme={theme:?} language={language:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_is_descendant_utility_blockifies_matching_lines() {
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
pre { display: flex }
|
||||
code code { position: relative }
|
||||
:is(.\*\*\:\[\.line\]\:block *).line { display: block }
|
||||
</style>
|
||||
<div class="**:[.line]:block">
|
||||
<pre><code><code><span id="one" class="line">one</span><span id="two" class="line">two</span></code></code></pre>
|
||||
</div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (500.0, 200.0));
|
||||
let one = tree.get_element_by_id("one").unwrap();
|
||||
let two = tree.get_element_by_id("two").unwrap();
|
||||
assert_eq!(layout.styles[&one].display, obscura_render::Display::Block);
|
||||
assert_eq!(layout.styles[&two].display, obscura_render::Display::Block);
|
||||
assert!(layout.rects[&two].y > layout.rects[&one].y);
|
||||
assert_eq!(layout.rects[&one].width, layout.rects[&two].width);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opposing_floats_share_header_band_through_inline_wrapper() {
|
||||
let tree = parse_html(include_str!("../../../render-repros/opposing-header-floats.html"));
|
||||
@@ -1168,3 +1326,304 @@ fn contextual_right_inset_uses_root_font_tokens() {
|
||||
"right inset should place 100px box at 900 - 56 - 100: {rect:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_legacy_supports_probe_does_not_override_modern_line_height() {
|
||||
// Tailwind v4 uses this old-browser probe around a leading-variable reset.
|
||||
// Chromium rejects the condition. Flattening every @supports body made
|
||||
// the reset win, invalidated the var(), and inflated three 96px lines from
|
||||
// 288px to an inherited 432px.
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
@supports (((-webkit-hyphens:none)) and
|
||||
(not (margin-trim:inline))) or
|
||||
((-moz-orient:inline) and
|
||||
(not (color:rgb(from red r g b)))) {
|
||||
* { --leading:initial }
|
||||
}
|
||||
:root { --hero-line-height:1 }
|
||||
body { margin:0; line-height:1.5 }
|
||||
#hero {
|
||||
margin:0;
|
||||
width:600px;
|
||||
font-size:96px;
|
||||
line-height:var(--leading,var(--hero-line-height))
|
||||
}
|
||||
</style>
|
||||
<h1 id="hero">one<br>two<br>three</h1>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (1280.0, 720.0));
|
||||
let hero = layout.rects[&tree.get_element_by_id("hero").unwrap()];
|
||||
assert!(
|
||||
(hero.height - 288.0).abs() < 0.01,
|
||||
"three 96px modern lines should be 288px tall: {hero:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_property_css_wide_keywords_preserve_var_fallback_semantics() {
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
:root {
|
||||
--tone:#112233;
|
||||
--light:initial;
|
||||
--toggle:var(--light) #18191b;
|
||||
--page:var(--toggle,#ffffff)
|
||||
}
|
||||
#invalid { --tone:initial; color:var(--tone,#445566) }
|
||||
#inherited { --tone:unset; color:var(--tone,#445566) }
|
||||
#nested { background-color:var(--page) }
|
||||
</style>
|
||||
<div id="invalid">fallback</div>
|
||||
<div id="inherited">inherit</div>
|
||||
<div id="nested">nested fallback</div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (800.0, 600.0));
|
||||
let invalid = tree.get_element_by_id("invalid").unwrap();
|
||||
let inherited = tree.get_element_by_id("inherited").unwrap();
|
||||
let nested = tree.get_element_by_id("nested").unwrap();
|
||||
assert_eq!(
|
||||
layout.styles[&invalid].color,
|
||||
Some([0x44, 0x55, 0x66, 0xff]),
|
||||
"`initial` is guaranteed-invalid and var() must use its fallback"
|
||||
);
|
||||
assert_eq!(
|
||||
layout.styles[&inherited].color,
|
||||
Some([0x11, 0x22, 0x33, 0xff]),
|
||||
"`unset` inherits a custom property's parent value"
|
||||
);
|
||||
assert_eq!(
|
||||
layout.styles[&nested].background_color,
|
||||
Some([0xff, 0xff, 0xff, 0xff]),
|
||||
"an invalid nested custom property must activate the outer fallback"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stable_root_scrollbar_gutter_reduces_the_initial_containing_block() {
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
html { overflow-y:auto; scrollbar-gutter:stable }
|
||||
body { margin:0 }
|
||||
#fill { width:100%; height:40px }
|
||||
#center { width:401px; height:40px; margin-inline:auto }
|
||||
</style>
|
||||
<div id="fill"></div>
|
||||
<div id="center"></div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (900.0, 600.0));
|
||||
let fill = layout.rects[&tree.get_element_by_id("fill").unwrap()];
|
||||
let center = layout.rects[&tree.get_element_by_id("center").unwrap()];
|
||||
assert!(
|
||||
(fill.width - 885.0).abs() < 0.01,
|
||||
"stable classic gutter should reserve 15px: {fill:?}"
|
||||
);
|
||||
assert!(
|
||||
(center.x - 242.0).abs() < 0.01,
|
||||
"401px box should center in the 885px ICB: {center:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_grid_track_freezes_at_growth_limit_during_distribution() {
|
||||
// The first auto track reaches its max-content growth limit before the
|
||||
// second. The remaining free space belongs only to the second track.
|
||||
// Giving the frozen track that remainder again makes the grid overflow
|
||||
// even though the two automatic minimums fit the definite container.
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
* { box-sizing:border-box }
|
||||
html, body { margin:0 }
|
||||
#grid {
|
||||
display:grid;
|
||||
width:1016px;
|
||||
grid-template-columns:auto auto;
|
||||
grid-template-rows:320px;
|
||||
column-gap:16px;
|
||||
}
|
||||
#copy {
|
||||
grid-column:1;
|
||||
font:400 48px/57.6px sans-serif;
|
||||
}
|
||||
#art {
|
||||
grid-column:2;
|
||||
overflow:hidden;
|
||||
}
|
||||
#art > div { width:560px; height:560px }
|
||||
</style>
|
||||
<div id="grid">
|
||||
<h1 id="copy">Resources for Developers,<br> by Developers</h1>
|
||||
<div id="art"><div></div></div>
|
||||
</div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (1280.0, 720.0));
|
||||
let rect = |id| layout.rects[&tree.get_element_by_id(id).unwrap()];
|
||||
let grid = rect("grid");
|
||||
let copy = rect("copy");
|
||||
let art = rect("art");
|
||||
assert!(
|
||||
((copy.x + copy.width + 16.0) - art.x).abs() < 0.01,
|
||||
"the authored column gap must separate the final tracks: {copy:?} {art:?}"
|
||||
);
|
||||
assert!(
|
||||
((art.x + art.width) - (grid.x + grid.width)).abs() < 0.01,
|
||||
"free-space redistribution must not grow a track after it freezes: {grid:?} {copy:?} {art:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_block_before_participates_in_normal_flow_geometry() {
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
html, body { margin:0 }
|
||||
body { font-size:20px; line-height:20px }
|
||||
#host { width:200px; background:#eee }
|
||||
#host::before {
|
||||
content:"";
|
||||
display:block;
|
||||
width:80px;
|
||||
height:40px;
|
||||
margin-bottom:10px;
|
||||
background:#06c;
|
||||
}
|
||||
#next { width:20px; height:10px; background:#0a0 }
|
||||
</style>
|
||||
<div id="host">TEXT</div><div id="next"></div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (800.0, 600.0));
|
||||
let host = layout.rects[&tree.get_element_by_id("host").unwrap()];
|
||||
let next = layout.rects[&tree.get_element_by_id("next").unwrap()];
|
||||
assert!(
|
||||
(host.height - 70.0).abs() < 0.01,
|
||||
"40px generated block + 10px margin + 20px line must size host: {host:?}"
|
||||
);
|
||||
assert!(
|
||||
(next.y - 70.0).abs() < 0.01,
|
||||
"following flow content must start after generated geometry: {next:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_inline_block_pseudo_is_an_atomic_flex_item() {
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
html, body { margin:0 }
|
||||
#host { display:flex; width:100px; height:20px }
|
||||
#host::before {
|
||||
content:"";
|
||||
display:inline-block;
|
||||
width:.5em;
|
||||
height:.5em;
|
||||
margin-right:5px;
|
||||
border:2px solid #06c;
|
||||
}
|
||||
#label { width:20px; height:10px }
|
||||
</style>
|
||||
<div id="host"><span id="label"></span></div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (800.0, 600.0));
|
||||
let label = layout.rects[&tree.get_element_by_id("label").unwrap()];
|
||||
assert!(
|
||||
(label.x - 17.0).abs() < 0.01,
|
||||
"8px pseudo content width + 2px borders + 5px margin must precede label: {label:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn light_dark_uses_the_final_inherited_color_scheme_across_the_cascade() {
|
||||
let tree = parse_html(
|
||||
r#"
|
||||
<style>
|
||||
:root {
|
||||
--surface:light-dark(#f5f5f5,#18191b);
|
||||
}
|
||||
#dark {
|
||||
background-color:var(--surface);
|
||||
color:light-dark(#111111,#eeeeee);
|
||||
border:1px solid light-dark(#dddddd,#333333);
|
||||
}
|
||||
#dark {
|
||||
color-scheme:dark;
|
||||
}
|
||||
#dark::before {
|
||||
content:"";
|
||||
display:block;
|
||||
width:10px;
|
||||
height:10px;
|
||||
background:light-dark(#ff0000,#0000ff);
|
||||
}
|
||||
#child {
|
||||
background-image:linear-gradient(
|
||||
light-dark(#ffffff,#101010),
|
||||
light-dark(#eeeeee,#202020)
|
||||
);
|
||||
}
|
||||
#icon rect {
|
||||
fill:light-dark(#c3c7cb,#51565d);
|
||||
stroke:light-dark(#ffffff,#000000);
|
||||
}
|
||||
#mixed {
|
||||
background:light-dark(#abcdef,#123456);
|
||||
color-scheme:light dark;
|
||||
}
|
||||
</style>
|
||||
<section id="dark">
|
||||
<div id="child"></div>
|
||||
<svg id="icon"><rect width="10" height="10"/></svg>
|
||||
</section>
|
||||
<div id="mixed"></div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (800.0, 600.0));
|
||||
let dark = tree.get_element_by_id("dark").unwrap();
|
||||
let child = tree.get_element_by_id("child").unwrap();
|
||||
let mixed = tree.get_element_by_id("mixed").unwrap();
|
||||
let rect = tree.query_selector("#icon rect").unwrap().unwrap();
|
||||
|
||||
let dark_style = &layout.styles[&dark];
|
||||
assert!(dark_style.color_scheme_dark);
|
||||
assert_eq!(dark_style.background_color, Some([0x18, 0x19, 0x1b, 0xff]));
|
||||
assert_eq!(dark_style.color, Some([0xee, 0xee, 0xee, 0xff]));
|
||||
assert_eq!(dark_style.border_color, Some([0x33, 0x33, 0x33, 0xff]));
|
||||
assert_eq!(
|
||||
dark_style
|
||||
.before_pseudo
|
||||
.as_deref()
|
||||
.and_then(|pseudo| pseudo.background_color),
|
||||
Some([0x00, 0x00, 0xff, 0xff]),
|
||||
"pseudo must use its host's inherited dark scheme"
|
||||
);
|
||||
|
||||
let child_style = &layout.styles[&child];
|
||||
assert!(child_style.color_scheme_dark);
|
||||
let (_, stops) = child_style
|
||||
.background_gradient
|
||||
.as_ref()
|
||||
.expect("dark gradient");
|
||||
assert_eq!(stops[0].0, [0x10, 0x10, 0x10, 0xff]);
|
||||
assert_eq!(stops[1].0, [0x20, 0x20, 0x20, 0xff]);
|
||||
|
||||
let rect_style = &layout.styles[&rect];
|
||||
assert!(rect_style.color_scheme_dark);
|
||||
assert_eq!(rect_style.svg_fill.as_deref(), Some("#51565dff"));
|
||||
assert_eq!(rect_style.svg_stroke.as_deref(), Some("#000000ff"));
|
||||
|
||||
let mixed_style = &layout.styles[&mixed];
|
||||
assert!(
|
||||
!mixed_style.color_scheme_dark,
|
||||
"a scheme list admitting light uses the current light preference"
|
||||
);
|
||||
assert_eq!(mixed_style.background_color, Some([0xab, 0xcd, 0xef, 0xff]));
|
||||
}
|
||||
Reference in new issue
Block a user