Skip to content

Instantly share code, notes, and snippets.

@felquis
Created March 18, 2026 20:37
Show Gist options
  • Select an option

  • Save felquis/676fc02953daae26d14de63c5e06d7e5 to your computer and use it in GitHub Desktop.

Select an option

Save felquis/676fc02953daae26d14de63c5e06d7e5 to your computer and use it in GitHub Desktop.
Remove gradient backgrounds from any website
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