# 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)