import { BORDER_SAFE_TAGS, GENERIC_FONTS, KNOWN_SERIF_FONTS, OVERUSED_FONTS, SAFE_TAGS, WCAG_LARGE_BOLD_TEXT_PX, WCAG_LARGE_TEXT_PX, isBrandFontOnOwnDomain, } from '../shared/constants.mjs'; import { colorToHex, contrastRatio, getHue, hasChroma, isNeutralColor, parseGradientColors, parseRgb, relativeLuminance, } from '../shared/color.mjs'; const DETECTOR_IS_BROWSER = typeof window !== 'undefined'; // ─── Section 3: Pure Detection ────────────────────────────────────────────── function checkBorders(tag, widths, colors, radius) { if (BORDER_SAFE_TAGS.has(tag)) return []; const findings = []; const sides = ['Top', 'Right', 'Bottom', 'Left']; for (const side of sides) { const w = widths[side]; if (w < 1 || isNeutralColor(colors[side])) continue; const otherSides = sides.filter(s => s !== side); const maxOther = Math.max(...otherSides.map(s => widths[s])); if (!(w >= 2 && (maxOther <= 1 || w >= maxOther * 2))) continue; const sn = side.toLowerCase(); const isSide = side === 'Left' || side === 'Right'; if (isSide) { if (radius > 0) findings.push({ id: 'side-tab', snippet: `border-${sn}: ${w}px + border-radius: ${radius}px` }); else if (w >= 3) findings.push({ id: 'side-tab', snippet: `border-${sn}: ${w}px` }); } else { if (radius > 0 && w >= 2) findings.push({ id: 'border-accent-on-rounded', snippet: `border-${sn}: ${w}px + border-radius: ${radius}px` }); } } return findings; } // Returns true if the given text is composed entirely of emoji characters // (plus whitespace / variation selectors). Emojis render as multicolor glyphs // regardless of CSS `color`, so contrast checks against the element's text // color are meaningless for these nodes. const EMOJI_CHAR_RE = /[\u{1F1E6}-\u{1F1FF}\u{1F300}-\u{1F9FF}\u{1FA00}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}\u{FE0F}\u{200D}\u{1F3FB}-\u{1F3FF}]/u; const EMOJI_CHARS_GLOBAL = /[\u{1F1E6}-\u{1F1FF}\u{1F300}-\u{1F9FF}\u{1FA00}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}\u{FE0F}\u{200D}\u{1F3FB}-\u{1F3FF}]/gu; function isEmojiOnlyText(text) { if (!text) return false; if (!EMOJI_CHAR_RE.test(text)) return false; return text.replace(EMOJI_CHARS_GLOBAL, '').trim() === ''; } function checkColors(opts) { const { tag, textColor, bgColor, effectiveBg, effectiveBgStops, fontSize, fontWeight, hasDirectText, isEmojiOnly, bgClip, bgImage, classList } = opts; if (SAFE_TAGS.has(tag)) { // Exception for and