# Contrast Detection Fixes ## Problems The contrast checking tool had three issues that caused false positives and incorrect reporting: ### Issue 1: White-on-White False Positives **Example:** Vision Leadership sponsor page (https://visionleadership.org/sponsor-opportunities/) - **Reported:** White text `rgb(255, 255, 255)` on white background `rgb(255, 255, 255)` = 1:1 ratio (FAIL) - **Reality:** White text on gradient background `linear-gradient(rgb(12, 90, 201) 0%, rgb(0, 0, 0) 100%)` - **Actual Contrast:** 6.32:1 to 21:1 (PASS) **Root Cause:** The gradient was on a parent container (`.et_pb_section`), not directly on the text elements. The tool only checked the element itself for gradients, not parent elements. ### Issue 2: Gradient Applied to Wrong Elements After the initial fix, the tool became too aggressive: - **Reported:** 54 elements with blue background `rgb(12, 90, 201)` (from gradient) - **Reality:** Those elements were on light gray `rgb(252, 252, 252)` background - **Root Cause:** Gradient detection walked up the tree but didn't stop when it encountered solid backgrounds ### Issue 3: Wrong Text Color for Elements with Child Spans The tool used parent element colors instead of actual visible text colors: - **Example:** `314-560-7171
` - **Reported:** Used the ``'s color (blue rgb(12, 113, 195)) = 4.17:1 contrast - **Reality:** The visible text uses the ``'s color (white rgb(255, 255, 255)) = 21.00:1 contrast - **Root Cause:** Tool only checked the matched element's color, not descendant elements that contain the actual text ## Solutions Modified the contrast detection logic in `daemon/daemon.go` to: 1. **Walk up the DOM tree** to find gradient backgrounds on parent elements 2. **Stop at solid backgrounds** - if a solid background color is found before a gradient, use the solid color 3. **Only apply gradients** when no solid background exists between the element and the gradient 4. **Check child elements for text color** - if an element has visible child elements with text, use the first child's color instead of the parent's color ### Code Changes #### 1. Enhanced `analyzeGradientContrast` Function (Lines 9152-9220) **Before:** Only checked the element itself for gradients **After:** - Walks up the DOM tree looking for gradients - Checks for solid backgrounds at each level - Returns `null` if a solid background is found (gradient doesn't apply) - Only returns gradient info if no solid background blocks it ```javascript function analyzeGradientContrast(element, textColor) { let current = element; while (current && current !== document.documentElement.parentElement) { const style = window.getComputedStyle(current); // Check for solid background first - if found, gradient doesn't apply const bgColor = style.backgroundColor; if (!isTransparent(bgColor)) { return null; // Solid background found } // Check for gradient const bgImage = style.backgroundImage; if (bgImage && bgImage.includes('gradient')) { // Extract and analyze gradient colors... return gradientInfo; } current = current.parentElement; } return null; } ``` #### 2. Updated Background Detection Logic (Lines 9305-9340) **Before:** Always checked for gradients, even when solid backgrounds were found **After:** - Tier 1: Check for solid backgrounds via DOM tree walking - Tier 2: Check for solid backgrounds via stacking context - Tier 3: Only check for gradients if no solid background found - Fallback: Use white if nothing found ```javascript // Tier 1: DOM tree walking for solid colors bgColor = getEffectiveBackgroundDOMTree(element); // Tier 2: Stacking context analysis (if Tier 1 failed) if (!bgColor) { bgColor = getBackgroundFromStackingContext(element); } // Tier 3: Check for gradients (only if no solid color found) if (!bgColor) { gradientInfo = analyzeGradientContrast(element, fgColor); if (gradientInfo) { bgColor = gradientInfo.colors[0]; } } // Fallback to white if (!bgColor) { bgColor = 'rgb(255, 255, 255)'; } ``` ## Testing After deploying the fix, test on: 1. **Vision Leadership sponsor page** - Should correctly detect gradient and report passing contrast 2. **Elements with solid backgrounds** - Should not incorrectly apply gradients from parent containers 3. **Nested backgrounds** - Should use the closest background (solid or gradient) ## Expected Results ### Vision Leadership Hero Section - **H1 "Sponsorship Opportunities That Work For You"** - Detection method: `gradient-analysis` - Gradient colors: `rgb(12, 90, 201)` to `rgb(0, 0, 0)` - Worst contrast: 6.32:1 (PASS AA for large text) - Best contrast: 21:1 (PASS AAA) ### Elements with Solid Backgrounds - **Text on light gray `rgb(252, 252, 252)`** - Detection method: `dom-tree` or `stacking-context` - Should NOT use gradient from parent - Should calculate contrast against actual gray background ## Deployment 1. Build the daemon: `make daemon` 2. Build the MCP server: `make mcp` 3. Restart the cremote daemon (deployment-specific process) 4. Restart the MCP server or Augment extension to pick up changes #### 3. Enhanced Text Color Detection (Lines 9301-9354) **Before:** Always used the matched element's text color **After:** - Recursively searches all descendant elements to find the one with the most text content - Uses that element's color for contrast checking - Handles complex structures like `text
` - Skips invisible elements (display: none, visibility: hidden) ```javascript // Recursively find the element with the most text content function findTextBearingElement(el) { const descendants = el.querySelectorAll('*'); let bestElement = el; let maxTextLength = 0; // Check the element itself first const directText = Array.from(el.childNodes) .filter(node => node.nodeType === Node.TEXT_NODE) .map(node => node.textContent.trim()) .join('').length; if (directText > maxTextLength) { maxTextLength = directText; bestElement = el; } // Check all descendants for (const desc of descendants) { const descStyle = window.getComputedStyle(desc); if (descStyle.display === 'none' || descStyle.visibility === 'hidden') { continue; } const descText = Array.from(desc.childNodes) .filter(node => node.nodeType === Node.TEXT_NODE) .map(node => node.textContent.trim()) .join('').length; if (descText > maxTextLength) { maxTextLength = descText; bestElement = desc; } } return bestElement; } // Find the element with the most text content textElement = findTextBearingElement(element); textStyle = window.getComputedStyle(textElement); const fgColor = textStyle.color; ``` ## Files Modified - `daemon/daemon.go` - Lines 9152-9220 (gradient analysis function) - `daemon/daemon.go` - Lines 9305-9340 (background detection logic) - `daemon/daemon.go` - Lines 9301-9354 (text color detection enhancement with recursive search)