YouTube Restore Scrollable Fullscreen

Restores scrollable fullscreen mode. Aggressively neutralizes all grid-state classes to prevent control bar freezing and hiding.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         YouTube Restore Scrollable Fullscreen
// @namespace    burak-tools
// @version      1.97
// @description  Restores scrollable fullscreen mode. Aggressively neutralizes all grid-state classes to prevent control bar freezing and hiding.
// @author       Waldoocs (https://x.com/Waldoocs) https://github.com/Waldoocs
// @match        https://www.youtube.com/*
// @run-at       document-idle
// @grant        GM_addStyle
// @grant        GM.addStyle
// @license      MIT
// @compatible   firefox
// @compatible   chrome
// ==/UserScript==

(function() {
    'use strict';

    const addStyle = (css) => {
        if (typeof GM_addStyle !== 'undefined') {
            GM_addStyle(css);
        } else if (typeof GM !== 'undefined' && GM.addStyle) {
            GM.addStyle(css);
        } else {
            const style = document.createElement('style');
            style.type = 'text/css';
            style.textContent = css;
            document.head.appendChild(style);
        }
    };

    addStyle(`
        /* Base layout restorations for scrolling */
        ytd-app[fullscreen] {
            overflow: auto !important;
        }
        ytd-app[scrolling] {
            position: absolute !important;
            top: 0 !important;
            left: 0 !important;
            right: calc((var(--ytd-app-fullerscreen-scrollbar-width) + 1px)*-1) !important;
            bottom: 0 !important;
            overflow-x: auto !important;
        }
        ytd-watch-flexy[fullscreen] #single-column-container.ytd-watch-flexy,
        ytd-watch-flexy[fullscreen] #columns.ytd-watch-flexy {
            display: flex !important;
        }

        /* Completely hide the grid and its invisible click-stealing overlays */
        .ytp-fullscreen-grid,
        .ytp-fullscreen-grid-stills-container,
        .ytp-fullscreen-grid-buttons-container,
        .ytp-fullscreen-grid-hover-overlay {
            display: none !important;
            visibility: hidden !important;
            opacity: 0 !important;
            pointer-events: none !important;
        }

        /* Force grid CSS math variables to zero */
        .html5-video-player {
            --ytp-grid-scroll-percentage: 0 !important;
            --ytp-grid-peek-height: 0px !important;
        }
    `);

    // Master blacklist of all classes YouTube uses to trigger grid states
    const GRID_CLASSES = [
        'ytp-grid-scrollable',
        'ytp-fullscreen-grid-peeking',
        'ytp-fullscreen-grid-active',
        'ytp-grid-scrolling'
    ];

    // Synchronous function to strip grid classes instantly
    function stripGridClasses() {
        const player = document.querySelector('.html5-video-player');
        if (!player) return;

        // Loop through the blacklist and remove any offending classes
        GRID_CLASSES.forEach(cls => {
            if (player.classList.contains(cls)) {
                player.classList.remove(cls);
            }
        });
    }

    function init() {
        stripGridClasses();

        // Observer fires instantly to prevent UI lockups before the screen paints
        const playerObserver = new MutationObserver(stripGridClasses);

        // Wait for player to exist before attaching
        const checkPlayer = setInterval(() => {
            const player = document.querySelector('.html5-video-player');
            if (player) {
                clearInterval(checkPlayer);
                playerObserver.observe(player, {
                    attributes: true,
                    attributeFilter: ['class']
                });
            }
        }, 500);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();