Mark Dark Text Light in Dark Mode v1.0.0

← Back to User Scripts

Script Content

// ==UserScript==
// @name         Zendesk: Mark Dark Text Light in Dark Mode
// @namespace    https://www.timhilton.xyz/user-scripts
// @version      1.0.0
// @description  Adjusts dark text to light colour in Zendesk tickets when dark mode is active.
// @author       Tim Hilton using GitHub Copilot
// @match        https://*.zendesk.com/agent/tickets/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const LIGHT_TEXT_COLOR = 'rgb(216, 220, 222)';
    const DARK_TEXT_CLASS = 'zendesk-dark-text-adjusted';

    // Inject CSS that only applies in dark mode
    const style = document.createElement('style');
    style.textContent = `
        section[data-theme="dark"] .${DARK_TEXT_CLASS} {
            color: ${LIGHT_TEXT_COLOR} !important;
        }
    `;
    document.head.appendChild(style);

    function isTextDark(element) {
        const color = window.getComputedStyle(element).color;
        
        // Parse RGB values
        const rgbMatch = color.match(/\d+/g);
        if (!rgbMatch) {
            return false; // If colour can't be parsed (e.g., transparent, named colours), assume not dark
        }
        const rgb = rgbMatch.map(Number);
        
        // Calculate relative luminance (WCAG formula)
        const [r, g, b] = rgb.map(val => {
            val = val / 255;
            return val <= 0.03928 ? val / 12.92 : Math.pow((val + 0.055) / 1.055, 2.4);
        });
        
        const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
        
        // Luminance threshold - adjust as needed (0.5 is middle grey)
        return luminance < 0.5;
    }

    function adjustDarkText() {
        // Find all comment elements (in any theme)
        const commentElements = document.querySelectorAll('[data-test-id="omni-log-omni-to-ag-comment"]');
        
        commentElements.forEach(commentElement => {
            // Get text-containing elements within the comment
            const textElements = commentElement.querySelectorAll('p, span, div, h1, h2, h3, h4, h5, h6, li, td, th, strong, em, b, i');
            
            textElements.forEach(element => {
                // Only process elements that have text content and are visible
                if (element.textContent.trim() && element.offsetParent !== null) {
                    // Check if element already has the class to avoid redundant checks
                    if (!element.classList.contains(DARK_TEXT_CLASS)) {
                        if (isTextDark(element)) {
                            element.classList.add(DARK_TEXT_CLASS);
                        }
                    }
                }
            });
        });
    }

    // Use MutationObserver to handle dynamic content and navigation
    let timeoutId = null;
    const observer = new MutationObserver(() => {
        // Debounce to avoid excessive executions
        if (timeoutId) {
            clearTimeout(timeoutId);
        }
        timeoutId = setTimeout(() => {
            adjustDarkText();
        }, 100); // Wait 100ms after last mutation before processing
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    // Initial run
    adjustDarkText();
})();