// ==UserScript==
// @name YouTube Fullscreen Fix — Disable “More Videos” Grid + Preserve Controls
// @namespace orangekite
// @version 1.3.6
// @description Hide fullscreen “More Videos” grid & vignette; keep scroll-wheel volume. Prevent tick-4/7 jiggle without forcing visibility. V-key manual hide. Hide interactive overlay in fullscreen. Allow normal autohide in FS and windowed modes.
// @match https://www.youtube.com/*
// @run-at document-start
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function () {
'use strict';
GM_addStyle(`
/* Fullscreen-scoped: match when either the player OR an ancestor is fullscreen */
.html5-video-player:fullscreen,
:fullscreen .html5-video-player {
--ytp-grid-scroll-percentage: 0 !important;
--ytp-grid-peek-height: 0px !important;
--ytp-grid-peek-gradient: 0 !important;
--ytp-controls-peek-height: 0px !important;
--ytp-controls-peek-percent: 0 !important;
}
/* Hide the fullscreen recommendations grid and its moving parts */
.html5-video-player:fullscreen [class*="fullscreen-grid"],
.html5-video-player:fullscreen [class*="fullerscreen-grid"],
.html5-video-player:fullscreen [class*="grid-stills"],
.html5-video-player:fullscreen [class*="videowall-still"],
:fullscreen .html5-video-player [class*="fullscreen-grid"],
:fullscreen .html5-video-player [class*="fullerscreen-grid"],
:fullscreen .html5-video-player [class*="grid-stills"],
:fullscreen .html5-video-player [class*="videowall-still"] {
display: none !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
height: 0 !important;
max-height: 0 !important;
overflow: hidden !important;
}
/* Hide the vignette/scrim/gradient behind the grid */
.html5-video-player:fullscreen [class*="grid-vignette"],
.html5-video-player:fullscreen [class*="grid-scrim"],
.html5-video-player:fullscreen [class*="gradient-bottom"],
:fullscreen .html5-video-player [class*="grid-vignette"],
:fullscreen .html5-video-player [class*="grid-scrim"],
:fullscreen .html5-video-player [class*="gradient-bottom"] {
display: none !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
/* Hide “fullerscreen” education/teaser overlays */
.html5-video-player:fullscreen [class*="fullerscreen"],
.html5-video-player:fullscreen .ytp-fullerscreen-edu-panel,
.html5-video-player:fullscreen .ytp-cards-teaser,
.html5-video-player:fullscreen .ytp-cards-teaser-box,
:fullscreen .html5-video-player [class*="fullerscreen"],
:fullscreen .html5-video-player .ytp-fullerscreen-edu-panel,
:fullscreen .html5-video-player .ytp-cards-teaser,
:fullscreen .html5-video-player .ytp-cards-teaser-box {
display: none !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
/* Safety net for lingering vignette/scrim overlays */
.html5-video-player:fullscreen [class*="vignette"],
.html5-video-player:fullscreen [class*="scrim"],
:fullscreen .html5-video-player [class*="vignette"],
:fullscreen .html5-video-player [class*="scrim"] {
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
/* --- IMPORTANT: Don't force visibility; just remove jiggle when bar is VISIBLE --- */
/* Neutralize any “peek” transforms but ONLY when not autohidden */
:is(.html5-video-player:fullscreen, :fullscreen .html5-video-player):not(.ytp-autohide):not(.ytp-hide-controls)
[class*="peek"] {
transform: none !important;
translate: 0 !important;
}
/* Stop tick-4 jiggle: anchor the chrome only when not autohidden */
:is(.html5-video-player:fullscreen, :fullscreen .html5-video-player):not(.ytp-autohide):not(.ytp-hide-controls)
.ytp-chrome-bottom {
bottom: 0 !important;
transform: translateY(0) !important;
translate: 0 !important;
margin-bottom: 0 !important;
transition: none !important;
}
/* If YouTube injects inline translateY on the chrome, zero it (when not autohidden) */
:is(.html5-video-player:fullscreen, :fullscreen .html5-video-player):not(.ytp-autohide):not(.ytp-hide-controls)
.ytp-chrome-bottom[style*="translate"] {
transform: translateY(0) !important;
}
/* Permanently remove interactive overlay in fullscreen */
.html5-video-player:fullscreen :is(.ytp-overlay-bottom-right, .branding-img-container.ytp-button),
:fullscreen .html5-video-player :is(.ytp-overlay-bottom-right, .branding-img-container.ytp-button) {
display: none !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
`);
// ---------------- Core JS helper: keep things smooth; V-key manual hide
let manualHideActive = false;
// NOTE: Do NOT force opacity/visibility in FS. Let YT autohide work.
const restoreChrome = () => {
const player = document.querySelector('.html5-video-player.ytp-fullscreen');
if (!player) return;
const chrome = player.querySelector('.ytp-chrome-bottom');
if (!chrome) return;
if (manualHideActive) return;
// If YT temporarily sets display:none during transitions, restore native display.
if (!chrome.dataset.nativeDisplay) {
chrome.dataset.nativeDisplay = getComputedStyle(chrome).display || 'flex';
}
const disp = getComputedStyle(chrome).display;
if (disp === 'none' || chrome.style.display === 'none') {
chrome.style.display = chrome.dataset.nativeDisplay;
}
// IMPORTANT: Do not set opacity/visibility/transform here.
// We only rely on CSS anchoring when not autohidden.
chrome.style.removeProperty('opacity');
chrome.style.removeProperty('visibility');
chrome.style.removeProperty('transform');
};
// Keep controls stable during wheel "peek" while in FS
window.addEventListener('wheel', restoreChrome, { passive: true });
// Scrub inline styles on FS boundaries so normal autohide works in both modes
document.addEventListener('fullscreenchange', () => {
const fsPlayer = document.querySelector('.html5-video-player.ytp-fullscreen');
const fsChrome = fsPlayer?.querySelector('.ytp-chrome-bottom');
manualHideActive = false;
if (document.fullscreenElement) {
// Entering FS: remove stale inline overrides and ensure native display
if (fsChrome) {
if (!fsChrome.dataset.nativeDisplay) {
fsChrome.dataset.nativeDisplay = getComputedStyle(fsChrome).display || 'flex';
}
fsChrome.style.removeProperty('opacity');
fsChrome.style.removeProperty('visibility');
fsChrome.style.removeProperty('transform');
fsChrome.style.removeProperty('bottom');
fsChrome.style.removeProperty('margin-bottom');
if (getComputedStyle(fsChrome).display === 'none') {
fsChrome.style.display = fsChrome.dataset.nativeDisplay;
}
}
} else {
// Exiting FS: clean all players so windowed autohide resumes
document.querySelectorAll('.html5-video-player .ytp-chrome-bottom').forEach(chrome => {
chrome.style.removeProperty('opacity');
chrome.style.removeProperty('visibility');
chrome.style.removeProperty('transform');
chrome.style.removeProperty('display');
chrome.style.removeProperty('bottom');
chrome.style.removeProperty('margin-bottom');
});
}
}, { passive: true });
// V-key: manual hide/show for the control bar (fullscreen only)
(() => {
const playerSelector = '.html5-video-player.ytp-fullscreen';
const chromeSelector = '.ytp-chrome-bottom';
function applyManualHideStyles(chrome, hide) {
if (hide) {
chrome.style.setProperty('opacity', '0', 'important');
chrome.style.setProperty('visibility', 'hidden', 'important');
chrome.style.removeProperty('transform');
} else {
chrome.style.removeProperty('opacity');
chrome.style.removeProperty('visibility');
restoreChrome();
}
}
window.addEventListener('keydown', e => {
if (e.key.toLowerCase() !== 'v') return;
const player = document.querySelector(playerSelector);
if (!player) return;
const chrome = player.querySelector(chromeSelector);
if (!chrome) return;
manualHideActive = !manualHideActive;
applyManualHideStyles(chrome, manualHideActive);
}, true);
// Keep behavior consistent if other events fire while hidden
const syncIfNeeded = () => {
if (!manualHideActive) return;
const player = document.querySelector(playerSelector);
const chrome = player?.querySelector(chromeSelector);
if (chrome) applyManualHideStyles(chrome, true);
};
window.addEventListener('wheel', syncIfNeeded, { passive: true });
window.addEventListener('keyup', syncIfNeeded, { passive: true });
})();
})();