Greasy Fork 还支持 简体中文。

Universal Dark Mode

An incredibly crappy implementation of universal dark mode

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Universal Dark Mode
// @namespace    https://github.com/colorBanded
// @version      1.0
// @description  An incredibly crappy implementation of universal dark mode
// @author       Sam Padilla
// @match        *://*/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    
    // Configuration
    const config = {
        // preserve
        preserveColors: [
            'red', 'green', 'blue', 'yellow', 'orange', 'purple', 'pink',
            '#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ffa500', '#800080', '#ffc0cb'
        ],
        
        // exclude from dark mode
        excludeSelectors: [
            'img', 'video', 'canvas', 'svg', 'iframe',
            '.preserve-colors', '[data-preserve-colors]'
        ],
        
        // Background color
        backgroundColor: '#000000',
        
        // Default text color
        textColor: '#ffffff',
        
        // Border color for elements
        borderColor: '#333333'
    };
    
    // Utility functions
    function isColorPreserved(color) {
        if (!color || color === 'inherit' || color === 'transparent') return false;
        
        // Convert rgb/rgba to hex for comparison
        const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
        if (rgbMatch) {
            const hex = '#' + [rgbMatch[1], rgbMatch[2], rgbMatch[3]]
                .map(x => parseInt(x).toString(16).padStart(2, '0')).join('');
            color = hex;
        }
        
        return config.preserveColors.some(preservedColor => 
            color.toLowerCase().includes(preservedColor.toLowerCase())
        );
    }
    
    function shouldExcludeElement(element) {
        return config.excludeSelectors.some(selector => {
            try {
                return element.matches && element.matches(selector);
            } catch (e) {
                return false;
            }
        });
    }
    
    function getComputedStyleProperty(element, property) {
        try {
            return window.getComputedStyle(element).getPropertyValue(property);
        } catch (e) {
            return '';
        }
    }
    
    // Main dark mode application function
    function applyDarkMode(element = document.documentElement) {
        // Apply to the element itself
        if (element.nodeType === Node.ELEMENT_NODE && !shouldExcludeElement(element)) {
            const computedStyle = window.getComputedStyle(element);
            
            // Handle background
            const bgColor = computedStyle.backgroundColor;
            if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
                if (!isColorPreserved(bgColor)) {
                    element.style.backgroundColor = config.backgroundColor;
                }
            }
            
            // Handle text color
            const textColor = computedStyle.color;
            if (textColor && !isColorPreserved(textColor)) {
                element.style.color = config.textColor;
            }
            
            // borders restaurant and cafe
            ['border-color', 'border-top-color', 'border-right-color', 
             'border-bottom-color', 'border-left-color'].forEach(prop => {
                const borderColor = computedStyle.getPropertyValue(prop);
                if (borderColor && borderColor !== 'rgba(0, 0, 0, 0)' && 
                    borderColor !== 'transparent' && !isColorPreserved(borderColor)) {
                    element.style.setProperty(prop, config.borderColor);
                }
            });
        }
        
        // Recursively apply to children
        // "how to kill a child (process)"
        if (element.children) {
            Array.from(element.children).forEach(child => {
                applyDarkMode(child);
            });
        }
    }
    
    // not mysql injection (CSS Injection)
    function injectGlobalCSS() {
        const style = document.createElement('style');
        style.id = 'dark-mode-userscript';
        style.textContent = `
            /* Global dark mode styles */
            html, body {
                background-color: ${config.backgroundColor} !important;
                color: ${config.textColor} !important;
            }
            
            /* Common elements */
            div, span, p, h1, h2, h3, h4, h5, h6, li, td, th, 
            article, section, header, footer, nav, aside {
                background-color: ${config.backgroundColor} !important;
                color: ${config.textColor} !important;
                border-color: ${config.borderColor} !important;
            }
            
            /* Form elements */
            input, textarea, select, button {
                background-color: #222222 !important;
                color: ${config.textColor} !important;
                border-color: ${config.borderColor} !important;
            }
            
            /* Links */
            a {
                color: #66b3ff !important;
            }
            
            a:visited {
                color: #cc99ff !important;
            }
            
            /* Preserve specific colors */
            ${config.preserveColors.map(color => `
                [style*="color: ${color}"],
                [style*="color:${color}"],
                [style*="background-color: ${color}"],
                [style*="background-color:${color}"] {
                    color: ${color} !important;
                }
            `).join('')}
            
            /* Exclude preserved elements */
            ${config.excludeSelectors.join(', ')} {
                filter: none !important;
            }
        `;
        
        // Insert at the beginning of head to allow overrides
        if (document.head) {
            document.head.insertBefore(style, document.head.firstChild);
        } else {
            document.documentElement.appendChild(style);
        }
    }
    
    // Observer for dynamically added content
    function setupMutationObserver() {
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                mutation.addedNodes.forEach((node) => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        applyDarkMode(node);
                    }
                });
            });
        });
        
        observer.observe(document.body || document.documentElement, {
            childList: true,
            subtree: true
        });
        
        return observer;
    }
    
    // Initialize dark mode :yippee:
    function initDarkMode() {
        // Inject global CSS immediately
        injectGlobalCSS();
        
        // Apply dark mode to existing elements
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => {
                applyDarkMode();
                setupMutationObserver();
            });
        } else {
        applyDarkMode();
        setupMutationObserver();
        }
        
        // Handle dynamically loaded content
        window.addEventListener('load', () => {
            setTimeout(() => applyDarkMode(), 1000);
        });
    }
    
    // Public API for customization
    window.DarkModeUserscript = {
        config: config,
        applyDarkMode: applyDarkMode,
        addPreservedColor: (color) => {
            config.preserveColors.push(color);
        },
        removePreservedColor: (color) => {
            const index = config.preserveColors.indexOf(color);
            if (index > -1) config.preserveColors.splice(index, 1);
        },
        addExcludeSelector: (selector) => {
            config.excludeSelectors.push(selector);
        },
        refresh: () => {
            document.getElementById('dark-mode-userscript')?.remove();
            initDarkMode();
        }
    };
    
    // Start the script
    initDarkMode();
    
    // Console info
    console.log('Velkommen to the Dark Side. Access customization via window.DarkModeUserscript');
    
})();