diff --git a/crates/core/src/browser/page_checks.rs b/crates/core/src/browser/page_checks.rs index c738be1f0..df0040fbf 100644 --- a/crates/core/src/browser/page_checks.rs +++ b/crates/core/src/browser/page_checks.rs @@ -7,8 +7,8 @@ #![allow(unused_imports)] use super::dom::{ - class_attr, closest_or_none, direct_text, has_direct_text_longer_than, pf0, style_px, - tag_lower, Dom, ElId, ElStyle, Rect, + ancestors_inclusive, class_attr, closest_or_none, direct_text, has_direct_text_longer_than, pf0, + style_px, tag_lower, Dom, ElId, ElStyle, Rect, }; use super::element_checks::{class_selector, effective_opacity_dom, is_rendered_for_browser_rule}; use super::{BrowserFinding, ElFinding}; @@ -16,7 +16,10 @@ use crate::checks::measures::{ cream_from_class_list, is_cream_color, is_opaque_decorated_box, is_screen_reader_only_text_style, SrOnlyMetrics, StyleMap, }; -use crate::checks::rules::{is_card_like_from_props, RuleHit}; +use crate::checks::rules::{ + check_flat_type_hierarchy_samples, is_card_like_from_props, type_hierarchy_role, RuleHit, + TypeSample, TYPE_HIERARCHY_SELECTOR, +}; use crate::color::parse_any_color; use crate::constants::{is_brand_font_on_own_domain, CSS_GENERIC_FONTS, OVERUSED_FONTS, SAFE_TAGS}; use crate::js::{self, math_max, math_min, math_round, number_to_string, parse_float, to_fixed}; @@ -162,42 +165,72 @@ pub fn check_typography(dom: &dyn Dom) -> Vec { } } - let mut sizes: Vec = Vec::new(); - for el in dom - .query_all(None, "h1,h2,h3,h4,h5,h6,p,span,a,li,td,th,label,button,div") - .unwrap_or_default() - { - let fs = parse_float(&dom.style(el, "fontSize")); - if fs > 0.0 && fs < 200.0 { - let v = math_round(fs * 10.0) / 10.0; - if !sizes.iter().any(|s| *s == v) { - sizes.push(v); - } - } - } - if sizes.len() >= 3 { - let mut sorted = sizes.clone(); - sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); - let ratio = sorted[sorted.len() - 1] / sorted[0]; - if ratio < 2.0 { - findings.push(BrowserFinding::new( - "flat-type-hierarchy", - format!( - "Sizes: {} (ratio {}:1)", - sorted - .iter() - .map(|s| format!("{}px", number_to_string(*s))) - .collect::>() - .join(", "), - to_fixed(ratio, 1) - ), - )); - } + for hit in check_flat_type_hierarchy_from_dom(dom, Some(TYPE_HIERARCHY_SKIP_SELECTOR)) { + findings.push(BrowserFinding::new(&hit.id, hit.snippet)); } findings } +/// The overlay chrome `checkTypography` hands `checkFlatTypeHierarchyFromDoc` +/// as its `skipElement` selector. +pub const TYPE_HIERARCHY_SKIP_SELECTOR: &str = + ".impeccable-overlay, .impeccable-label, .impeccable-banner, .impeccable-tooltip, [id^=\"impeccable-live-\"]"; + +/// JS: checks.mjs#isRenderedTypeElement over a live DOM. +fn is_rendered_type_element(dom: &dyn Dom, el: ElId) -> bool { + for current in ancestors_inclusive(dom, el) { + if dom.hidden_prop(current) || dom.attr(current, "hidden").is_some() { + return false; + } + let display = js::to_lower_case(&dom.style(current, "display")); + let visibility = js::to_lower_case(&dom.style(current, "visibility")); + let content_visibility = js::to_lower_case(&dom.style(current, "contentVisibility")); + if display == "none" + || visibility == "hidden" + || visibility == "collapse" + || content_visibility == "hidden" + { + return false; + } + let opacity = parse_float(&dom.style(current, "opacity")); + if opacity.is_finite() && opacity <= 0.01 { + return false; + } + } + true +} + +/// JS: checks.mjs#checkFlatTypeHierarchyFromDoc over a live DOM. +pub fn check_flat_type_hierarchy_from_dom( + dom: &dyn Dom, + skip_selector: Option<&str>, +) -> Vec { + let mut samples: Vec = Vec::new(); + for el in dom + .query_all(None, TYPE_HIERARCHY_SELECTOR) + .unwrap_or_default() + { + if let Some(sel) = skip_selector { + if closest_or_none(dom, el, sel).is_some() { + continue; + } + } + if js::trim(&dom.text_content(el)).is_empty() || !is_rendered_type_element(dom, el) { + continue; + } + let font_size = parse_float(&dom.style(el, "fontSize")); + if !font_size.is_finite() || font_size < 8.0 || font_size >= 200.0 { + continue; + } + samples.push(TypeSample { + role: type_hierarchy_role(&tag_lower(dom, el)), + size: font_size, + }); + } + check_flat_type_hierarchy_samples(&samples) +} + /// JS: checks.mjs#isCardLikeDOM(el) pub fn is_card_like_dom(dom: &dyn Dom, el: ElId) -> bool { let tag = tag_lower(dom, el); @@ -1404,11 +1437,54 @@ mod tests { d.set_style(s, "fontFamily", "Georgia"); d.set_style(s, "fontSize", "24px"); let f = check_typography(&d); - assert_eq!(f.len(), 2, "{f:?}"); + // One `body` role is under TYPE_HIERARCHY_MIN_ROLES, so the flat-type + // rule abstains and only the font finding stands (#702). + assert_eq!(f.len(), 1, "{f:?}"); assert_eq!(f[0].type_, "overused-font"); assert_eq!(f[0].detail, "Primary font: inter (95% of text)"); - assert_eq!(f[1].type_, "flat-type-hierarchy"); - assert_eq!(f[1].detail, "Sizes: 16px, 18px, 24px (ratio 1.5:1)"); + } + + #[test] + fn flat_type_hierarchy_roles() { + let mut d = FakeDom::new(); + let (_h, body) = d.with_page(); + for (tag, size) in [("p", "16px"), ("p", "16px"), ("h2", "17px"), ("h1", "18px")] { + let el = d.add(Some(body), tag); + d.add_text(el, "text"); + d.set_style(el, "fontSize", size); + } + let f = check_typography(&d); + assert_eq!(f.len(), 1, "{f:?}"); + assert_eq!(f[0].type_, "flat-type-hierarchy"); + assert_eq!( + f[0].detail, + "Role sizes: body 16px, h2 17px, h1 18px (largest adjacent step 1.06:1; target 1.25:1)" + ); + + // A clear step at any adjacent pair clears the rule. + let mut d = FakeDom::new(); + let (_h, body) = d.with_page(); + for (tag, size) in [("p", "16px"), ("h2", "24px"), ("h1", "40px")] { + let el = d.add(Some(body), tag); + d.add_text(el, "text"); + d.set_style(el, "fontSize", size); + } + assert!(check_typography(&d).is_empty()); + + // A hidden ancestor takes its text out of the sample. + let mut d = FakeDom::new(); + let (_h, body) = d.with_page(); + for (tag, size) in [("p", "16px"), ("p", "16px"), ("h2", "17px")] { + let el = d.add(Some(body), tag); + d.add_text(el, "text"); + d.set_style(el, "fontSize", size); + } + let wrap = d.add(Some(body), "div"); + d.set_style(wrap, "display", "none"); + let h1 = d.add(Some(wrap), "h1"); + d.add_text(h1, "text"); + d.set_style(h1, "fontSize", "18px"); + assert!(check_typography(&d).is_empty()); } #[test] diff --git a/crates/core/src/checks/rules.rs b/crates/core/src/checks/rules.rs index 39d25784c..a1dbfff40 100644 --- a/crates/core/src/checks/rules.rs +++ b/crates/core/src/checks/rules.rs @@ -794,6 +794,113 @@ pub fn check_glow(opts: &GlowOpts) -> Vec { } } +// ─── Section 6 shared: flat type hierarchy ────────────────────────────────── + +/// JS: checks.mjs#TYPE_HIERARCHY_SELECTOR +pub const TYPE_HIERARCHY_SELECTOR: &str = "h1,h2,h3,h4,h5,h6,p,li,td,th,dd,blockquote,figcaption"; +/// JS: checks.mjs#TYPE_HIERARCHY_MIN_ROLES +pub const TYPE_HIERARCHY_MIN_ROLES: usize = 3; +/// JS: checks.mjs#TYPE_HIERARCHY_MIN_STEP_RATIO +pub const TYPE_HIERARCHY_MIN_STEP_RATIO: f64 = 1.25; + +/// One `{ role, size }` entry the JS pushes into `samples`. +#[derive(Debug, Clone, PartialEq)] +pub struct TypeSample { + pub role: String, + pub size: f64, +} + +/// JS: checks.mjs#typeHierarchyRole +pub fn type_hierarchy_role(tag: &str) -> String { + let tag = js::to_lower_case(tag); + let b = tag.as_bytes(); + if b.len() == 2 && b[0] == b'h' && (b'1'..=b'6').contains(&b[1]) { + tag + } else { + "body".to_string() + } +} + +/// JS: checks.mjs#dominantTypeRoleSize +fn dominant_type_role_size(samples: &[f64]) -> Option { + // `new Map()` keeps insertion order; the JS sorts by count desc then size asc. + let mut counts: Vec<(f64, f64)> = Vec::new(); + for size in samples { + match counts + .iter_mut() + .find(|(k, _)| crate::js_ext_b::same_value_zero(*k, *size)) + { + Some(slot) => slot.1 += 1.0, + None => counts.push((*size, 1.0)), + } + } + let mut ranked = counts; + ranked.sort_by(|a, b| { + b.1.partial_cmp(&a.1) + .unwrap_or(std::cmp::Ordering::Equal) + .then(a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal)) + }); + if ranked.len() > 1 && ranked[0].1 == ranked[1].1 { + return None; + } + ranked.first().map(|(size, _)| *size) +} + +/// JS: checks.mjs#checkFlatTypeHierarchySamples +pub fn check_flat_type_hierarchy_samples(samples: &[TypeSample]) -> Vec { + // `new Map()` keyed by role, in first-seen order. + let mut by_role: Vec<(String, Vec)> = Vec::new(); + for sample in samples { + let size = math_round(sample.size * 10.0) / 10.0; + if sample.role.is_empty() || !size.is_finite() || size < 8.0 || size >= 200.0 { + continue; + } + match by_role.iter_mut().find(|(r, _)| *r == sample.role) { + Some(slot) => slot.1.push(size), + None => by_role.push((sample.role.clone(), vec![size])), + } + } + + let mut roles: Vec<(String, f64)> = by_role + .into_iter() + .filter_map(|(role, sizes)| dominant_type_role_size(&sizes).map(|size| (role, size))) + .collect(); + + if roles.len() < TYPE_HIERARCHY_MIN_ROLES { + return Vec::new(); + } + + roles.sort_by(|a, b| { + a.1.partial_cmp(&b.1) + .unwrap_or(std::cmp::Ordering::Equal) + // JS-PARITY: checks.mjs sorts ties with `a.role.localeCompare(b.role)`. + // Every role is `body` or `h1`..`h6`, lowercase ASCII, where the ICU + // root collation and byte order agree. + .then_with(|| a.0.cmp(&b.0)) + }); + let mut largest_step = 1.0f64; + for i in 1..roles.len() { + largest_step = math_max(largest_step, roles[i].1 / roles[i - 1].1); + } + if largest_step >= TYPE_HIERARCHY_MIN_STEP_RATIO { + return Vec::new(); + } + + let role_sizes: Vec = roles + .iter() + .map(|(role, size)| format!("{} {}px", role, number_to_string(*size))) + .collect(); + vec![RuleHit::new( + "flat-type-hierarchy", + format!( + "Role sizes: {} (largest adjacent step {}:1; target {}:1)", + role_sizes.join(", "), + to_fixed(largest_step, 2), + number_to_string(TYPE_HIERARCHY_MIN_STEP_RATIO) + ), + )] +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/detect/src/detect_text.rs b/crates/detect/src/detect_text.rs index b8a563f56..abcce7006 100644 --- a/crates/detect/src/detect_text.rs +++ b/crates/detect/src/detect_text.rs @@ -1367,8 +1367,11 @@ pub fn run_text_content_analyzers( return vec![]; } let mut findings = Vec::new(); + // JS: the 3 text-content analyzers sit at indices 1-3 of REGEX_ANALYZERS. + // flat-type-hierarchy left this source-only path in #702 because it needs + // rendered role and usage evidence. for (i, rule_id) in TEXT_CONTENT_ANALYZER_IDS.iter().enumerate() { - let analyzer = REGEX_ANALYZERS[2 + i]; + let analyzer = REGEX_ANALYZERS[1 + i]; let meta = ProfileMeta { engine: "regex", phase: "text-content", diff --git a/crates/detect/src/regex_matchers.rs b/crates/detect/src/regex_matchers.rs index 2cda16132..a30f6e6ff 100644 --- a/crates/detect/src/regex_matchers.rs +++ b/crates/detect/src/regex_matchers.rs @@ -9,7 +9,7 @@ use impeccable_core::constants::{EM_DASH_CHARS_PER_DASH, EM_DASH_FLOOR, OVERUSED use impeccable_core::findings::{finding, Finding}; use impeccable_core::fonts::extract_google_font_families; use impeccable_core::js::{ - self, ci, math_round, number_to_string, parse_float, string_to_number, to_fixed, + self, ci, math_round, number_to_string, parse_float, string_to_number, }; use impeccable_core::js_ext_a::{advance_utf16, slice_utf16_start, utf16_index, utf16_length}; use once_cell::sync::Lazy; @@ -787,33 +787,6 @@ fn utf16_slice(s: &str, start: usize, end: usize) -> String { pub type Analyzer = fn(&str, &str) -> Vec; -re!( - FONT_SIZE_RE, - format!( - "{}{WS}*:{WS}*([0-9.]+)({px}|{rem}|{em}){B}", - ci("font-size"), - px = ci("px"), - rem = ci("rem"), - em = ci("em") - ) -); -re!( - CLAMP_SIZE_RE, - format!( - "{fs}{WS}*:{WS}*{clamp}\\({WS}*([0-9.]+)({px}|{rem}|{em}){WS}*,{WS}*[^,]+,{WS}*([0-9.]+)({px}|{rem}|{em}){WS}*\\)", - fs = ci("font-size"), - clamp = ci("clamp"), - px = ci("px"), - rem = ci("rem"), - em = ci("em") - ) -); -re!(FONT_SIZE_WORD_RE, ci("font-size")); -re!( - TEXT_SIZE_CLASS_RE, - format!("{B}{}(?:xs|sm|base|lg|xl|[0-9])", ci("text-")) -); - fn same_value_zero(a: f64, b: f64) -> bool { (a.is_nan() && b.is_nan()) || a == b } @@ -824,85 +797,6 @@ fn set_add(set: &mut Vec, v: f64) { } } -fn analyze_flat_type_hierarchy(content: &str, file_path: &str) -> Vec { - let mut sizes: Vec = Vec::new(); - let rem = 16.0; - for m in FONT_SIZE_RE.captures_iter(content) { - let px = if &m[2] == "px" { - num(&m[1]) - } else { - num(&m[1]) * rem - }; - if px > 0.0 && px < 200.0 { - set_add(&mut sizes, math_round(px * 10.0) / 10.0); - } - } - for m in CLAMP_SIZE_RE.captures_iter(content) { - let a = if &m[2] == "px" { - num(&m[1]) - } else { - num(&m[1]) * rem - }; - set_add(&mut sizes, math_round(a * 10.0) / 10.0); - let b = if &m[4] == "px" { - num(&m[3]) - } else { - num(&m[3]) * rem - }; - set_add(&mut sizes, math_round(b * 10.0) / 10.0); - } - const TW: &[(&str, f64)] = &[ - ("text-xs", 12.0), - ("text-sm", 14.0), - ("text-base", 16.0), - ("text-lg", 18.0), - ("text-xl", 20.0), - ("text-2xl", 24.0), - ("text-3xl", 30.0), - ("text-4xl", 36.0), - ("text-5xl", 48.0), - ("text-6xl", 60.0), - ("text-7xl", 72.0), - ("text-8xl", 96.0), - ("text-9xl", 128.0), - ]; - for (cls, px) in TW { - let re = Regex::new(&format!("{B}{cls}{B}")).unwrap(); - if re.is_match(content) { - set_add(&mut sizes, *px); - } - } - if sizes.len() < 3 { - return vec![]; - } - let mut sorted = sizes.clone(); - sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); - let ratio = sorted[sorted.len() - 1] / sorted[0]; - if ratio >= 2.0 { - return vec![]; - } - let mut line = 1; - for (i, l) in content.split('\n').enumerate() { - if FONT_SIZE_WORD_RE.is_match(l) || TEXT_SIZE_CLASS_RE.is_match(l) { - line = i + 1; - break; - } - } - let list: Vec = sorted - .iter() - .map(|s| format!("{}px", number_to_string(*s))) - .collect(); - vec![finding( - "flat-type-hierarchy", - file_path, - &format!( - "Sizes: {} (ratio {}:1)", - list.join(", "), - to_fixed(ratio, 1) - ), - line as f64, - )] -} re!( SPACING_PX_RE, @@ -1220,7 +1114,6 @@ fn analyze_marquee(content: &str, file_path: &str) -> Vec { /// JS `REGEX_ANALYZERS` in order. pub const REGEX_ANALYZERS: &[Analyzer] = &[ - analyze_flat_type_hierarchy, analyze_monotonous_spacing, analyze_em_dash_overuse, analyze_marketing_buzzword, @@ -1240,7 +1133,6 @@ pub const TEXT_CONTENT_ANALYZER_IDS: &[&str] = &[ /// The ruleIds the JS assigns to analyzers by index (`analyzerIds[i] || analyzer-${i+1}`). pub fn analyzer_rule_id(i: usize) -> String { const IDS: &[&str] = &[ - "flat-type-hierarchy", "monotonous-spacing", "em-dash-overuse", "marketing-buzzword", diff --git a/crates/foundation/src/registry.rs b/crates/foundation/src/registry.rs index 9120bbdc1..0535727df 100644 --- a/crates/foundation/src/registry.rs +++ b/crates/foundation/src/registry.rs @@ -73,7 +73,7 @@ pub static ANTIPATTERNS: &[Antipattern] = &[ severity: None, advisory: false, name: "Flat type hierarchy", - description: "Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).", + description: "Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.", skill_section: Some("Typography"), skill_guideline: Some("flat type hierarchy"), }, diff --git a/crates/html/src/cascade/defaults.rs b/crates/html/src/cascade/defaults.rs index ed8b2a42b..5abc3c5c1 100644 --- a/crates/html/src/cascade/defaults.rs +++ b/crates/html/src/cascade/defaults.rs @@ -109,6 +109,7 @@ pub const STATIC_DEFAULT_STYLE: &[(&str, &str)] = &[ ("marginLeft", "0px"), ("position", "static"), ("visibility", "visible"), + ("contentVisibility", "visible"), ("opacity", "1"), ("top", "auto"), ("right", "auto"), diff --git a/crates/html/src/page.rs b/crates/html/src/page.rs index 7224a64ca..871f294a4 100644 --- a/crates/html/src/page.rs +++ b/crates/html/src/page.rs @@ -8,12 +8,15 @@ use crate::background::{read_own_background_color, resolve_border_radius_px, sv} use crate::dom::{StaticDocument, StaticElement}; use crate::quality::{has_nonblank_direct_text, pf0}; use impeccable_core::checks::measures::{cream_from_class_list, is_cream_color}; -use impeccable_core::checks::rules::{is_card_like_from_props, RuleHit}; +use impeccable_core::checks::rules::{ + check_flat_type_hierarchy_samples, is_card_like_from_props, type_hierarchy_role, RuleHit, + TypeSample, TYPE_HIERARCHY_SELECTOR, +}; use impeccable_core::checks::text_rules::{ is_repeated_text_container, REPEATED_TEXT_CONTAINER_TAGS, REPEATED_TEXT_SKIP_SELECTOR, }; use impeccable_core::constants::{CSS_GENERIC_FONTS, OVERUSED_FONTS, SAFE_TAGS}; -use impeccable_core::js::{self, math_round, number_to_string, parse_float, to_fixed}; +use impeccable_core::js::{self, number_to_string, parse_float}; use impeccable_core::js_ext_b::{slice_utf16_prefix, utf16_len}; use once_cell::sync::Lazy; use regex::Regex; @@ -60,40 +63,62 @@ pub fn check_static_page_typography(doc: &StaticDocument) -> Vec { format!("Primary font: {}", font), )); } - let mut sizes: Vec = Vec::new(); - for el in - doc.query_selector_all("h1, h2, h3, h4, h5, h6, p, span, a, li, td, th, label, button, div") - { - let font_size = parse_float(sv(el.style(), "fontSize")); - if font_size >= 8.0 && font_size < 200.0 { - let v = math_round(font_size * 10.0) / 10.0; - if !sizes.contains(&v) { - sizes.push(v); - } - } - } - if sizes.len() >= 3 { - let mut sorted = sizes.clone(); - sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); - let ratio = sorted[sorted.len() - 1] / sorted[0]; - if ratio < 2.0 { - let list: Vec = sorted - .iter() - .map(|s| format!("{}px", number_to_string(*s))) - .collect(); - findings.push(RuleHit::new( - "flat-type-hierarchy", - format!( - "Sizes: {} (ratio {}:1)", - list.join(", "), - to_fixed(ratio, 1) - ), - )); - } - } + findings.extend(check_flat_type_hierarchy_from_doc(doc)); findings } +/// JS: checks.mjs#isRenderedTypeElement over the static cascade. +/// +/// JS-PARITY: jsdom's `el.hidden` reflects the `hidden` attribute, which the +/// attribute test already covers. `contentVisibility` only ever reads its +/// `STATIC_DEFAULT_STYLE` default here: css-cascade.mjs#STATIC_PROP_MAP has no +/// `content-visibility` entry, so a declared `content-visibility: hidden` +/// never reaches the static computed style. +fn is_rendered_type_element(el: &StaticElement<'_>) -> bool { + let mut current = Some(el.clone()); + while let Some(node) = current { + if node.get_attribute("hidden").is_some() { + return false; + } + let style = node.style(); + let display = js::to_lower_case(sv(style, "display")); + let visibility = js::to_lower_case(sv(style, "visibility")); + let content_visibility = js::to_lower_case(sv(style, "contentVisibility")); + if display == "none" + || visibility == "hidden" + || visibility == "collapse" + || content_visibility == "hidden" + { + return false; + } + let opacity = parse_float(sv(style, "opacity")); + if opacity.is_finite() && opacity <= 0.01 { + return false; + } + current = node.parent_element(); + } + true +} + +/// JS: checks.mjs#checkFlatTypeHierarchyFromDoc over the static document. +pub fn check_flat_type_hierarchy_from_doc(doc: &StaticDocument) -> Vec { + let mut samples: Vec = Vec::new(); + for el in doc.query_selector_all(TYPE_HIERARCHY_SELECTOR) { + if js::trim(&el.text_content()).is_empty() || !is_rendered_type_element(&el) { + continue; + } + let font_size = parse_float(sv(el.style(), "fontSize")); + if !font_size.is_finite() || font_size < 8.0 || font_size >= 200.0 { + continue; + } + samples.push(TypeSample { + role: type_hierarchy_role(&el.tag_lower()), + size: font_size, + }); + } + check_flat_type_hierarchy_samples(&samples) +} + // ─── Nested cards ─────────────────────────────────────────────────────────── static SHADOW_CLASS_RE: Lazy = Lazy::new(|| { diff --git a/tests/oracle/golden/detect-fixture-json-glow-html.json b/tests/oracle/golden/detect-fixture-json-glow-html.json index bd931a04a..db8ea8c97 100644 --- a/tests/oracle/golden/detect-fixture-json-glow-html.json +++ b/tests/oracle/golden/detect-fixture-json-glow-html.json @@ -1,5 +1,5 @@ { - "stdout": "[\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#3b82f6)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#8b5cf6)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#06b6d4)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#a855f7)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#ec4899)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#00c0c2)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#3b82f6)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#b447eb)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#63d18f)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset text-shadow glow (#22d3ee)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#a46bf5)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#d946ef)\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Sizes: 11px, 12px, 13px, 14px, 16px (ratio 1.5:1)\"\n },\n {\n \"antipattern\": \"buried-raster\",\n \"name\": \"Raster buried under a wash or opacity\",\n \"description\": \"A background image under a near-opaque gradient wash, or a raster on an element at near-zero opacity, never reaches the screen: the page shows the wash, and the produced texture or photo ships as a compliance token. Let the material show (a tint under 0.9 alpha, a blend mode, an opacity you can see) or remove the file.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"raster under a near-opaque gradient wash: linear-gradient(#111827, #0b1220), url('/fixtures/antipatterns/missing-photo.png')\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#3b82f6)\"\n }\n]\n", + "stdout": "[\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#3b82f6)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#8b5cf6)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#06b6d4)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#a855f7)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#ec4899)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#00c0c2)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#3b82f6)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#b447eb)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#63d18f)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset text-shadow glow (#22d3ee)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#a46bf5)\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#d946ef)\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Role sizes: h3 11px, body 12px, h4 13px, h2 14px (largest adjacent step 1.09:1; target 1.25:1)\"\n },\n {\n \"antipattern\": \"buried-raster\",\n \"name\": \"Raster buried under a wash or opacity\",\n \"description\": \"A background image under a near-opaque gradient wash, or a raster on an element at near-zero opacity, never reaches the screen: the page shows the wash, and the produced texture or photo ships as a compliance token. Let the material show (a tint under 0.9 alpha, a blend mode, an opacity you can see) or remove the file.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"raster under a near-opaque gradient wash: linear-gradient(#111827, #0b1220), url('/fixtures/antipatterns/missing-photo.png')\"\n },\n {\n \"antipattern\": \"dark-glow\",\n \"name\": \"Glowing shadow accents\",\n \"description\": \"Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/glow.html\",\n \"line\": 0,\n \"snippet\": \"Zero-offset box-shadow glow (#3b82f6)\"\n }\n]\n", "stderr": "", "exit": 2, "signal": null, diff --git a/tests/oracle/golden/detect-fixture-json-icon-tile-stack-html.json b/tests/oracle/golden/detect-fixture-json-icon-tile-stack-html.json index 7a1a5dd26..aae8d6997 100644 --- a/tests/oracle/golden/detect-fixture-json-icon-tile-stack-html.json +++ b/tests/oracle/golden/detect-fixture-json-icon-tile-stack-html.json @@ -1,5 +1,5 @@ { - "stdout": "[\n {\n \"antipattern\": \"icon-tile-stack\",\n \"name\": \"Icon tile stacked above heading\",\n \"description\": \"A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"80x80px icon tile above h3 \\\"Lightning Fast\\\"\"\n },\n {\n \"antipattern\": \"icon-tile-stack\",\n \"name\": \"Icon tile stacked above heading\",\n \"description\": \"A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"64x64px icon tile above h3 \\\"Secure Storage\\\"\"\n },\n {\n \"antipattern\": \"icon-tile-stack\",\n \"name\": \"Icon tile stacked above heading\",\n \"description\": \"A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"72x72px icon tile above h3 \\\"Easy Setup\\\"\"\n },\n {\n \"antipattern\": \"icon-tile-stack\",\n \"name\": \"Icon tile stacked above heading\",\n \"description\": \"A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"96x96px icon tile above h3 \\\"Powerful Analytics\\\"\"\n },\n {\n \"antipattern\": \"icon-tile-stack\",\n \"name\": \"Icon tile stacked above heading\",\n \"description\": \"A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"48x48px icon tile above h3 \\\"Emoji Inline Icon\\\"\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"Sizes: 14px, 16px, 18px, 24px (ratio 1.7:1)\"\n },\n {\n \"antipattern\": \"ai-color-palette\",\n \"name\": \"AI color palette\",\n \"description\": \"Purple/violet gradients and cyan-on-dark are the most recognizable tells of AI-generated UIs. Choose a distinctive, intentional palette.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"Purple/violet accent colors detected\"\n },\n {\n \"antipattern\": \"marketing-buzzword\",\n \"name\": \"Marketing buzzword\",\n \"description\": \"Generic SaaS phrases (streamline / empower / supercharge / world-class / enterprise-grade / next-generation / cutting-edge / etc) are instant AI tells. Pick a specific verb and noun that says what the product literally does.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"1 buzzword phrase: \\\"ure Storage Enterprise-grade security fo\\\"\"\n }\n]\n", + "stdout": "[\n {\n \"antipattern\": \"icon-tile-stack\",\n \"name\": \"Icon tile stacked above heading\",\n \"description\": \"A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"80x80px icon tile above h3 \\\"Lightning Fast\\\"\"\n },\n {\n \"antipattern\": \"icon-tile-stack\",\n \"name\": \"Icon tile stacked above heading\",\n \"description\": \"A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"64x64px icon tile above h3 \\\"Secure Storage\\\"\"\n },\n {\n \"antipattern\": \"icon-tile-stack\",\n \"name\": \"Icon tile stacked above heading\",\n \"description\": \"A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"72x72px icon tile above h3 \\\"Easy Setup\\\"\"\n },\n {\n \"antipattern\": \"icon-tile-stack\",\n \"name\": \"Icon tile stacked above heading\",\n \"description\": \"A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"96x96px icon tile above h3 \\\"Powerful Analytics\\\"\"\n },\n {\n \"antipattern\": \"icon-tile-stack\",\n \"name\": \"Icon tile stacked above heading\",\n \"description\": \"A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"48x48px icon tile above h3 \\\"Emoji Inline Icon\\\"\"\n },\n {\n \"antipattern\": \"ai-color-palette\",\n \"name\": \"AI color palette\",\n \"description\": \"Purple/violet gradients and cyan-on-dark are the most recognizable tells of AI-generated UIs. Choose a distinctive, intentional palette.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"Purple/violet accent colors detected\"\n },\n {\n \"antipattern\": \"marketing-buzzword\",\n \"name\": \"Marketing buzzword\",\n \"description\": \"Generic SaaS phrases (streamline / empower / supercharge / world-class / enterprise-grade / next-generation / cutting-edge / etc) are instant AI tells. Pick a specific verb and noun that says what the product literally does.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/icon-tile-stack.html\",\n \"line\": 0,\n \"snippet\": \"1 buzzword phrase: \\\"ure Storage Enterprise-grade security fo\\\"\"\n }\n]\n", "stderr": "", "exit": 2, "signal": null, diff --git a/tests/oracle/golden/detect-fixture-json-layout-html.json b/tests/oracle/golden/detect-fixture-json-layout-html.json index 85afca9b2..d112a0321 100644 --- a/tests/oracle/golden/detect-fixture-json-layout-html.json +++ b/tests/oracle/golden/detect-fixture-json-layout-html.json @@ -1,5 +1,5 @@ { - "stdout": "[\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Sizes: 11px, 14px, 16px (ratio 1.5:1)\"\n },\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n },\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n },\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n },\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n }\n]\n", + "stdout": "[\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n },\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n },\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n },\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n }\n]\n", "stderr": "", "exit": 2, "signal": null, diff --git a/tests/oracle/golden/detect-fixture-json-modern-color-borders-html.json b/tests/oracle/golden/detect-fixture-json-modern-color-borders-html.json index 3c73b2f61..040003088 100644 --- a/tests/oracle/golden/detect-fixture-json-modern-color-borders-html.json +++ b/tests/oracle/golden/detect-fixture-json-modern-color-borders-html.json @@ -1,5 +1,5 @@ { - "stdout": "[\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 3px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 6px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 6px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 6px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 3px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 6px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 5px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-right: 4px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 6px\"\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\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"4.49:1 (need 4.5:1) — text #64748b on #fef7f2\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"Sizes: 13px, 14px, 16px (ratio 1.2:1)\"\n }\n]\n", + "stdout": "[\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 3px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 6px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 6px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 6px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 3px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 6px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 5px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-right: 4px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 6px\"\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\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"4.49:1 (need 4.5:1) — text #64748b on #fef7f2\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/modern-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"Role sizes: body 13px, h2 14px, h3 14px (largest adjacent step 1.08:1; target 1.25:1)\"\n }\n]\n", "stderr": "", "exit": 2, "signal": null, diff --git a/tests/oracle/golden/detect-fixture-json-motion-html.json b/tests/oracle/golden/detect-fixture-json-motion-html.json index 1f8377851..59314c0b4 100644 --- a/tests/oracle/golden/detect-fixture-json-motion-html.json +++ b/tests/oracle/golden/detect-fixture-json-motion-html.json @@ -1,5 +1,5 @@ { - "stdout": "[\n {\n \"antipattern\": \"bounce-easing\",\n \"name\": \"Bounce or elastic easing\",\n \"description\": \"Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"animation: bounce-keyframe\"\n },\n {\n \"antipattern\": \"bounce-easing\",\n \"name\": \"Bounce or elastic easing\",\n \"description\": \"Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"cubic-bezier(0.68, -0.55, 0.265, 1.55)\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: width\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: height\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: padding\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: margin\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: max-height\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: width, height\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: width\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: width\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"Sizes: 11px, 12px, 13px, 14px, 16px (ratio 1.5:1)\"\n }\n]\n", + "stdout": "[\n {\n \"antipattern\": \"bounce-easing\",\n \"name\": \"Bounce or elastic easing\",\n \"description\": \"Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"animation: bounce-keyframe\"\n },\n {\n \"antipattern\": \"bounce-easing\",\n \"name\": \"Bounce or elastic easing\",\n \"description\": \"Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"cubic-bezier(0.68, -0.55, 0.265, 1.55)\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: width\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: height\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: padding\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: margin\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: max-height\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: width, height\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: width\"\n },\n {\n \"antipattern\": \"layout-transition\",\n \"name\": \"Layout property animation\",\n \"description\": \"Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"transition: width\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/motion.html\",\n \"line\": 0,\n \"snippet\": \"Role sizes: h3 11px, body 12px, h4 13px, h2 14px (largest adjacent step 1.09:1; target 1.25:1)\"\n }\n]\n", "stderr": "", "exit": 2, "signal": null, diff --git a/tests/oracle/golden/detect-fixture-json-named-color-borders-html.json b/tests/oracle/golden/detect-fixture-json-named-color-borders-html.json index c41ff3b62..ce38ef9e9 100644 --- a/tests/oracle/golden/detect-fixture-json-named-color-borders-html.json +++ b/tests/oracle/golden/detect-fixture-json-named-color-borders-html.json @@ -1,5 +1,5 @@ { - "stdout": "[\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 8px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 5px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-top: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 3px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 6px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 7px\"\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\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"4.4:1 (need 4.5:1) — text #64748b on #f6f6f6\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"Sizes: 13px, 14px, 16px (ratio 1.2:1)\"\n }\n]\n", + "stdout": "[\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 8px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 5px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-top: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 3px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 6px + border-radius: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 7px\"\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\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"4.4:1 (need 4.5:1) — text #64748b on #f6f6f6\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/named-color-borders.html\",\n \"line\": 0,\n \"snippet\": \"Role sizes: body 13px, h2 14px, h3 14px (largest adjacent step 1.08:1; target 1.25:1)\"\n }\n]\n", "stderr": "", "exit": 2, "signal": null, diff --git a/tests/oracle/golden/detect-fixture-json-numbered-section-markers-html.json b/tests/oracle/golden/detect-fixture-json-numbered-section-markers-html.json index afe20a428..1e26561eb 100644 --- a/tests/oracle/golden/detect-fixture-json-numbered-section-markers-html.json +++ b/tests/oracle/golden/detect-fixture-json-numbered-section-markers-html.json @@ -1,7 +1,7 @@ { - "stdout": "[]\n", + "stdout": "[\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/numbered-section-markers.html\",\n \"line\": 0,\n \"snippet\": \"Role sizes: body 16px, h1 16px, h2 16px (largest adjacent step 1.00:1; target 1.25:1)\"\n }\n]\n", "stderr": "", - "exit": 0, + "exit": 2, "signal": null, "files": {} } diff --git a/tests/oracle/golden/detect-fixture-json-oklch-neon-text-html.json b/tests/oracle/golden/detect-fixture-json-oklch-neon-text-html.json index 2fe01bf4c..9de5660cf 100644 --- a/tests/oracle/golden/detect-fixture-json-oklch-neon-text-html.json +++ b/tests/oracle/golden/detect-fixture-json-oklch-neon-text-html.json @@ -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\": \"/tests/fixtures/antipatterns/oklch-neon-text.html\",\n \"line\": 0,\n \"snippet\": \"1.3:1 (need 4.5:1) — text #00f4f6 on #f5f5f5\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/oklch-neon-text.html\",\n \"line\": 0,\n \"snippet\": \"Sizes: 13px, 16px, 18px (ratio 1.4: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\": \"/tests/fixtures/antipatterns/oklch-neon-text.html\",\n \"line\": 0,\n \"snippet\": \"1.3:1 (need 4.5:1) — text #00f4f6 on #f5f5f5\"\n }\n]\n", "stderr": "", "exit": 2, "signal": null, diff --git a/tests/oracle/golden/detect-fixture-json-typography-should-flag-html.json b/tests/oracle/golden/detect-fixture-json-typography-should-flag-html.json index 8332c38a7..87c6e1f72 100644 --- a/tests/oracle/golden/detect-fixture-json-typography-should-flag-html.json +++ b/tests/oracle/golden/detect-fixture-json-typography-should-flag-html.json @@ -1,5 +1,5 @@ { - "stdout": "[\n {\n \"antipattern\": \"overused-font\",\n \"name\": \"Overused font\",\n \"description\": \"Inter, Roboto, Fraunces, Geist, Plus Jakarta Sans, and Space Grotesk are used on so many sites they no longer feel distinctive. Each new wave of AI-generated UIs converges on the same handful of faces. Choose a face that gives your interface personality.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/typography-should-flag.html\",\n \"line\": 0,\n \"snippet\": \"Primary font: inter\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/typography-should-flag.html\",\n \"line\": 0,\n \"snippet\": \"Sizes: 13px, 14px, 15px, 16px, 18px (ratio 1.4:1)\"\n }\n]\n", + "stdout": "[\n {\n \"antipattern\": \"overused-font\",\n \"name\": \"Overused font\",\n \"description\": \"Inter, Roboto, Fraunces, Geist, Plus Jakarta Sans, and Space Grotesk are used on so many sites they no longer feel distinctive. Each new wave of AI-generated UIs converges on the same handful of faces. Choose a face that gives your interface personality.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/typography-should-flag.html\",\n \"line\": 0,\n \"snippet\": \"Primary font: inter\"\n },\n {\n \"antipattern\": \"flat-type-hierarchy\",\n \"name\": \"Flat type hierarchy\",\n \"description\": \"Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/typography-should-flag.html\",\n \"line\": 0,\n \"snippet\": \"Role sizes: body 14px, h3 15px, h2 16px, h1 18px (largest adjacent step 1.13:1; target 1.25:1)\"\n }\n]\n", "stderr": "", "exit": 2, "signal": null, diff --git a/tests/oracle/golden/detect-fixture-text-glow-html.json b/tests/oracle/golden/detect-fixture-text-glow-html.json index 14d0aafee..100719fb7 100644 --- a/tests/oracle/golden/detect-fixture-text-glow-html.json +++ b/tests/oracle/golden/detect-fixture-text-glow-html.json @@ -1,6 +1,6 @@ { "stdout": "", - "stderr": "\n/tests/fixtures/antipatterns/glow.html\n [dark-glow] Zero-offset box-shadow glow (#3b82f6)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#8b5cf6)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#06b6d4)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#a855f7)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#ec4899)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#00c0c2)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#3b82f6)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#b447eb)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#63d18f)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset text-shadow glow (#22d3ee)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#a46bf5)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#d946ef)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [flat-type-hierarchy] Sizes: 11px, 12px, 13px, 14px, 16px (ratio 1.5:1)\n → Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\n [buried-raster] raster under a near-opaque gradient wash: linear-gradient(#111827, #0b1220), url('/fixtures/antipatterns/missing-photo.png')\n → A background image under a near-opaque gradient wash, or a raster on an element at near-zero opacity, never reaches the screen: the page shows the wash, and the produced texture or photo ships as a compliance token. Let the material show (a tint under 0.9 alpha, a blend mode, an opacity you can see) or remove the file.\n [dark-glow] Zero-offset box-shadow glow (#3b82f6)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n\n15 anti-patterns found.\n", + "stderr": "\n/tests/fixtures/antipatterns/glow.html\n [dark-glow] Zero-offset box-shadow glow (#3b82f6)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#8b5cf6)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#06b6d4)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#a855f7)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#ec4899)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#00c0c2)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#3b82f6)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#b447eb)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#63d18f)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset text-shadow glow (#22d3ee)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#a46bf5)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [dark-glow] Zero-offset box-shadow glow (#d946ef)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n [flat-type-hierarchy] Role sizes: h3 11px, body 12px, h4 13px, h2 14px (largest adjacent step 1.09:1; target 1.25:1)\n → Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\n [buried-raster] raster under a near-opaque gradient wash: linear-gradient(#111827, #0b1220), url('/fixtures/antipatterns/missing-photo.png')\n → A background image under a near-opaque gradient wash, or a raster on an element at near-zero opacity, never reaches the screen: the page shows the wash, and the produced texture or photo ships as a compliance token. Let the material show (a tint under 0.9 alpha, a blend mode, an opacity you can see) or remove the file.\n [dark-glow] Zero-offset box-shadow glow (#3b82f6)\n → Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \"cool\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\n\n15 anti-patterns found.\n", "exit": 2, "signal": null, "files": {} diff --git a/tests/oracle/golden/detect-fixture-text-icon-tile-stack-html.json b/tests/oracle/golden/detect-fixture-text-icon-tile-stack-html.json index 0a1ebcc76..92081c30d 100644 --- a/tests/oracle/golden/detect-fixture-text-icon-tile-stack-html.json +++ b/tests/oracle/golden/detect-fixture-text-icon-tile-stack-html.json @@ -1,6 +1,6 @@ { "stdout": "", - "stderr": "\n/tests/fixtures/antipatterns/icon-tile-stack.html\n [icon-tile-stack] 80x80px icon tile above h3 \"Lightning Fast\"\n → A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\n [icon-tile-stack] 64x64px icon tile above h3 \"Secure Storage\"\n → A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\n [icon-tile-stack] 72x72px icon tile above h3 \"Easy Setup\"\n → A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\n [icon-tile-stack] 96x96px icon tile above h3 \"Powerful Analytics\"\n → A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\n [icon-tile-stack] 48x48px icon tile above h3 \"Emoji Inline Icon\"\n → A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\n [flat-type-hierarchy] Sizes: 14px, 16px, 18px, 24px (ratio 1.7:1)\n → Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\n [ai-color-palette] Purple/violet accent colors detected\n → Purple/violet gradients and cyan-on-dark are the most recognizable tells of AI-generated UIs. Choose a distinctive, intentional palette.\n [marketing-buzzword] 1 buzzword phrase: \"ure Storage Enterprise-grade security fo\"\n → Generic SaaS phrases (streamline / empower / supercharge / world-class / enterprise-grade / next-generation / cutting-edge / etc) are instant AI tells. Pick a specific verb and noun that says what the product literally does.\n\n8 anti-patterns found.\n", + "stderr": "\n/tests/fixtures/antipatterns/icon-tile-stack.html\n [icon-tile-stack] 80x80px icon tile above h3 \"Lightning Fast\"\n → A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\n [icon-tile-stack] 64x64px icon tile above h3 \"Secure Storage\"\n → A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\n [icon-tile-stack] 72x72px icon tile above h3 \"Easy Setup\"\n → A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\n [icon-tile-stack] 96x96px icon tile above h3 \"Powerful Analytics\"\n → A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\n [icon-tile-stack] 48x48px icon tile above h3 \"Emoji Inline Icon\"\n → A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.\n [ai-color-palette] Purple/violet accent colors detected\n → Purple/violet gradients and cyan-on-dark are the most recognizable tells of AI-generated UIs. Choose a distinctive, intentional palette.\n [marketing-buzzword] 1 buzzword phrase: \"ure Storage Enterprise-grade security fo\"\n → Generic SaaS phrases (streamline / empower / supercharge / world-class / enterprise-grade / next-generation / cutting-edge / etc) are instant AI tells. Pick a specific verb and noun that says what the product literally does.\n\n7 anti-patterns found.\n", "exit": 2, "signal": null, "files": {} diff --git a/tests/oracle/golden/detect-fixture-text-layout-html.json b/tests/oracle/golden/detect-fixture-text-layout-html.json index 4afeeea52..6c422cb97 100644 --- a/tests/oracle/golden/detect-fixture-text-layout-html.json +++ b/tests/oracle/golden/detect-fixture-text-layout-html.json @@ -1,6 +1,6 @@ { "stdout": "", - "stderr": "\n/tests/fixtures/antipatterns/layout.html\n [flat-type-hierarchy] Sizes: 11px, 14px, 16px (ratio 1.5:1)\n → Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\n [nested-cards] Card inside card (div)\n → Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\n [nested-cards] Card inside card (div)\n → Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\n [nested-cards] Card inside card (div)\n → Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\n [nested-cards] Card inside card (div)\n → Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\n\n5 anti-patterns found.\n", + "stderr": "\n/tests/fixtures/antipatterns/layout.html\n [nested-cards] Card inside card (div)\n → Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\n [nested-cards] Card inside card (div)\n → Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\n [nested-cards] Card inside card (div)\n → Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\n [nested-cards] Card inside card (div)\n → Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\n\n4 anti-patterns found.\n", "exit": 2, "signal": null, "files": {} diff --git a/tests/oracle/golden/detect-fixture-text-modern-color-borders-html.json b/tests/oracle/golden/detect-fixture-text-modern-color-borders-html.json index 4de7e9d31..23885efe5 100644 --- a/tests/oracle/golden/detect-fixture-text-modern-color-borders-html.json +++ b/tests/oracle/golden/detect-fixture-text-modern-color-borders-html.json @@ -1,6 +1,6 @@ { "stdout": "", - "stderr": "\n/tests/fixtures/antipatterns/modern-color-borders.html\n [side-tab] border-left: 3px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 6px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 6px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 6px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 3px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 6px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 5px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-right: 4px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 6px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [low-contrast] 4.49:1 (need 4.5:1) — text #64748b on #fef7f2\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 [flat-type-hierarchy] Sizes: 13px, 14px, 16px (ratio 1.2:1)\n → Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\n\n14 anti-patterns found.\n", + "stderr": "\n/tests/fixtures/antipatterns/modern-color-borders.html\n [side-tab] border-left: 3px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 6px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 6px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 6px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 3px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 6px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 5px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-right: 4px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 4px + border-radius: 6px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [low-contrast] 4.49:1 (need 4.5:1) — text #64748b on #fef7f2\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 [flat-type-hierarchy] Role sizes: body 13px, h2 14px, h3 14px (largest adjacent step 1.08:1; target 1.25:1)\n → Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\n\n14 anti-patterns found.\n", "exit": 2, "signal": null, "files": {} diff --git a/tests/oracle/golden/detect-fixture-text-motion-html.json b/tests/oracle/golden/detect-fixture-text-motion-html.json index 0ee2b7b13..0bca6379f 100644 --- a/tests/oracle/golden/detect-fixture-text-motion-html.json +++ b/tests/oracle/golden/detect-fixture-text-motion-html.json @@ -1,6 +1,6 @@ { "stdout": "", - "stderr": "\n/tests/fixtures/antipatterns/motion.html\n [bounce-easing] animation: bounce-keyframe\n → Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\n [bounce-easing] cubic-bezier(0.68, -0.55, 0.265, 1.55)\n → Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\n [layout-transition] transition: width\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: height\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: padding\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: margin\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: max-height\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: width, height\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: width\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: width\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [flat-type-hierarchy] Sizes: 11px, 12px, 13px, 14px, 16px (ratio 1.5:1)\n → Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\n\n11 anti-patterns found.\n", + "stderr": "\n/tests/fixtures/antipatterns/motion.html\n [bounce-easing] animation: bounce-keyframe\n → Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\n [bounce-easing] cubic-bezier(0.68, -0.55, 0.265, 1.55)\n → Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\n [layout-transition] transition: width\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: height\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: padding\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: margin\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: max-height\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: width, height\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: width\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [layout-transition] transition: width\n → Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.\n [flat-type-hierarchy] Role sizes: h3 11px, body 12px, h4 13px, h2 14px (largest adjacent step 1.09:1; target 1.25:1)\n → Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\n\n11 anti-patterns found.\n", "exit": 2, "signal": null, "files": {} diff --git a/tests/oracle/golden/detect-fixture-text-named-color-borders-html.json b/tests/oracle/golden/detect-fixture-text-named-color-borders-html.json index 54379d833..0585bad53 100644 --- a/tests/oracle/golden/detect-fixture-text-named-color-borders-html.json +++ b/tests/oracle/golden/detect-fixture-text-named-color-borders-html.json @@ -1,6 +1,6 @@ { "stdout": "", - "stderr": "\n/tests/fixtures/antipatterns/named-color-borders.html\n [side-tab] border-left: 4px + border-radius: 8px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 5px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-top: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 3px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 6px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 7px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [low-contrast] 4.4:1 (need 4.5:1) — text #64748b on #f6f6f6\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 [flat-type-hierarchy] Sizes: 13px, 14px, 16px (ratio 1.2:1)\n → Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\n\n8 anti-patterns found.\n", + "stderr": "\n/tests/fixtures/antipatterns/named-color-borders.html\n [side-tab] border-left: 4px + border-radius: 8px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 5px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-top: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 3px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 6px + border-radius: 4px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [side-tab] border-left: 7px\n → Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\n [low-contrast] 4.4:1 (need 4.5:1) — text #64748b on #f6f6f6\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 [flat-type-hierarchy] Role sizes: body 13px, h2 14px, h3 14px (largest adjacent step 1.08:1; target 1.25:1)\n → Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\n\n8 anti-patterns found.\n", "exit": 2, "signal": null, "files": {} diff --git a/tests/oracle/golden/detect-fixture-text-numbered-section-markers-html.json b/tests/oracle/golden/detect-fixture-text-numbered-section-markers-html.json index 7ea122487..18b5b671b 100644 --- a/tests/oracle/golden/detect-fixture-text-numbered-section-markers-html.json +++ b/tests/oracle/golden/detect-fixture-text-numbered-section-markers-html.json @@ -1,7 +1,7 @@ { "stdout": "", - "stderr": "", - "exit": 0, + "stderr": "\n/tests/fixtures/antipatterns/numbered-section-markers.html\n [flat-type-hierarchy] Role sizes: body 16px, h1 16px, h2 16px (largest adjacent step 1.00:1; target 1.25:1)\n → Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\n\n1 anti-pattern found.\n", + "exit": 2, "signal": null, "files": {} } diff --git a/tests/oracle/golden/detect-fixture-text-oklch-neon-text-html.json b/tests/oracle/golden/detect-fixture-text-oklch-neon-text-html.json index 4af1a99c1..208466e4e 100644 --- a/tests/oracle/golden/detect-fixture-text-oklch-neon-text-html.json +++ b/tests/oracle/golden/detect-fixture-text-oklch-neon-text-html.json @@ -1,6 +1,6 @@ { "stdout": "", - "stderr": "\n/tests/fixtures/antipatterns/oklch-neon-text.html\n [low-contrast] 1.3:1 (need 4.5:1) — text #00f4f6 on #f5f5f5\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 [flat-type-hierarchy] Sizes: 13px, 16px, 18px (ratio 1.4:1)\n → Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\n\n2 anti-patterns found.\n", + "stderr": "\n/tests/fixtures/antipatterns/oklch-neon-text.html\n [low-contrast] 1.3:1 (need 4.5:1) — text #00f4f6 on #f5f5f5\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\n1 anti-pattern found.\n", "exit": 2, "signal": null, "files": {} diff --git a/tests/oracle/golden/detect-fixture-text-typography-should-flag-html.json b/tests/oracle/golden/detect-fixture-text-typography-should-flag-html.json index 7a14199f0..6715964e2 100644 --- a/tests/oracle/golden/detect-fixture-text-typography-should-flag-html.json +++ b/tests/oracle/golden/detect-fixture-text-typography-should-flag-html.json @@ -1,6 +1,6 @@ { "stdout": "", - "stderr": "\n/tests/fixtures/antipatterns/typography-should-flag.html\n [overused-font] Primary font: inter\n → Inter, Roboto, Fraunces, Geist, Plus Jakarta Sans, and Space Grotesk are used on so many sites they no longer feel distinctive. Each new wave of AI-generated UIs converges on the same handful of faces. Choose a face that gives your interface personality.\n [flat-type-hierarchy] Sizes: 13px, 14px, 15px, 16px, 18px (ratio 1.4:1)\n → Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).\n\n2 anti-patterns found.\n", + "stderr": "\n/tests/fixtures/antipatterns/typography-should-flag.html\n [overused-font] Primary font: inter\n → Inter, Roboto, Fraunces, Geist, Plus Jakarta Sans, and Space Grotesk are used on so many sites they no longer feel distinctive. Each new wave of AI-generated UIs converges on the same handful of faces. Choose a face that gives your interface personality.\n [flat-type-hierarchy] Role sizes: body 14px, h3 15px, h2 16px, h1 18px (largest adjacent step 1.13:1; target 1.25:1)\n → Dominant heading and body roles are separated by less than 1.25× at every step, leaving the size hierarchy flat. Add at least one stronger size step.\n\n2 anti-patterns found.\n", "exit": 2, "signal": null, "files": {}