Created
March 18, 2026 20:37
-
-
Save felquis/676fc02953daae26d14de63c5e06d7e5 to your computer and use it in GitHub Desktop.
Remove gradient backgrounds from any website
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| javascript:(function(){ | |
| // ──────────────────────────────────────────────── | |
| // Helpers | |
| // ──────────────────────────────────────────────── | |
| function hasGradient(value) { | |
| return /gradient|linear-gradient|radial-gradient|repeating-/i.test(value || ''); | |
| } | |
| function isGradientTextLike(style) { | |
| const bgClip = style.backgroundClip || style.webkitBackgroundClip; | |
| const textClip = bgClip === 'text' || bgClip === '-webkit-text'; | |
| const colorTransparent = style.color === 'transparent' || style.color === 'rgba(0,0,0,0)'; | |
| const hasBgGradient = hasGradient(style.backgroundImage); | |
| return textClip && colorTransparent && hasBgGradient; | |
| } | |
| function pickSolidColorFromGradient(str) { | |
| str = (str || '').toLowerCase().trim(); | |
| const colorRe = /#([0-9a-f]{3,8})|rgb\([^)]+\)|rgba\([^)]+\)/gi; | |
| let matches = [...str.matchAll(colorRe)]; | |
| if (matches.length === 0) return 'rgb(100,100,110)'; | |
| // Prefer first opaque color | |
| for (let m of matches) { | |
| let c = m[0]; | |
| if (c.startsWith('rgba')) { | |
| const nums = c.match(/[\d.]+/g); | |
| if (nums && nums.length >= 4 && parseFloat(nums[3]) > 0.25) { | |
| return `rgb(${nums[0]},${nums[1]},${nums[2]})`; | |
| } | |
| } else { | |
| return c; | |
| } | |
| } | |
| // Average first + last if multiple colors | |
| if (matches.length >= 2) { | |
| try { | |
| const c1 = parseColor(matches[0][0]); | |
| const c2 = parseColor(matches[matches.length - 1][0]); | |
| const avg = [ | |
| Math.round((c1[0] + c2[0]) / 2), | |
| Math.round((c1[1] + c2[1]) / 2), | |
| Math.round((c1[2] + c2[2]) / 2) | |
| ]; | |
| return `rgb(${avg.join(',')})`; | |
| } catch {} | |
| } | |
| return matches[0][0].startsWith('#') ? matches[0][0] : 'rgb(100,100,110)'; | |
| } | |
| function parseColor(c) { | |
| if (c.startsWith('#')) { | |
| let hex = c.slice(1); | |
| if (hex.length === 3) hex = hex.split('').map(x => x + x).join(''); | |
| return [ | |
| parseInt(hex.slice(0,2),16), | |
| parseInt(hex.slice(2,4),16), | |
| parseInt(hex.slice(4,6),16) | |
| ]; | |
| } | |
| const nums = c.match(/[\d.]+/g); | |
| return nums ? nums.slice(0,3).map(Number) : [100,100,110]; | |
| } | |
| function shouldUseWhiteText(bgRgb) { | |
| const [r, g, b] = bgRgb; | |
| const yiq = (r * 0.299 + g * 0.587 + b * 0.114); | |
| return yiq < 160; | |
| } | |
| // ──────────────────────────────────────────────── | |
| // Main logic | |
| // ──────────────────────────────────────────────── | |
| const allElements = document.querySelectorAll('*'); | |
| let gradientsFlattened = 0; | |
| let gradientTextsFixed = 0; | |
| let textColorsAdjusted = 0; | |
| console.groupCollapsed(`Scanning ${allElements.length.toLocaleString()} elements for gradients & gradient-text...`); | |
| allElements.forEach((el, idx) => { | |
| if (idx > 0 && idx % 4000 === 0) { | |
| console.log(`... progress (${idx}/${allElements.length})`); | |
| } | |
| if (el.textContent.trim().length === 0) return; | |
| const style = getComputedStyle(el); | |
| // ─── Case 1: Gradient TEXT technique ──────────────────────── | |
| if (isGradientTextLike(style)) { | |
| const gradientValue = style.backgroundImage; | |
| const solidColor = pickSolidColorFromGradient(gradientValue); | |
| // Remove gradient & clip → make normal colored text | |
| el.style.backgroundImage = 'none'; | |
| el.style.backgroundClip = 'border-box'; | |
| el.style.webkitBackgroundClip = 'border-box'; | |
| el.style.setProperty('-webkit-text-fill-color', 'currentColor', 'important'); | |
| el.style.color = solidColor; | |
| console.log( | |
| `%c${el.tagName} gradient-text detected → set color: ${solidColor}`, | |
| 'color:#d83' | |
| ); | |
| gradientTextsFixed++; | |
| gradientsFlattened++; | |
| return; // skip other checks for this element | |
| } | |
| // ─── Case 2: Normal background gradient (on box) ───────────── | |
| const bgProps = ['background', 'backgroundImage', 'background-image']; | |
| let bgChanged = false; | |
| let newBgColor = null; | |
| for (const prop of bgProps) { | |
| let val = style.getPropertyValue(prop).trim(); | |
| if (hasGradient(val)) { | |
| newBgColor = pickSolidColorFromGradient(val); | |
| el.style.setProperty(prop, newBgColor, 'important'); | |
| gradientsFlattened++; | |
| if (!bgChanged) { | |
| console.log( | |
| `%c${el.tagName} bg gradient → ${newBgColor}`, | |
| 'color:#777' | |
| ); | |
| bgChanged = true; | |
| } | |
| } | |
| } | |
| // ─── Contrast fix for elements with text & solid/opaque bg ─── | |
| if ( | |
| (bgChanged || style.backgroundColor !== 'rgba(0, 0, 0, 0)') && | |
| parseFloat(style.opacity) > 0.7 && | |
| style.display !== 'none' && style.visibility !== 'hidden' | |
| ) { | |
| const currentBg = getComputedStyle(el).backgroundColor; // re-compute after change | |
| const rgbMatch = currentBg.match(/rgb\((\d+),\s*(\d+),\s*(\d+)/); | |
| if (rgbMatch) { | |
| const bgRgb = [parseInt(rgbMatch[1]), parseInt(rgbMatch[2]), parseInt(rgbMatch[3])]; | |
| const useWhite = shouldUseWhiteText(bgRgb); | |
| const desired = useWhite ? '#ffffff' : '#000000'; | |
| // Force only if current looks bad | |
| const currentColor = style.color.toLowerCase(); | |
| if ( | |
| (useWhite && (currentColor.includes('black') || currentColor === 'transparent')) || | |
| (!useWhite && (currentColor.includes('white') || currentColor === 'transparent')) | |
| ) { | |
| el.style.setProperty('color', desired, 'important'); | |
| textColorsAdjusted++; | |
| console.log(` └─ text contrast → ${desired}`, 'color:#0a8'); | |
| } | |
| } | |
| } | |
| }); | |
| console.groupEnd(); | |
| console.log( | |
| `%cDone.\n` + | |
| `→ Flattened ${gradientsFlattened} gradients in total\n` + | |
| ` • ${gradientTextsFixed} were gradient-text (clip:text) cases → converted to solid color text\n` + | |
| ` • Adjusted contrast on ${textColorsAdjusted} other text elements`, | |
| "color:#0c0; font-weight:bold; font-size:1.1em" | |
| ); | |
| if (gradientsFlattened === 0) { | |
| console.log("%cNo gradients or gradient-text found", "color:#888"); | |
| } else { | |
| console.log("%cRefresh to undo", "color:#666; font-size:0.95em"); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment