PrairieLearn Dark Mode

Dark mode for PrairieLearn

// ==UserScript==
// @name         PrairieLearn Dark Mode
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Dark mode for PrairieLearn
// @author       plon
// @match        https://us.prairielearn.com/*
// @exclude      https://us.prairielearn.com/pl/workspace/*
// @grant        none
// @license      MIT
// @run-at       document-start
// ==/UserScript==

(function () {
    "use strict";

    const darkModeStyles = `
        :root {
            --pl-dark-bg-primary: #1a1a1a;
            --pl-dark-bg-secondary: #222222;
            --pl-dark-bg-tertiary: #2a2a2a;
            --pl-dark-text-primary: #e0e0e0;
            --pl-dark-text-secondary: #b0b0b0;
            --pl-dark-border-color: #444444;
            --pl-dark-link-color: #4da8ff;
            --pl-dark-link-hover-color: #80bfff;
            --pl-dark-code-bg: #282c34;
            --pl-dark-code-text: #abb2bf;
            --pl-dark-disabled-bg: #333333;
            --pl-dark-disabled-text: #777777;
        }

        html {
             color-scheme: dark;
        }
        body {
            background-color: var(--pl-dark-bg-primary) !important;
            color: var(--pl-dark-text-primary) !important;
        }


        .card, .panel, .container, .container-fluid, .modal-content, .dropdown-menu, .popover, .navbar, .offcanvas, .list-group-item, .jumbotron,
        header, footer, nav, main,
        [class*="bg-light"], [class*="bg-white"],
        .tooltip-inner  {
            background-color: var(--pl-dark-bg-secondary) !important;
            color: var(--pl-dark-text-primary) !important;
            border-color: var(--pl-dark-border-color) !important;
        }

        .modal-header, .modal-footer, .card-header, .card-footer, .popover-header, .offcanvas-header, .list-group-item-heading  {
            background-color: var(--pl-dark-bg-tertiary) !important;
            border-color: var(--pl-dark-border-color) !important;
            color: inherit !important;
        }

        .popover .popover-arrow::before, .popover .popover-arrow::after { border-color: transparent !important; }
        .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::after { border-top-color: var(--pl-dark-bg-secondary) !important; }
        .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::after { border-right-color: var(--pl-dark-bg-secondary) !important; }
        .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::after { border-bottom-color: var(--pl-dark-bg-tertiary) !important; }
        .bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::before { border-bottom-color: var(--pl-dark-border-color) !important; }
        .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::after { border-left-color: var(--pl-dark-bg-secondary) !important; }

        .popover-body {
             color: var(--pl-dark-text-primary) !important;
        }



        button, input, select, textarea, .form-control, .form-select, .input-group-text {
            background-color: var(--pl-dark-bg-tertiary) !important;
            color: var(--pl-dark-text-primary) !important;
            border: 1px solid var(--pl-dark-border-color) !important;
        }
        input::placeholder, textarea::placeholder { color: var(--pl-dark-text-secondary) !important; opacity: 0.7 !important; }
        input:disabled, textarea:disabled, button:disabled, select:disabled, .form-control:disabled, .form-select:disabled { background-color: var(--pl-dark-disabled-bg) !important; color: var(--pl-dark-disabled-text) !important; opacity: 0.7 !important; cursor: not-allowed !important; }
        .form-check-input { background-color: var(--pl-dark-bg-tertiary) !important; border-color: var(--pl-dark-border-color) !important; }
        .form-check-input:checked { background-color: var(--pl-dark-link-color) !important; border-color: var(--pl-dark-link-color) !important; }
        .form-control-plaintext { color: var(--pl-dark-text-primary) !important; background-color: transparent !important; border-color: transparent !important; }



        .btn { border-color: var(--pl-dark-border-color) !important; }
        .btn-primary { background-color: #0d6efd !important; border-color: #0b5ed7 !important; color: white !important; }
        .btn-primary:hover { background-color: #0b5ed7 !important; border-color: #0a58ca !important; }
        .btn-secondary, .btn-light { background-color: #444 !important; border-color: #555 !important; color: var(--pl-dark-text-primary) !important; }
        .btn-secondary:hover, .btn-light:hover { background-color: #555 !important; border-color: #666 !important;}
        .btn-danger { background-color: #dc3545 !important; border-color: #b02a37 !important; color: white !important; }
        .btn-danger:hover { background-color: #b02a37 !important; border-color: #a02633 !important; }
        .btn-info { background-color: #1a4d5a !important; border-color: #2a6d7a !important; color: #cff4fc !important; }
        .btn-info:hover { background-color: #2a6d7a !important; border-color: #3aa0b8 !important; }
        .btn-success { background-color: #198754 !important; border-color: #146c43 !important; color: white !important; }
        .btn-success:hover { background-color: #146c43 !important; border-color: #105635 !important; }
        .btn-warning { background-color: #ffc107 !important; border-color: #d39e00 !important; color: #333 !important; }
        .btn-warning:hover { background-color: #d39e00 !important; border-color: #b88a00 !important; }
        .btn-link { color: var(--pl-dark-link-color) !important; background-color: transparent !important; border-color: transparent !important; text-decoration: underline !important; }
        .btn-link:hover { color: var(--pl-dark-link-hover-color) !important; }



        table, .table { border-color: var(--pl-dark-border-color) !important; }
        th, td {
            background-color: var(--pl-dark-bg-tertiary) !important;
            color: var(--pl-dark-text-primary) !important;
            border-color: var(--pl-dark-border-color) !important;
             vertical-align: middle !important;
        }
        thead th { background-color: #303030 !important; }
        .table-striped tbody tr:nth-of-type(odd) > * { background-color: var(--pl-dark-bg-secondary) !important; }
        .table-hover tbody tr:hover > * { background-color: #353535 !important; }



        a { color: var(--pl-dark-link-color) !important; }
        a:hover { color: var(--pl-dark-link-hover-color) !important; }



        .text-dark, .text-black, .text-body { color: var(--pl-dark-text-primary) !important; }
        .text-muted { color: var(--pl-dark-text-secondary) !important; }



        :not(.pl-file-editor) code, :not(.pl-file-editor) kbd, :not(.pl-file-editor) samp, :not(.pl-file-editor) pre {
             background-color: var(--pl-dark-code-bg) !important;
             color: var(--pl-dark-code-text) !important;
             border: 1px solid var(--pl-dark-border-color) !important;
             border-radius: 3px;
             padding: 0.2em 0.4em;
        }
         :not(.pl-file-editor) pre { padding: 0.8em !important; overflow: auto;}
         :not(.pl-file-editor) pre code { background-color: transparent !important; color: inherit !important; border: none !important; padding: 0 !important; }



        img, svg, [class*="fa-"], [class*="bi-"], [class*="glyphicon-"] {
            background-color: transparent !important;
        }


        .pl-file-editor .editor span[class*="ace_"],
        .pl-file-editor .editor div[class*="ace_"] { color: initial !important; background-color: initial !important; }
        .pl-file-editor .editor { background-color: initial !important; color: initial !important; border: initial !important; }
        .pl-file-editor .editor .ace_gutter { background: initial !important; color: initial !important; }
        .pl-file-editor .editor .ace_cursor { color: initial !important; }
        .pl-file-editor .modal-content { background-color: var(--pl-dark-bg-secondary) !important; color: var(--pl-dark-text-primary) !important; border-color: var(--pl-dark-border-color) !important; }



        hr, .dropdown-divider { border-top-color: var(--pl-dark-border-color) !important; opacity: 0.25 !important; }
        .border, .border-top, .border-bottom, .border-start, .border-end { border-color: var(--pl-dark-border-color) !important; }




        .progress {
             background-color: #444444 !important;
             border-radius: 0.25rem;
             border: 1px solid var(--pl-dark-border-color) !important;


             height: 1.25rem;
             position: relative;
             overflow: hidden;
        }

        .progress.border-success { border-color: #1e6a45 !important; }
        .progress.border-primary { border-color: #0b5ed7 !important; }
        .progress.border-info { border-color: #3aa0b8 !important; }
        .progress.border-warning { border-color: #997404 !important; }
        .progress.border-danger { border-color: #b02a37 !important; }

        .progress-bar {
             color: white !important;
             font-weight: 600;
             display: flex !important;
             justify-content: center !important;
             align-items: center !important;
             background-color: initial;
         }

         .progress-bar.bg-success { background-color: #198754 !important; }
         .progress-bar.bg-info { background-color: #0dcaf0 !important; }
         .progress-bar.bg-warning { background-color: #ffc107 !important; color: #333 !important; }
         .progress-bar.bg-danger { background-color: #dc3545 !important; }
         .progress-bar.bg-primary { background-color: #0d6efd !important; }
         .progress-bar.bg-secondary { background-color: #6c757d !important; }


         .progress > div:not(.progress-bar) {
            color: white !important;
            display: flex !important;
            justify-content: center !important;
            align-items: center !important;
            font-weight: 600;
            width: 100%;
            position: absolute;
            left: 0;
            top: 0;
            height: 100%;
            line-height: normal;
         }



    `;

    const styleElement = document.createElement("style");
    styleElement.id = "prairielearn-dark-mode-style";
    styleElement.textContent = darkModeStyles;
    document.head.appendChild(styleElement);
})();