15 KiB
Feature Request: Enhanced Background Color Detection for Contrast Analysis
Date: December 8, 2025
Requested By: Shortcut Solutions (ADA Audit Team)
Priority: Medium
Estimated ROI: High (saves 1-2 hours per audit)
Problem Statement
The current web_contrast_check_cremotemcp_cremotemcp tool frequently fails to determine background colors when elements overlap or have transparent backgrounds. This results in:
- False "failures" that require manual verification
- Increased audit time (1-2 hours per site for manual verification)
- Reduced confidence in automated results
- Poor client experience (reports show "requires manual verification" instead of definitive results)
Real-World Example
During the visionleadership.org audit (December 8, 2025):
- 78 elements flagged for contrast issues (27% of 289 elements)
- Actual issues: Likely 0-5 (most are false positives)
- Root cause: Tool couldn't determine background color due to:
- Overlapping header elements
- Transparent navigation backgrounds
- Gradient backgrounds on sliders
- Complex z-index stacking contexts
Current Behavior
// Current approach (simplified)
const bgColor = window.getComputedStyle(element).backgroundColor;
// Problem: Returns 'transparent' or 'rgba(0, 0, 0, 0)' for many elements
// Result: Cannot calculate contrast ratio → flagged as "requires verification"
Proposed Solution
Implement a three-tier background detection algorithm with progressive fallback:
Tier 1: Enhanced DOM Tree Walking (Priority 1)
Effort: Low (2-4 hours)
Impact: Solves 80% of cases
Walk up the DOM tree to find the first non-transparent background:
function getEffectiveBackgroundColor(element) {
let current = element;
let bgColor = window.getComputedStyle(current).backgroundColor;
// Walk up DOM tree until we find a non-transparent background
while (current && (bgColor === 'transparent' || bgColor === 'rgba(0, 0, 0, 0)')) {
current = current.parentElement;
if (current) {
bgColor = window.getComputedStyle(current).backgroundColor;
}
}
// If still transparent, default to white (common body background)
if (bgColor === 'transparent' || bgColor === 'rgba(0, 0, 0, 0)') {
bgColor = 'rgb(255, 255, 255)';
}
return bgColor;
}
Tier 2: Element Stacking Context Analysis (Priority 2)
Effort: Medium (4-8 hours)
Impact: Solves 95% of cases
Use document.elementsFromPoint() to find what's actually behind the text:
function getBackgroundFromStackingContext(element) {
const rect = element.getBoundingClientRect();
// Sample multiple points (center, corners) for accuracy
const samplePoints = [
{ x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }, // center
{ x: rect.left + rect.width * 0.25, y: rect.top + rect.height * 0.25 }, // top-left
{ x: rect.left + rect.width * 0.75, y: rect.top + rect.height * 0.75 } // bottom-right
];
const backgrounds = [];
for (const point of samplePoints) {
// Get all elements at this point (in z-index order)
const elementsAtPoint = document.elementsFromPoint(point.x, point.y);
// Find the first element with a non-transparent background
for (const el of elementsAtPoint) {
if (el === element) continue; // Skip the text element itself
const bgColor = window.getComputedStyle(el).backgroundColor;
if (bgColor !== 'transparent' && bgColor !== 'rgba(0, 0, 0, 0)') {
backgrounds.push(bgColor);
break;
}
}
}
// Return the most common background color found
return getMostCommonColor(backgrounds) || 'rgb(255, 255, 255)';
}
Tier 3: Gradient and Complex Background Handling (Priority 3)
Effort: Medium-High (8-12 hours)
Impact: Solves 99% of cases
For gradient backgrounds, calculate worst-case contrast:
function analyzeGradientContrast(element) {
const bgImage = window.getComputedStyle(element).backgroundImage;
// Check if background is a gradient
if (bgImage.includes('gradient')) {
// Extract gradient colors using regex
const colors = extractGradientColors(bgImage);
// Calculate contrast against each color in the gradient
const textColor = window.getComputedStyle(element).color;
const contrastRatios = colors.map(bgColor =>
calculateContrastRatio(textColor, bgColor)
);
// Return worst-case (minimum) contrast ratio
return {
worstCase: Math.min(...contrastRatios),
bestCase: Math.max(...contrastRatios),
isGradient: true,
colors: colors
};
}
return null; // Not a gradient
}
function extractGradientColors(gradientString) {
// Parse gradient string to extract color stops
// Example: "linear-gradient(90deg, rgb(255,0,0) 0%, rgb(0,0,255) 100%)"
const colorRegex = /rgba?\([^)]+\)|#[0-9a-f]{3,6}/gi;
return gradientString.match(colorRegex) || [];
}
Implementation Plan
Phase 1: Core Enhancement (Week 1)
Effort: 2-4 hours
- Implement Tier 1 (DOM tree walking)
- Update
web_contrast_check_cremotemcp_cremotemcpto use new function - Add fallback to existing method if new method fails
- Test on 5-10 real-world sites
Deliverables:
- Updated contrast checking function
- Unit tests for DOM tree walking
- Documentation of new behavior
Phase 2: Stacking Context Analysis (Week 2)
Effort: 4-8 hours
- Implement Tier 2 (elementsFromPoint)
- Add multi-point sampling for accuracy
- Handle edge cases (fixed positioning, transforms)
- Test on complex layouts
Deliverables:
- Stacking context analysis function
- Integration tests with overlapping elements
- Performance benchmarks
Phase 3: Gradient Support (Week 3-4)
Effort: 8-12 hours
- Implement Tier 3 (gradient parsing)
- Add worst-case contrast calculation
- Handle CSS gradients, background images
- Test on sites with complex backgrounds
Deliverables:
- Gradient analysis function
- Comprehensive test suite
- Documentation with examples
Phase 4: Integration and Testing (Week 4)
Effort: 4-6 hours
- Integrate all three tiers with progressive fallback
- Add configuration options (enable/disable tiers)
- Performance optimization
- Documentation and examples
Deliverables:
- Complete integrated solution
- Performance benchmarks
- User documentation
- Migration guide
Technical Specifications
New Function Signature
/**
* Enhanced contrast checking with improved background detection
* @param {string} selector - CSS selector for elements to check
* @param {Object} options - Configuration options
* @param {boolean} options.useStackingContext - Enable Tier 2 (default: true)
* @param {boolean} options.analyzeGradients - Enable Tier 3 (default: true)
* @param {number} options.samplePoints - Number of points to sample (default: 3)
* @param {string} options.fallbackBg - Fallback background color (default: 'rgb(255, 255, 255)')
* @returns {Object} Enhanced contrast analysis results
*/
async function enhancedContrastCheck(selector, options = {}) {
const elements = document.querySelectorAll(selector);
const results = [];
for (const element of elements) {
const textColor = window.getComputedStyle(element).color;
let bgColor = null;
let method = null;
// Tier 1: DOM tree walking
bgColor = getEffectiveBackgroundColor(element);
method = 'dom-tree';
// Tier 2: Stacking context (if enabled and Tier 1 returned transparent)
if (options.useStackingContext && (bgColor === 'transparent' || bgColor === 'rgba(0, 0, 0, 0)')) {
bgColor = getBackgroundFromStackingContext(element);
method = 'stacking-context';
}
// Tier 3: Gradient analysis (if enabled)
let gradientInfo = null;
if (options.analyzeGradients) {
gradientInfo = analyzeGradientContrast(element);
if (gradientInfo) {
method = 'gradient-analysis';
}
}
// Calculate contrast ratio
const contrastRatio = gradientInfo
? gradientInfo.worstCase
: calculateContrastRatio(textColor, bgColor);
results.push({
element: element,
selector: getElementSelector(element),
textColor: textColor,
backgroundColor: bgColor,
contrastRatio: contrastRatio,
detectionMethod: method,
gradientInfo: gradientInfo,
passes: {
AA: contrastRatio >= 4.5,
AAA: contrastRatio >= 7.0,
AALarge: contrastRatio >= 3.0,
AAALarge: contrastRatio >= 4.5
}
});
}
return results;
}
Return Value Structure
{
element: HTMLElement,
selector: "nav > ul > li > a",
textColor: "rgb(51, 51, 51)",
backgroundColor: "rgb(255, 255, 255)",
contrastRatio: 12.63,
detectionMethod: "stacking-context", // or "dom-tree", "gradient-analysis"
gradientInfo: null, // or { worstCase, bestCase, isGradient, colors }
passes: {
AA: true,
AAA: true,
AALarge: true,
AAALarge: true
}
}
Expected Benefits
Quantitative Benefits
- Reduced False Positives: 80-95% reduction in "requires manual verification" flags
- Time Savings: 1-2 hours per audit (manual verification eliminated)
- Accuracy Improvement: 95%+ accuracy vs current ~70%
- Client Satisfaction: Fewer ambiguous results in reports
Qualitative Benefits
- Increased Confidence: Clients trust automated results more
- Better Reports: Definitive pass/fail instead of "requires verification"
- Competitive Advantage: More accurate than other automated tools
- Reduced Support: Fewer questions about flagged items
Cost-Benefit Analysis
Development Cost: 18-30 hours ($1,800-$3,000 at $100/hour)
Time Savings: 1.5 hours per audit × 50 audits/year = 75 hours/year ($7,500/year)
ROI: 150-250% in first year
Payback Period: 3-4 months
Testing Strategy
Unit Tests
describe('Enhanced Background Detection', () => {
test('should find background from parent element', () => {
// Test DOM tree walking
});
test('should detect background from overlapping element', () => {
// Test stacking context analysis
});
test('should calculate worst-case contrast for gradients', () => {
// Test gradient analysis
});
test('should fallback gracefully when detection fails', () => {
// Test fallback behavior
});
});
Integration Tests
- Simple Layouts: Text on solid backgrounds
- Overlapping Elements: Navigation menus, headers
- Transparent Backgrounds: Inherited backgrounds
- Gradient Backgrounds: Linear and radial gradients
- Complex Stacking: Multiple z-index layers
- Fixed/Absolute Positioning: Overlays, modals
Real-World Testing
Test on 10-20 production websites including:
- E-commerce sites (complex headers)
- News sites (overlapping content)
- Corporate sites (gradient backgrounds)
- Educational sites (accessibility plugins)
Backwards Compatibility
Migration Strategy
- Default Behavior: New algorithm enabled by default
- Opt-Out Option: Add
legacy_mode: trueto use old algorithm - Gradual Rollout: Test on internal audits first, then production
- Comparison Mode: Run both algorithms and compare results
Configuration Options
// Enable all enhancements (recommended)
web_contrast_check_cremotemcp_cremotemcp({
selector: 'body *',
enhanced_detection: true,
use_stacking_context: true,
analyze_gradients: true
});
// Legacy mode (old behavior)
web_contrast_check_cremotemcp_cremotemcp({
selector: 'body *',
legacy_mode: true
});
// Custom configuration
web_contrast_check_cremotemcp_cremotemcp({
selector: 'body *',
enhanced_detection: true,
use_stacking_context: true,
analyze_gradients: false, // Skip gradient analysis for speed
sample_points: 5,
fallback_bg: 'rgb(255, 255, 255)'
});
Performance Considerations
Current Performance
- Time per element: ~5-10ms
- 289 elements: ~1.5-3 seconds
Expected Performance with Enhancements
- Tier 1 (DOM tree): +1-2ms per element
- Tier 2 (stacking context): +3-5ms per element
- Tier 3 (gradient): +5-10ms per element (only when gradients detected)
Total: ~2-5 seconds for 289 elements (acceptable)
Optimization Strategies
- Lazy Evaluation: Only use Tier 2/3 when Tier 1 fails
- Caching: Cache computed styles for repeated elements
- Parallel Processing: Process elements in batches
- Early Exit: Stop at first successful detection method
Documentation Requirements
User Documentation
- Feature Overview: What's new and why it matters
- Configuration Guide: How to enable/disable features
- Examples: Before/after comparisons
- Troubleshooting: Common issues and solutions
Developer Documentation
- Architecture: How the three tiers work
- API Reference: Function signatures and parameters
- Testing Guide: How to test the new features
- Performance Guide: Optimization tips
Success Metrics
Acceptance Criteria
- Reduces false positives by 80%+ on test suite
- Maintains or improves performance (< 5 seconds for 300 elements)
- Passes all unit and integration tests
- Successfully tested on 10+ real-world sites
- Documentation complete and reviewed
- Backwards compatible with legacy mode
Post-Launch Monitoring
- Accuracy Tracking: Compare automated vs manual verification results
- Performance Monitoring: Track execution time per audit
- User Feedback: Collect feedback from audit team
- Error Rates: Monitor detection failures and edge cases
Risks and Mitigations
Risk 1: Performance Degradation
Likelihood: Low
Impact: Medium
Mitigation: Implement lazy evaluation, caching, and performance benchmarks
Risk 2: New False Positives
Likelihood: Low
Impact: Medium
Mitigation: Extensive testing, gradual rollout, legacy mode fallback
Risk 3: Browser Compatibility
Likelihood: Low
Impact: Low
Mitigation: Test on Chrome, Firefox, Safari; use polyfills if needed
Risk 4: Complex Edge Cases
Likelihood: Medium
Impact: Low
Mitigation: Progressive fallback, comprehensive test suite, manual verification option
Future Enhancements
Phase 5: Machine Learning (Future)
- Train ML model on manually verified results
- Predict contrast issues with higher accuracy
- Learn from false positives/negatives
Phase 6: Screenshot-Based Analysis (Future)
- Capture actual rendered pixels
- Use image processing for perfect accuracy
- Handle all edge cases (animations, transforms, etc.)
Phase 7: Real-Time Validation (Future)
- Integrate with browser DevTools
- Provide live feedback during development
- Suggest color adjustments for compliance
Conclusion
This enhancement will significantly improve the accuracy and reliability of automated contrast checking, reducing manual verification time and increasing client confidence in audit results. The three-tier approach provides a good balance between accuracy, performance, and complexity.
Recommendation: Proceed with implementation starting with Phase 1 (Tier 1 DOM tree walking) as a quick win, then evaluate results before proceeding to Phases 2-3.
Feature Request Prepared By: Shortcut Solutions
Date: December 8, 2025
Status: Pending Review
Priority: Medium
Estimated ROI: High (150-250% first year)