RYM Release Page Colorizer

Change specific CSS variables and elements based on album cover colors on RateYourMusic

// ==UserScript==
// @name         RYM Release Page Colorizer
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Change specific CSS variables and elements based on album cover colors on RateYourMusic
// @author       https://greasyfork.org/users/1320826-polachek
// @match        *://*rateyourmusic.com/release/*
// @grant        none
// @require      https://cdnjs.cloudflare.com/ajax/libs/color-thief/2.3.0/color-thief.umd.js
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    function addGlobalStyle(css) {
        const style = document.createElement('style');
        style.type = 'text/css';
        style.appendChild(document.createTextNode(css));
        document.head.appendChild(style);
    }

    function adjustColor(rgb, percent) {
        return rgb.map(c => Math.min(255, Math.max(0, Math.floor(c * (1 + percent)))));
    }

    function getLuminance(rgb) {
        const a = rgb.map(v => {
            v /= 255;
            return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
        });
        return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
    }

    function getContrastRatio(rgb1, rgb2) {
        const lum1 = getLuminance(rgb1);
        const lum2 = getLuminance(rgb2);
        return (Math.max(lum1, lum2) + 0.05) / (Math.min(lum1, lum2) + 0.05);
    }

    function getContrastYIQ(rgb) {
        const yiq = ((rgb[0]*299)+(rgb[1]*587)+(rgb[2]*114))/1000;
        return yiq >= 128 ? 'black' : 'white';
    }

    function adjustForContrast(bgColor, fontColor) {
        let ratio = getContrastRatio(bgColor, fontColor);
        const bgLum = getLuminance(bgColor);
        const fontLum = getLuminance(fontColor);
        const adjustment = 0.1;
        let iterations = 0;

        while (ratio < 4.5 && iterations < 20) {
            fontColor = adjustColor(fontColor, (bgLum > fontLum) ? -adjustment : adjustment);
            ratio = getContrastRatio(bgColor, fontColor);
            iterations++;
        }

        if (ratio < 4.5) {
            return getContrastYIQ(bgColor) === 'black' ? [0, 0, 0] : [255, 255, 255];
        }

        return fontColor;
    }

    // addGlobalStyle(`*, *::before, *::after { border: none !important; }`);

    addGlobalStyle(`a { transition:.4s; }`);

    window.addEventListener('load', function () {
        const albumCover = document.querySelector('div[class^="coverart_"] img');

        if (!albumCover) return;

        albumCover.crossOrigin = "Anonymous";

        albumCover.onload = function () {
            const colorThief = new ColorThief();
            const palette = colorThief.getPalette(albumCover, 5);
            const dominantColor = palette[0];
            const fontColor = palette[1];
            const darkerColor = adjustColor(dominantColor, -0.05);
            const adjustedFontColor = adjustForContrast(dominantColor, fontColor);

            const dominantColorRGB = `rgb(${dominantColor.join(',')})`;
            const darkerColorRGB = `rgb(${darkerColor.join(',')})`;
            const adjustedFontColorRGB = `rgb(${adjustedFontColor.join(',')})`;

            addGlobalStyle(`
                body, body * { color: ${adjustedFontColorRGB}; }
                a.sametitle:hover { color: ${darkerColorRGB} !important; }
                #ui_search_input_main_search::placeholder { color: ${adjustedFontColorRGB} !important; }
                *, *::before, *::after { border-color: ${darkerColorRGB} !important; }
            `);

            const html = document.documentElement;
            const cssVars = {
                '--mono-0': adjustedFontColorRGB,
                '--mono-1': adjustedFontColorRGB,
                '--mono-3': adjustedFontColorRGB,
                '--mono-4': adjustedFontColorRGB,
                '--mono-5': adjustedFontColorRGB,
                '--mono-6': adjustedFontColorRGB,
                '--mono-7': adjustedFontColorRGB,
                '--mono-8': adjustedFontColorRGB,
                '--monof': dominantColorRGB,
                '--mono-a': adjustedFontColorRGB,
                '--mono-f': dominantColorRGB,
                '--mono-fb': dominantColorRGB,
                '--mono-f0': darkerColorRGB,
                '--mono-f2': darkerColorRGB,
                '--mono-f3': darkerColorRGB,
                '--mono-f4': dominantColorRGB,
                '--mono-f8': darkerColorRGB,
                '--mono-db': darkerColorRGB,
                '--mono-d8': darkerColorRGB,
                '--mono-e': darkerColorRGB,
                '--mono-d': dominantColorRGB,
                '--mono-ef': dominantColorRGB,
                '--mono-abs-3': dominantColorRGB,
                '--mono-abs-8': darkerColorRGB,
                '--mono-abs-a': adjustedFontColorRGB,
                '--mono-abs-d': adjustedFontColorRGB,
                '--mono-abs-f': adjustedFontColorRGB,
                '--surface-primary-light': darkerColorRGB,
                '--surface-secondary-light': darkerColorRGB,
                '--surface-tertiary': dominantColorRGB,
                '--background': dominantColorRGB,
                '--gen-bg-lightgreen': darkerColorRGB,
                '--btn-primary-background-default': darkerColorRGB,
                '--btn-primary-background-hover': dominantColorRGB,
                '--btn-primary-background-active': adjustedFontColorRGB,
                '--btn-expand-background-default': darkerColorRGB,
                '--btn-secondary-background-hover-light': dominantColorRGB,
                '--btn-secondary-background-default-light': darkerColorRGB,
                '--header-item-link': adjustedFontColorRGB,
                '--btn-primary-text': adjustedFontColorRGB,
                '--btn-secondary-text': adjustedFontColorRGB,
                '--btn-expand-background-hover': dominantColorRGB,
                '--btn-expand-background-disabled': darkerColorRGB,
                '--link-text-default-light': dominantColorRGB,
                '--link-text-hover': darkerColorRGB,
                '--gen-blue-dark': adjustedFontColorRGB,
                '--gen-blue-darker': adjustedFontColorRGB,
                '--gen-blue-darkest': adjustedFontColorRGB,
                '--link-text-default': adjustedFontColorRGB,
                '--text-primary': adjustedFontColorRGB,
                '--text-secondary': adjustedFontColorRGB,
            };

            for (const [key, value] of Object.entries(cssVars)) {
                html.style.setProperty(key, value);
            }

            const themeLight = document.querySelector('html.theme_light');
            if (themeLight) {
                [
                    '--mono-f', '--mono-fb', '--mono-f2', '--mono-f3', '--mono-f8',
                    '--mono-db', '--mono-d8', '--mono-4', '--mono-e', '--mono-ef',
                    '--mono-abs-3', '--surface-primary-light', '--surface-secondary-light',
                    '--surface-tertiary', '--background', '--gen-bg-lightgreen'
                ].forEach(varName => {
                    themeLight.style.setProperty(varName, cssVars[varName]);
                });
            }

            const releaseLeft = document.querySelector('.release_left_column');

            [releaseLeft].forEach(el => {
                if (el) {
                    el.style.backgroundColor = dominantColorRGB;
                    el.style.color = adjustedFontColorRGB;
                }
            });

            const pageArt = document.querySelector('.page_release_art_frame');

            [pageArt].forEach(el => {
                if (el) {
                    el.style.backgroundColor = darkerColorRGB;
                    el.style.color = adjustedFontColorRGB;
                }
            });


        };
    });
})();