Skip to content

Instantly share code, notes, and snippets.

@noizbuster
Created June 11, 2026 02:41
Show Gist options
  • Select an option

  • Save noizbuster/5788014650d180fa81691b496d42937f to your computer and use it in GitHub Desktop.

Select an option

Save noizbuster/5788014650d180fa81691b496d42937f to your computer and use it in GitHub Desktop.
Notion Layout Shift Bug Fix
// ==UserScript==
// @name Notion Layout Left-Shift Bug Fix
// @namespace http://tampermonkey.net/
// @version 2026-06-09
// @description Comprehensive CSS reinforcement to fix Notion layout shifting left on PgUp/PgDn in modern Chrome.
// @author NoizBuster
// @match https://app.notion.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=notion.com
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
const DEBUG = false;
function log(...args) {
if (DEBUG) console.log('[Notion PgUp/PgDn Fix]', ...args);
}
function isPageKey(event) {
return event.key === 'PageDown' || event.key === 'PageUp';
}
function isEditableTarget(target) {
if (!target || !(target instanceof Element)) return false;
const tagName = target.tagName?.toLowerCase();
return (
target.isContentEditable ||
tagName === 'input' ||
tagName === 'textarea' ||
tagName === 'select' ||
target.closest('[contenteditable="true"]') ||
target.closest('[role="textbox"]')
);
}
function getAllScrollableElements() {
const selectors = [
'html',
'body',
'#notion-app',
'.notion-app-inner',
'.notion-frame',
'.notion-scroller',
'.notion-scroller.vertical',
'[data-testid="notion-app"]',
'[data-block-id]',
'[style*="overflow"]',
'[style*="transform"]'
];
const set = new Set();
for (const selector of selectors) {
document.querySelectorAll(selector).forEach((el) => set.add(el));
}
set.add(document.documentElement);
set.add(document.body);
if (document.scrollingElement) {
set.add(document.scrollingElement);
}
return [...set].filter(Boolean);
}
function neutralizeHorizontalScroll() {
const elements = getAllScrollableElements();
for (const el of elements) {
try {
if ('scrollLeft' in el && el.scrollLeft !== 0) {
log('reset scrollLeft', el, el.scrollLeft);
el.scrollLeft = 0;
}
} catch (_) {}
}
try {
window.scrollTo({
left: 0,
top: window.scrollY,
behavior: 'auto'
});
} catch (_) {
window.scrollTo(0, window.scrollY);
}
}
function injectCss() {
if (document.getElementById('notion-pgup-pgdn-horizontal-fix-style')) {
return;
}
const style = document.createElement('style');
style.id = 'notion-pgup-pgdn-horizontal-fix-style';
style.textContent = `
html,
body {
max-width: 100vw !important;
overflow-x: clip !important;
}
#notion-app,
.notion-app-inner,
.notion-frame {
max-width: 100vw !important;
overflow-x: clip !important;
}
.notion-scroller.vertical {
overflow-x: hidden !important;
overscroll-behavior-x: none !important;
}
body {
position: relative !important;
}
`;
const append = () => {
if (document.head) {
document.head.appendChild(style);
} else {
requestAnimationFrame(append);
}
};
append();
}
function runCorrectionBurst() {
neutralizeHorizontalScroll();
requestAnimationFrame(() => {
neutralizeHorizontalScroll();
requestAnimationFrame(() => {
neutralizeHorizontalScroll();
});
});
setTimeout(neutralizeHorizontalScroll, 0);
setTimeout(neutralizeHorizontalScroll, 16);
setTimeout(neutralizeHorizontalScroll, 50);
setTimeout(neutralizeHorizontalScroll, 120);
setTimeout(neutralizeHorizontalScroll, 250);
}
function onKeyDown(event) {
if (!isPageKey(event)) return;
// Ctrl / Alt / Meta / Shift 조합은 브라우저나 OS 단축키일 수 있으므로 건드리지 않음
if (event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) {
return;
}
// Notion 에디터 내부에서 텍스트 편집 중이어도 PageUp/PageDown은 보통 페이지 이동 목적이므로
// 완전히 막지는 않고, 수평 깨짐만 보정한다.
log('page key detected', event.key, 'editable:', isEditableTarget(event.target));
runCorrectionBurst();
}
function onKeyUp(event) {
if (!isPageKey(event)) return;
runCorrectionBurst();
}
function observeLayoutChanges() {
const observer = new MutationObserver(() => {
// Notion이 페이지 구조를 교체하는 경우 CSS가 빠지지 않도록 재삽입
injectCss();
});
const start = () => {
if (!document.documentElement) {
requestAnimationFrame(start);
return;
}
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
};
start();
}
injectCss();
window.addEventListener('keydown', onKeyDown, true);
window.addEventListener('keyup', onKeyUp, true);
document.addEventListener('keydown', onKeyDown, true);
document.addEventListener('keyup', onKeyUp, true);
observeLayoutChanges();
// 초기 로딩 중 이미 수평 스크롤이 생기는 경우 방어
window.addEventListener('load', () => {
injectCss();
runCorrectionBurst();
});
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
injectCss();
runCorrectionBurst();
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment