mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-14 07:06:45 +03:00
Sampled contrast: no panic on multibyte urls, any layer order, auto size
Second review round for the sampled-contrast path (#560), plus the Windows CI fix: - `is_data_uri` byte-sliced the url, so a local image whose name opens with a multibyte character panicked the static scanner. It reads the prefix with `get` now. - The ground walk only accepted a layer that begins with `url(`, while a `background` shorthand may put color, position, size, or repeat tokens before the image; such layers fell out of the sampled path. The call is found anywhere in the layer and decoded from there. A fixture case pins it, so the fixture and directory goldens gain one finding. - The decoration gate scaled the missing size axis only for a single `40px` token; `40px auto` and `auto 40px` now follow the image's aspect ratio the same way. - The linked-sheet url rewrite did its path math through the OS-flavored helpers, which rendered `css/light.png` as `light.png` on Windows. It uses the POSIX helpers on both directories, since a CSS url is POSIX on every OS. AI assistance: Claude Code (Claude Fable 5.1), on maintainer instruction. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
66945a79da
commit
0f115e5d37
@@ -79,15 +79,16 @@ pub fn is_decorative_layer(repeat: &str, size: &str, intrinsic_w: f64, intrinsic
|
||||
}
|
||||
let tokens: Vec<&str> = size.split_whitespace().collect();
|
||||
let px = |t: Option<&&str>| t.filter(|t| t.ends_with("px")).map(|t| parse_float(t));
|
||||
let w = px(tokens.first()).unwrap_or(intrinsic_w);
|
||||
let h = px(tokens.get(1)).unwrap_or_else(|| {
|
||||
// `background-size: 40px` scales the height with the width.
|
||||
if tokens.len() == 1 && px(tokens.first()).is_some() && intrinsic_w > 0.0 {
|
||||
intrinsic_h * (w / intrinsic_w)
|
||||
} else {
|
||||
intrinsic_h
|
||||
}
|
||||
});
|
||||
// An `auto` (or absent) axis follows the other at the image's aspect
|
||||
// ratio, as in CSS: `40px`, `40px auto`, and `auto 40px` all scale.
|
||||
let (w, h) = match (px(tokens.first()), px(tokens.get(1))) {
|
||||
(Some(w), Some(h)) => (w, h),
|
||||
(Some(w), None) if intrinsic_w > 0.0 => (w, intrinsic_h * (w / intrinsic_w)),
|
||||
(None, Some(h)) if intrinsic_h > 0.0 => (intrinsic_w * (h / intrinsic_h), h),
|
||||
(Some(w), None) => (w, intrinsic_h),
|
||||
(None, Some(h)) => (intrinsic_w, h),
|
||||
(None, None) => (intrinsic_w, intrinsic_h),
|
||||
};
|
||||
(!repeat_x && w < DECORATION_MAX_PX) || (!repeat_y && h < DECORATION_MAX_PX)
|
||||
}
|
||||
|
||||
@@ -213,6 +214,14 @@ mod tests {
|
||||
fn decoration_gate() {
|
||||
assert!(is_decorative_layer("no-repeat", "auto", 24.0, 24.0));
|
||||
assert!(is_decorative_layer("no-repeat", "40px", 1200.0, 800.0));
|
||||
assert!(is_decorative_layer("no-repeat", "40px auto", 1200.0, 800.0));
|
||||
assert!(is_decorative_layer("no-repeat", "auto 40px", 1200.0, 800.0));
|
||||
assert!(!is_decorative_layer(
|
||||
"no-repeat",
|
||||
"auto 800px",
|
||||
1200.0,
|
||||
800.0
|
||||
));
|
||||
assert!(is_decorative_layer("repeat-y", "auto", 8.0, 400.0));
|
||||
assert!(is_decorative_layer("repeat-x", "auto", 1200.0, 80.0));
|
||||
assert!(!is_decorative_layer("no-repeat", "cover", 24.0, 24.0));
|
||||
|
||||
@@ -363,12 +363,14 @@ fn layer_entry(value: &str, index: usize) -> String {
|
||||
}
|
||||
|
||||
/// The `url()` argument of one background layer: `url(x\ y.png)` from the
|
||||
/// css-tree generator, `url("x y.png")` from a style attribute. The layer
|
||||
/// may carry the rest of a shorthand after the call (`url(x)center/cover`),
|
||||
/// css-tree generator, `url("x y.png")` from a style attribute. A shorthand
|
||||
/// layer may carry other tokens around the call (`#000 url(x)center/cover`),
|
||||
/// so only the call itself is decoded.
|
||||
fn layer_url(layer: &str) -> String {
|
||||
let layer = js::trim(layer);
|
||||
let open = layer.find('(').map(|i| i + 1).unwrap_or(layer.len());
|
||||
let Some(call) = URL_CALL_RE.find(layer) else {
|
||||
return String::new();
|
||||
};
|
||||
let open = call.end();
|
||||
let mut depth = 1usize;
|
||||
let mut quote: Option<char> = None;
|
||||
let mut close = layer.len();
|
||||
@@ -388,7 +390,7 @@ fn layer_url(layer: &str) -> String {
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
let decoded = decode_url(&layer[..close]);
|
||||
let decoded = decode_url(&format!("url({}", &layer[open..close]));
|
||||
let trimmed = js::trim(&decoded);
|
||||
let unquoted = trimmed
|
||||
.strip_prefix('"')
|
||||
@@ -433,7 +435,10 @@ pub fn find_image_ground(el: &StaticElement<'_>) -> Option<ImageGround> {
|
||||
};
|
||||
for (index, layer) in layers.iter().enumerate() {
|
||||
let layer = js::trim(layer);
|
||||
if URL_START_RE.is_match(layer) {
|
||||
// A shorthand layer may put color, position, size, or repeat
|
||||
// tokens before the image call; the call is what matters.
|
||||
let is_gradient = GRADIENT_CALL_RE.is_match(layer);
|
||||
if !is_gradient && URL_CALL_RE.is_match(layer) {
|
||||
// A layer beneath the image is unknown paint; otherwise the
|
||||
// element's own color over its parent's ground, white at the
|
||||
// root like the analytic walk.
|
||||
@@ -459,7 +464,7 @@ pub fn find_image_ground(el: &StaticElement<'_>) -> Option<ImageGround> {
|
||||
under,
|
||||
});
|
||||
}
|
||||
if GRADIENT_CALL_RE.is_match(layer) {
|
||||
if is_gradient {
|
||||
let stops = parse_gradient_colors(Some(layer));
|
||||
if stops.is_empty() || stops.iter().all(|s| s.alpha_or_one() >= 0.99) {
|
||||
return None;
|
||||
|
||||
@@ -176,8 +176,13 @@ pub fn rewrite_sheet_urls(css: &str, sheet_dir: &str, page_dir: &str) -> String
|
||||
}
|
||||
let cut = target.find(['?', '#']).unwrap_or(target.len());
|
||||
let (path, suffix) = target.split_at(cut);
|
||||
let absolute = jsp::resolve("/", &[sheet_dir, path]);
|
||||
let relative = jsp::to_posix(&jsp::relative("/", page_dir, &absolute));
|
||||
// Pure POSIX string math on both directories: a CSS url is
|
||||
// POSIX on every OS, and the win32 helpers would render a drive
|
||||
// path (`D:\a\...`) differently from the url beside it.
|
||||
let sheet = jsp::to_posix(sheet_dir);
|
||||
let page = jsp::to_posix(page_dir);
|
||||
let absolute = jsp::posix::resolve("/", &[&sheet, path]);
|
||||
let relative = jsp::posix::relative("/", &page, &absolute);
|
||||
if relative.is_empty() {
|
||||
return whole.to_string();
|
||||
}
|
||||
|
||||
@@ -121,7 +121,9 @@ impl ImageSampler {
|
||||
}
|
||||
|
||||
fn is_data_uri(url: &str) -> bool {
|
||||
url.len() > 5 && url[..5].eq_ignore_ascii_case("data:")
|
||||
// `get`, not a byte slice: a url may open with a multibyte character.
|
||||
url.get(..5)
|
||||
.is_some_and(|prefix| prefix.eq_ignore_ascii_case("data:"))
|
||||
}
|
||||
|
||||
/// The cache key of a data URI: its length and a hash, so the cache never
|
||||
@@ -242,6 +244,11 @@ mod tests {
|
||||
assert!(sampler.load("https://example.com/hero.jpg").is_none());
|
||||
assert!(sampler.load("//cdn.example.com/hero.jpg").is_none());
|
||||
assert_eq!(ground_label("img/hero.jpg?v=3"), "hero.jpg");
|
||||
// A url that opens with a multibyte character is a local path, not
|
||||
// a panic.
|
||||
assert!(!is_data_uri("dat\u{20ac}"));
|
||||
assert!(sampler.load("abcd\u{20ac}.png").is_none());
|
||||
assert_eq!(ground_label("abcd\u{20ac}.png"), "abcd\u{20ac}.png");
|
||||
// A payload past the byte budget is refused before it is decoded.
|
||||
let huge = format!(
|
||||
"data:image/png;base64,{}",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
.panel h2 { margin: 0; font-size: 28px; line-height: 1.3; }
|
||||
.light { background: url(sampled-images/light.png); }
|
||||
.light-webp { background: url("sampled-images/light.webp") center / cover; }
|
||||
.tokens-first { background: center / cover no-repeat url(sampled-images/light.png); }
|
||||
.dark-jpg { background: url(sampled-images/dark.jpg) center / cover no-repeat; }
|
||||
.split { background: url(sampled-images/split.png) repeat; }
|
||||
.inline-data { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAaUlEQVR42k2PSQrAMAwD/f9ndt+TNl2OLmMQ5CCELUtR7CuLP+cUuPPo19F73tqY09q4nXsXC0SYA8T3mgNxgMiypMGZccLA5Kij6+eMDhKIZEZQsmkBA47UKxLkFKsHjCE6IOBQSv2bH8D48hUetch/AAAAAElFTkSuQmCC); }
|
||||
@@ -73,6 +74,9 @@
|
||||
<div class="panel light">
|
||||
<h2 class="white">Large white heading on the texture</h2>
|
||||
</div>
|
||||
<div class="panel tokens-first">
|
||||
<p class="white">Shorthand tokens before the image still name the ground.</p>
|
||||
</div>
|
||||
|
||||
<!-- ── Should pass ──────────────────────────────────────────────── -->
|
||||
<div class="panel light">
|
||||
|
||||
@@ -172,7 +172,7 @@ text instead of skipping the contrast check (docs/CLI-CONTRACT.md, "Sampled
|
||||
contrast"). The JS never had this path, so the new fixture
|
||||
`sampled-image-contrast.html` (with `sampled-images/`) has no JS golden; its
|
||||
cases were recorded from the engine, and the directory sweeps below moved
|
||||
only by that fixture's eight `low-contrast` findings, verified entry by entry.
|
||||
only by that fixture's nine `low-contrast` findings, verified entry by entry.
|
||||
|
||||
- `detect-fixture-json-sampled-image-contrast-html`, `detect-fixture-text-sampled-image-contrast-html`, `detect-fixture-json-sampled-images`, `detect-fixture-text-sampled-images`: new cases.
|
||||
- `detect-dir-json-all-fixtures`, `detect-dir-text-all-fixtures`, `detect-dir-quiet-all-fixtures`, `detect-no-advisory-json`, `detect-no-advisory-text`: the directory sweep picks up the new fixture.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"stdout": "",
|
||||
"stderr": "423 anti-patterns found.\n17 advisory notes (not counted).\n",
|
||||
"stderr": "424 anti-patterns found.\n17 advisory notes (not counted).\n",
|
||||
"exit": 2,
|
||||
"signal": null,
|
||||
"files": {}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"stdout": "[\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.3:1 (need 4.5:1) — text #3a3632 on dark.jpg; p90 of 36 samples, median 1.2:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.webp; p90 of 36 samples, median 1.2:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.1:1 (need 4.5:1) — text #fdfdfd on data:image/png; p90 of 36 samples, median 1.1:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 2.5:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 2.4:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.8:1 (need 4.5:1) — text #262421 on cutout.png; p90 of 36 samples, median 1.8:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.2:1 (need 3:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\"\n }\n]\n",
|
||||
"stdout": "[\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.3:1 (need 4.5:1) — text #3a3632 on dark.jpg; p90 of 36 samples, median 1.2:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.webp; p90 of 36 samples, median 1.2:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.1:1 (need 4.5:1) — text #fdfdfd on data:image/png; p90 of 36 samples, median 1.1:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 2.5:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 2.4:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.8:1 (need 4.5:1) — text #262421 on cutout.png; p90 of 36 samples, median 1.8:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.2:1 (need 3:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\",\n \"line\": 0,\n \"snippet\": \"sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\"\n }\n]\n",
|
||||
"stderr": "",
|
||||
"exit": 2,
|
||||
"signal": null,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"stdout": "",
|
||||
"stderr": "\n<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\n [low-contrast] sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.3:1 (need 4.5:1) — text #3a3632 on dark.jpg; p90 of 36 samples, median 1.2:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.webp; p90 of 36 samples, median 1.2:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.1:1 (need 4.5:1) — text #fdfdfd on data:image/png; p90 of 36 samples, median 1.1:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 2.5:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 2.4:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.8:1 (need 4.5:1) — text #262421 on cutout.png; p90 of 36 samples, median 1.8:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.2:1 (need 3:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n\n8 anti-patterns found.\n",
|
||||
"stderr": "\n<REPO>/tests/fixtures/antipatterns/sampled-image-contrast.html\n [low-contrast] sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.3:1 (need 4.5:1) — text #3a3632 on dark.jpg; p90 of 36 samples, median 1.2:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.webp; p90 of 36 samples, median 1.2:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.1:1 (need 4.5:1) — text #fdfdfd on data:image/png; p90 of 36 samples, median 1.1:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 2.5:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 2.4:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.8:1 (need 4.5:1) — text #262421 on cutout.png; p90 of 36 samples, median 1.8:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.2:1 (need 3:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n [low-contrast] sampled (coarse) 1.2:1 (need 4.5:1) — text #fdfdfd on light.png; p90 of 36 samples, median 1.1:1\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n\n9 anti-patterns found.\n",
|
||||
"exit": 2,
|
||||
"signal": null,
|
||||
"files": {}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user