mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-17 16:46:31 +03:00
detector: regenerate browser bundle after glow hardening
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
0d72991bc8
commit
d5af1112a4
@@ -209,9 +209,9 @@ const ANTIPATTERNS = [
|
||||
{
|
||||
id: 'dark-glow',
|
||||
category: 'slop',
|
||||
name: 'Dark mode with glowing accents',
|
||||
name: 'Glowing shadow accents',
|
||||
description:
|
||||
'Dark backgrounds with colored box-shadow glows are the default "cool" look of AI-generated UIs. Use subtle, purposeful lighting instead — or skip the dark theme entirely.',
|
||||
'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.',
|
||||
skillSection: 'Color & Contrast',
|
||||
skillGuideline: 'dark mode with glowing accents',
|
||||
},
|
||||
@@ -1082,36 +1082,155 @@ function checkMotion(opts) {
|
||||
return findings;
|
||||
}
|
||||
|
||||
// Locate the color token in a single shadow layer. Returns
|
||||
// { color, start, end } where color is the parsed {r,g,b,a} (null when the
|
||||
// token exists but can't be parsed — e.g. an unresolved var() or an exotic
|
||||
// color space), or null when no color token is present at all. Handles both
|
||||
// serialization orders: computed style puts the color first
|
||||
// ("rgb(…) 0px 0px 20px"), authored CSS usually puts it last
|
||||
// ("0 0 20px #3b82f6").
|
||||
function findShadowColor(layer) {
|
||||
const fn = layer.match(/(?:rgba?|hsla?|hwb|oklch|oklab|lch|lab|color)\([^)]*\)/i);
|
||||
if (fn) return { color: parseAnyColor(fn[0]), start: fn.index, end: fn.index + fn[0].length };
|
||||
const hex = layer.match(/#[0-9a-fA-F]{3,8}\b/);
|
||||
if (hex) return { color: parseAnyColor(hex[0]), start: hex.index, end: hex.index + hex[0].length };
|
||||
const wordRe = /[a-zA-Z][a-zA-Z]*/g;
|
||||
let m;
|
||||
while ((m = wordRe.exec(layer)) !== null) {
|
||||
const named = CSS_NAMED_COLORS[m[0].toLowerCase()];
|
||||
if (named) return { color: { ...named, a: 1 }, start: m.index, end: m.index + m[0].length };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Extract the length values of a shadow layer in declaration order, with the
|
||||
// color token removed so its components aren't misread as lengths. Handles
|
||||
// computed-style px values AND authored unitless zeros ("0 0 20px"); rem/em
|
||||
// approximate at 16px. Result order is offset-x, offset-y, blur, [spread].
|
||||
function extractShadowLengths(layer, colorStart, colorEnd) {
|
||||
const stripped = colorStart != null
|
||||
? layer.slice(0, colorStart) + ' ' + layer.slice(colorEnd)
|
||||
: layer;
|
||||
const vals = [];
|
||||
const re = /(-?\d*\.?\d+)(px|rem|em)?/g;
|
||||
let m;
|
||||
while ((m = re.exec(stripped)) !== null) {
|
||||
let v = parseFloat(m[1]);
|
||||
if (m[2] === 'rem' || m[2] === 'em') v *= 16;
|
||||
vals.push(v);
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
function checkGlow(opts) {
|
||||
const { boxShadow, effectiveBg } = opts;
|
||||
if (!boxShadow || boxShadow === 'none') return [];
|
||||
if (!effectiveBg) return [];
|
||||
const { boxShadow, textShadow, effectiveBg } = opts;
|
||||
const onDarkBg = effectiveBg ? relativeLuminance(effectiveBg) < 0.1 : false;
|
||||
|
||||
// Only flag on dark backgrounds (luminance < 0.1)
|
||||
const bgLum = relativeLuminance(effectiveBg);
|
||||
if (bgLum >= 0.1) return [];
|
||||
// Scan one shadow list. Two glow tells, in any color format:
|
||||
// 1. Zero-offset chromatic halo (0 0 Npx <color>) — slop on ANY
|
||||
// background; the light radiates evenly outward, which is never how
|
||||
// real elevation shadows behave. Achromatic zero-offset shadows stay
|
||||
// legal (soft ambient elevation), as do focus rings (blur 0).
|
||||
// 2. Any chromatic shadow with real blur on a dark background — the
|
||||
// classic dark-mode glow accent.
|
||||
const scan = (value, prop) => {
|
||||
if (!value || value === 'none') return null;
|
||||
// Split multiple shadows (commas not inside parentheses)
|
||||
for (const layer of value.split(/,(?![^(]*\))/)) {
|
||||
const colorInfo = findShadowColor(layer);
|
||||
// No color token, or one we can't resolve (unresolved var(), exotic
|
||||
// color space): don't guess — skip rather than false-positive.
|
||||
if (!colorInfo || !colorInfo.color) continue;
|
||||
const color = colorInfo.color;
|
||||
if (!hasChroma(color, 30)) continue;
|
||||
const vals = extractShadowLengths(layer, colorInfo.start, colorInfo.end);
|
||||
// Third value is blur (offset-x, offset-y, blur, [spread])
|
||||
if (vals.length < 3 || vals[2] <= 4) continue;
|
||||
if (vals[0] === 0 && vals[1] === 0) {
|
||||
return { id: 'dark-glow', snippet: `Zero-offset ${prop} glow (${colorToHex(color)})` };
|
||||
}
|
||||
if (onDarkBg) {
|
||||
return { id: 'dark-glow', snippet: `Colored ${prop} glow (${colorToHex(color)}) on dark background` };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Split multiple shadows (commas not inside parentheses)
|
||||
const parts = boxShadow.split(/,(?![^(]*\))/);
|
||||
for (const shadow of parts) {
|
||||
const colorMatch = shadow.match(/rgba?\([^)]+\)/);
|
||||
if (!colorMatch) continue;
|
||||
const color = parseRgb(colorMatch[0]);
|
||||
if (!color || !hasChroma(color, 30)) continue;
|
||||
const found = scan(boxShadow, 'box-shadow') || scan(textShadow, 'text-shadow');
|
||||
return found ? [found] : [];
|
||||
}
|
||||
|
||||
// Extract px values — in computed style: "color Xpx Ypx BLURpx [SPREADpx]"
|
||||
const afterColor = shadow.substring(shadow.indexOf(colorMatch[0]) + colorMatch[0].length);
|
||||
const beforeColor = shadow.substring(0, shadow.indexOf(colorMatch[0]));
|
||||
const pxVals = [...beforeColor.matchAll(/([\d.]+)px/g), ...afterColor.matchAll(/([\d.]+)px/g)]
|
||||
.map(m => parseFloat(m[1]));
|
||||
// Collect CSS custom property declarations from raw stylesheet/HTML text.
|
||||
// First declaration wins (:root declarations usually come first); good
|
||||
// enough for the single-level var() resolution the text engines need.
|
||||
function collectCssCustomProps(content) {
|
||||
const map = new Map();
|
||||
const re = /(--[\w-]+)\s*:\s*([^;{}]+)/g;
|
||||
let m;
|
||||
while ((m = re.exec(content)) !== null) {
|
||||
if (!map.has(m[1])) map.set(m[1], m[2].trim());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// Third value is blur (offset-x, offset-y, blur, [spread])
|
||||
if (pxVals.length >= 3 && pxVals[2] > 4) {
|
||||
return [{ id: 'dark-glow', snippet: `Colored glow (${colorToHex(color)}) on dark background` }];
|
||||
// Text-level glow scan shared by the regex engine and the page-level HTML
|
||||
// pattern pass. Resolves single-level var() refs against custom properties
|
||||
// collected from the same text, then applies the same two glow tells as
|
||||
// checkGlow: zero-offset chromatic halo (any background) and chromatic
|
||||
// blurred shadow when the page has a dark background. Returns
|
||||
// [{ index, snippet }] — index is the offset of the shadow declaration.
|
||||
function scanCssTextForGlow(content) {
|
||||
const customProps = collectCssCustomProps(content);
|
||||
|
||||
// Dark-page heuristic: dark hex/rgb literals, Tailwind dark bg utilities,
|
||||
// or a ROOT-scoped (body/html/:root or <body style>) background that
|
||||
// resolves — via var() — to a dark color. The var/modern-color extension
|
||||
// is deliberately root-scoped: a light page with one dark accent chip
|
||||
// must not turn every tinted drop shadow into a "dark page" glow.
|
||||
const darkBgRe = /background(?:-color)?\s*:\s*(?:#(?:0[0-9a-f]|1[0-9a-f]|2[0-3])[0-9a-f]{4}\b|#(?:0|1)[0-9a-f]{2}\b|rgb\(\s*(\d{1,2})\s*,\s*(\d{1,2})\s*,\s*(\d{1,2})\s*\))/i;
|
||||
const twDarkBg = /\bbg-(?:gray|slate|zinc|neutral|stone)-(?:9\d{2}|800)\b/;
|
||||
let hasDarkBg = darkBgRe.test(content) || twDarkBg.test(content);
|
||||
if (!hasDarkBg) {
|
||||
const rootScopes = [];
|
||||
const blockRe = /(?:^|[}\s,;>])(?:body|html|:root)\s*(?:,[^{]*)?\{([^}]*)\}/gi;
|
||||
let sm;
|
||||
while ((sm = blockRe.exec(content)) !== null) rootScopes.push(sm[1]);
|
||||
const inlineBody = content.match(/<body[^>]*\bstyle\s*=\s*"([^"]*)"/i);
|
||||
if (inlineBody) rootScopes.push(inlineBody[1]);
|
||||
for (const scope of rootScopes) {
|
||||
const bgRe = /background(?:-color)?\s*:\s*([^;{}]+)/gi;
|
||||
let bm;
|
||||
while (!hasDarkBg && (bm = bgRe.exec(scope)) !== null) {
|
||||
const c = parseAnyColor(resolveVarRefs(bm[1].trim(), customProps));
|
||||
if (c && (c.a ?? 1) > 0.5 && relativeLuminance(c) < 0.1) hasDarkBg = true;
|
||||
}
|
||||
if (hasDarkBg) break;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
const results = [];
|
||||
const shadowRe = /\b(box-shadow|text-shadow)\s*:\s*([^;{}]+)/gi;
|
||||
let m;
|
||||
while ((m = shadowRe.exec(content)) !== null) {
|
||||
const prop = m[1].toLowerCase();
|
||||
const value = resolveVarRefs(m[2].trim(), customProps);
|
||||
for (const layer of value.split(/,(?![^(]*\))/)) {
|
||||
const colorInfo = findShadowColor(layer);
|
||||
if (!colorInfo || !colorInfo.color || !hasChroma(colorInfo.color, 30)) continue;
|
||||
const vals = extractShadowLengths(layer, colorInfo.start, colorInfo.end);
|
||||
if (vals.length < 3 || vals[2] <= 4) continue;
|
||||
const zeroOffset = vals[0] === 0 && vals[1] === 0;
|
||||
if (!zeroOffset && !hasDarkBg) continue;
|
||||
results.push({
|
||||
index: m.index,
|
||||
snippet: zeroOffset
|
||||
? `Zero-offset ${prop} glow (${colorToHex(colorInfo.color)})`
|
||||
: `Colored ${prop} glow (${colorToHex(colorInfo.color)}) on dark page`,
|
||||
});
|
||||
break; // one finding per declaration
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1222,25 +1341,11 @@ function checkHtmlPatterns(html) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Dark glow ---
|
||||
// --- Dark glow / chromatic halo shadows ---
|
||||
|
||||
const darkBgRe = /background(?:-color)?\s*:\s*(?:#(?:0[0-9a-f]|1[0-9a-f]|2[0-3])[0-9a-f]{4}\b|#(?:0|1)[0-9a-f]{2}\b|rgb\(\s*(\d{1,2})\s*,\s*(\d{1,2})\s*,\s*(\d{1,2})\s*\))/gi;
|
||||
const twDarkBg = /\bbg-(?:gray|slate|zinc|neutral|stone)-(?:9\d{2}|800)\b/;
|
||||
if (darkBgRe.test(html) || twDarkBg.test(html)) {
|
||||
const shadowRe = /box-shadow\s*:\s*([^;{}]+)/gi;
|
||||
let shm;
|
||||
while ((shm = shadowRe.exec(html)) !== null) {
|
||||
const val = shm[1];
|
||||
const colorMatch = val.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
|
||||
if (!colorMatch) continue;
|
||||
const [r, g, b] = [+colorMatch[1], +colorMatch[2], +colorMatch[3]];
|
||||
if ((Math.max(r, g, b) - Math.min(r, g, b)) < 30) continue;
|
||||
const pxVals = [...val.matchAll(/(\d+)px|(?<![.\d])\b(0)\b(?![.\d])/g)].map(p => +(p[1] || p[2]));
|
||||
if (pxVals.length >= 3 && pxVals[2] > 4) {
|
||||
findings.push({ id: 'dark-glow', snippet: `Colored glow (rgb(${r},${g},${b})) on dark page` });
|
||||
break;
|
||||
}
|
||||
}
|
||||
const glowHits = scanCssTextForGlow(html);
|
||||
if (glowHits.length > 0) {
|
||||
findings.push({ id: 'dark-glow', snippet: glowHits[0].snippet });
|
||||
}
|
||||
|
||||
// --- Provider tells (gated): repeating-gradient stripes (GPT) ---
|
||||
@@ -1635,8 +1740,10 @@ function resolveVarRefs(raw, customPropMap, depth = 0) {
|
||||
// detector's contrast / color checks.
|
||||
function oklchToRgb(L, C, H) {
|
||||
const hRad = (H * Math.PI) / 180;
|
||||
const a = C * Math.cos(hRad);
|
||||
const b = C * Math.sin(hRad);
|
||||
return oklabToRgb(L, C * Math.cos(hRad), C * Math.sin(hRad));
|
||||
}
|
||||
|
||||
function oklabToRgb(L, a, b) {
|
||||
const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
|
||||
const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
|
||||
const s_ = L - 0.0894841775 * a - 1.2914855480 * b;
|
||||
@@ -1656,9 +1763,85 @@ function oklchToRgb(L, C, H) {
|
||||
};
|
||||
}
|
||||
|
||||
// Extended color parser: rgb/rgba/hex/oklch. Returns null on no match.
|
||||
// Use this when the input might be any CSS color form; use plain parseRgb
|
||||
// when you only expect computed rgb() values from real browsers.
|
||||
function hslToRgb(h, s, l) {
|
||||
h = ((h % 360) + 360) % 360;
|
||||
const c = (1 - Math.abs(2 * l - 1)) * s;
|
||||
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
||||
const m0 = l - c / 2;
|
||||
const [r, g, b] =
|
||||
h < 60 ? [c, x, 0] :
|
||||
h < 120 ? [x, c, 0] :
|
||||
h < 180 ? [0, c, x] :
|
||||
h < 240 ? [0, x, c] :
|
||||
h < 300 ? [x, 0, c] : [c, 0, x];
|
||||
return {
|
||||
r: Math.round((r + m0) * 255),
|
||||
g: Math.round((g + m0) * 255),
|
||||
b: Math.round((b + m0) * 255),
|
||||
a: 1,
|
||||
};
|
||||
}
|
||||
|
||||
function hwbToRgb(h, w, bl) {
|
||||
if (w + bl >= 1) {
|
||||
const g = Math.round((w / (w + bl)) * 255);
|
||||
return { r: g, g, b: g, a: 1 };
|
||||
}
|
||||
const base = hslToRgb(h, 1, 0.5);
|
||||
const mix = (c) => Math.round(((c / 255) * (1 - w - bl) + w) * 255);
|
||||
return { r: mix(base.r), g: mix(base.g), b: mix(base.b), a: 1 };
|
||||
}
|
||||
|
||||
// Common CSS named colors — the handful that actually show up in generated
|
||||
// UIs, not the full 148-name spec list. Includes the achromatic names so a
|
||||
// named gray parses (and correctly reads as no-chroma) instead of being
|
||||
// treated as an unknown color.
|
||||
const CSS_NAMED_COLORS = {
|
||||
black: { r: 0, g: 0, b: 0 },
|
||||
white: { r: 255, g: 255, b: 255 },
|
||||
gray: { r: 128, g: 128, b: 128 },
|
||||
grey: { r: 128, g: 128, b: 128 },
|
||||
silver: { r: 192, g: 192, b: 192 },
|
||||
dimgray: { r: 105, g: 105, b: 105 },
|
||||
darkgray: { r: 169, g: 169, b: 169 },
|
||||
lightgray: { r: 211, g: 211, b: 211 },
|
||||
gainsboro: { r: 220, g: 220, b: 220 },
|
||||
whitesmoke: { r: 245, g: 245, b: 245 },
|
||||
red: { r: 255, g: 0, b: 0 },
|
||||
crimson: { r: 220, g: 20, b: 60 },
|
||||
tomato: { r: 255, g: 99, b: 71 },
|
||||
coral: { r: 255, g: 127, b: 80 },
|
||||
salmon: { r: 250, g: 128, b: 114 },
|
||||
orange: { r: 255, g: 165, b: 0 },
|
||||
gold: { r: 255, g: 215, b: 0 },
|
||||
yellow: { r: 255, g: 255, b: 0 },
|
||||
olive: { r: 128, g: 128, b: 0 },
|
||||
lime: { r: 0, g: 255, b: 0 },
|
||||
green: { r: 0, g: 128, b: 0 },
|
||||
teal: { r: 0, g: 128, b: 128 },
|
||||
turquoise: { r: 64, g: 224, b: 208 },
|
||||
cyan: { r: 0, g: 255, b: 255 },
|
||||
aqua: { r: 0, g: 255, b: 255 },
|
||||
skyblue: { r: 135, g: 206, b: 235 },
|
||||
dodgerblue: { r: 30, g: 144, b: 255 },
|
||||
blue: { r: 0, g: 0, b: 255 },
|
||||
navy: { r: 0, g: 0, b: 128 },
|
||||
indigo: { r: 75, g: 0, b: 130 },
|
||||
rebeccapurple: { r: 102, g: 51, b: 153 },
|
||||
purple: { r: 128, g: 0, b: 128 },
|
||||
violet: { r: 238, g: 130, b: 238 },
|
||||
orchid: { r: 218, g: 112, b: 214 },
|
||||
magenta: { r: 255, g: 0, b: 255 },
|
||||
fuchsia: { r: 255, g: 0, b: 255 },
|
||||
hotpink: { r: 255, g: 105, b: 180 },
|
||||
pink: { r: 255, g: 192, b: 203 },
|
||||
maroon: { r: 128, g: 0, b: 0 },
|
||||
};
|
||||
|
||||
// Extended color parser: rgb/rgba/hex/oklch/oklab/hsl/hwb/common named
|
||||
// colors. Returns null on no match. Use this when the input might be any
|
||||
// CSS color form; use plain parseRgb when you only expect computed rgb()
|
||||
// values from real browsers.
|
||||
function parseAnyColor(s) {
|
||||
if (!s || typeof s !== 'string') return null;
|
||||
const str = s.trim();
|
||||
@@ -1700,6 +1883,41 @@ function parseAnyColor(s) {
|
||||
}
|
||||
return rgb;
|
||||
}
|
||||
// OKLAB — a/b are signed axes; percentages map 100% → 0.4.
|
||||
m = str.match(/oklab\(\s*([\d.]+)(%?)\s+(-?[\d.]+)(%?)\s+(-?[\d.]+)(%?)(?:\s*\/\s*([\d.]+)(%)?)?\s*\)/i);
|
||||
if (m) {
|
||||
const L = m[2] === '%' ? parseFloat(m[1]) / 100 : parseFloat(m[1]);
|
||||
const a = m[4] === '%' ? parseFloat(m[3]) * 0.004 : parseFloat(m[3]);
|
||||
const b = m[6] === '%' ? parseFloat(m[5]) * 0.004 : parseFloat(m[5]);
|
||||
const rgb = oklabToRgb(L, a, b);
|
||||
if (m[7] !== undefined) {
|
||||
const alpha = parseFloat(m[7]);
|
||||
rgb.a = m[8] === '%' ? alpha / 100 : alpha;
|
||||
}
|
||||
return rgb;
|
||||
}
|
||||
// HSL/HSLA — comma or space syntax, optional deg on hue.
|
||||
m = str.match(/hsla?\(\s*(-?[\d.]+)(?:deg)?\s*[,\s]\s*([\d.]+)%\s*[,\s]\s*([\d.]+)%(?:\s*[,/]\s*([\d.]+)(%)?)?\s*\)/i);
|
||||
if (m) {
|
||||
const rgb = hslToRgb(parseFloat(m[1]), parseFloat(m[2]) / 100, parseFloat(m[3]) / 100);
|
||||
if (m[4] !== undefined) {
|
||||
const alpha = parseFloat(m[4]);
|
||||
rgb.a = m[5] === '%' ? alpha / 100 : alpha;
|
||||
}
|
||||
return rgb;
|
||||
}
|
||||
// HWB — hue whiteness% blackness%.
|
||||
m = str.match(/hwb\(\s*(-?[\d.]+)(?:deg)?\s+([\d.]+)%\s+([\d.]+)%(?:\s*\/\s*([\d.]+)(%)?)?\s*\)/i);
|
||||
if (m) {
|
||||
const rgb = hwbToRgb(parseFloat(m[1]), parseFloat(m[2]) / 100, parseFloat(m[3]) / 100);
|
||||
if (m[4] !== undefined) {
|
||||
const alpha = parseFloat(m[4]);
|
||||
rgb.a = m[5] === '%' ? alpha / 100 : alpha;
|
||||
}
|
||||
return rgb;
|
||||
}
|
||||
const named = CSS_NAMED_COLORS[str.toLowerCase()];
|
||||
if (named) return { ...named, a: 1 };
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1846,7 +2064,14 @@ function checkElementMotionDOM(el) {
|
||||
function checkElementGlowDOM(el) {
|
||||
const tag = el.tagName.toLowerCase();
|
||||
const style = getComputedStyle(el);
|
||||
if (!style.boxShadow || style.boxShadow === 'none') return [];
|
||||
const boxShadow = style.boxShadow && style.boxShadow !== 'none' ? style.boxShadow : '';
|
||||
// text-shadow inherits: only check the element that introduces it, so one
|
||||
// declaration doesn't produce a finding on every descendant.
|
||||
let textShadow = style.textShadow && style.textShadow !== 'none' ? style.textShadow : '';
|
||||
if (textShadow && el.parentElement && getComputedStyle(el.parentElement).textShadow === textShadow) {
|
||||
textShadow = '';
|
||||
}
|
||||
if (!boxShadow && !textShadow) return [];
|
||||
// Use parent's background — glow radiates outward, so the surrounding context matters
|
||||
// If resolveBackground returns null (gradient), try to infer from the gradient colors
|
||||
let parentBg = el.parentElement ? resolveBackground(el.parentElement) : resolveBackground(el);
|
||||
@@ -1869,7 +2094,7 @@ function checkElementGlowDOM(el) {
|
||||
cur = cur.parentElement;
|
||||
}
|
||||
}
|
||||
return checkGlow({ tag, boxShadow: style.boxShadow, effectiveBg: parentBg });
|
||||
return checkGlow({ tag, boxShadow, textShadow, effectiveBg: parentBg });
|
||||
}
|
||||
|
||||
function checkElementAIPaletteDOM(el) {
|
||||
@@ -2609,8 +2834,10 @@ function checkElementMotion(tag, style) {
|
||||
}
|
||||
|
||||
function checkElementGlow(tag, style, effectiveBg) {
|
||||
if (!style.boxShadow || style.boxShadow === 'none') return [];
|
||||
return checkGlow({ tag, boxShadow: style.boxShadow, effectiveBg });
|
||||
const boxShadow = style.boxShadow && style.boxShadow !== 'none' ? style.boxShadow : '';
|
||||
const textShadow = style.textShadow && style.textShadow !== 'none' ? style.textShadow : '';
|
||||
if (!boxShadow && !textShadow) return [];
|
||||
return checkGlow({ tag, boxShadow, textShadow, effectiveBg });
|
||||
}
|
||||
|
||||
// ─── Section 6: Page-Level Checks ───────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user