import Foundation enum RedditHomeChromeStyle { /// Keeps Reddit header separators flush with the in-app sidebar divider and /// fixes Log In / menu hit targets that get covered by borders or feed chrome. static let injectionScript = """ (function() { const STYLE_ID = 'reddora-home-chrome-style'; if (window.__reddoraHomeChromeInstalled) return; window.__reddoraHomeChromeInstalled = true; const HEADER_ACTION_IDS = ['login-button', 'expand-user-drawer-button']; const DECORATIVE_SELECTORS = [ 'faceplate-border', 'hr', '[role="separator"]', 'shreddit-overlay-display' ].join(', '); const style = document.createElement('style'); style.id = STYLE_ID; style.textContent = ` html, body, shreddit-app { margin-top: 0 !important; padding-top: 0 !important; } header, header > * { margin-left: 0 !important; } header { position: sticky !important; top: 0 !important; z-index: 300 !important; align-items: center !important; isolation: isolate !important; margin-top: 0 !important; padding-top: 0 !important; background: var(--shreddit-content-background, #ffffff) !important; } header faceplate-border, header [role="separator"], header hr { width: 100% !important; max-width: none !important; margin-left: 0 !important; margin-right: 0 !important; pointer-events: none !important; z-index: 0 !important; } header faceplate-search-input, header #SearchDropdown, header [data-testid="search-input-container"] { flex: 1 1 auto !important; min-width: 0 !important; max-width: 100% !important; overflow: hidden !important; align-self: center !important; pointer-events: auto !important; } /* Wrappers around header actions often extend past the visible button. */ faceplate-tracker:has(#login-button), faceplate-tracker:has(#expand-user-drawer-button), rpl-tooltip:has(#login-button), rpl-tooltip:has(#expand-user-drawer-button), activate-feature:has(#expand-user-drawer-button) { display: contents !important; } #login-button, #expand-user-drawer-button { position: relative !important; z-index: 301 !important; pointer-events: auto !important; flex-shrink: 0 !important; align-self: center !important; touch-action: manipulation !important; } shreddit-overlay-display { pointer-events: none !important; } shreddit-overlay-display:has( shreddit-signup-drawer, auth-flow-modal, input[type="password"] ) { pointer-events: auto !important; } /* Feed sort row sits below the header; keep it from overlapping action buttons. */ shreddit-sort-dropdown, [data-testid="sort-dropdown"], [data-testid="sort-posts-dropdown"], shreddit-feed-sort-dropdown { position: relative !important; z-index: 1 !important; } `; (document.head || document.documentElement).appendChild(style); function isDecorative(node) { if (!(node instanceof Element)) return false; return node.matches(DECORATIVE_SELECTORS) || (node.closest('header') && node.matches('faceplate-border, hr, [role="separator"]')); } function isInteractive(node) { if (!(node instanceof Element)) return false; return node.matches( 'button, a, [role="button"], input, select, textarea, label, faceplate-button' ); } function clearBlockersForButton(button) { if (!(button instanceof Element)) return; const rect = button.getBoundingClientRect(); if (rect.width < 1 || rect.height < 1) return; const sampleYs = [ rect.top + rect.height * 0.25, rect.top + rect.height * 0.5, rect.top + rect.height * 0.75, rect.bottom - 1 ]; const x = rect.left + rect.width * 0.5; sampleYs.forEach(function(y) { const stack = document.elementsFromPoint(x, y); for (let i = 0; i < stack.length; i++) { const el = stack[i]; if (el === button || button.contains(el)) break; if (isInteractive(el)) break; if (isDecorative(el) || !el.closest('header')) { el.style.setProperty('pointer-events', 'none', 'important'); el.dataset.reddoraPointerFix = '1'; } } }); } function pinHeader() { document.querySelectorAll('header, reddit-header-large, faceplate-header').forEach(function(node) { if (!(node instanceof HTMLElement)) return; node.style.setProperty('position', 'sticky', 'important'); node.style.setProperty('top', '0', 'important'); node.style.setProperty('z-index', '300', 'important'); node.style.setProperty( 'background', 'var(--shreddit-content-background, #ffffff)', 'important' ); }); } function patchHeaderHitTargets() { pinHeader(); HEADER_ACTION_IDS.forEach(function(id) { const button = document.getElementById(id); if (!button) return; clearBlockersForButton(button); button.style.setProperty('position', 'relative', 'important'); button.style.setProperty('z-index', '301', 'important'); }); document.querySelectorAll('header faceplate-border, header hr, header [role="separator"]').forEach(function(el) { el.style.setProperty('pointer-events', 'none', 'important'); }); } let patchTimer = null; function schedulePatch() { if (patchTimer) return; patchTimer = setTimeout(function() { patchTimer = null; patchHeaderHitTargets(); }, 50); } patchHeaderHitTargets(); [0, 100, 300, 750, 1500, 3000].forEach(function(delay) { setTimeout(patchHeaderHitTargets, delay); }); const observer = new MutationObserver(schedulePatch); observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true }); window.addEventListener('resize', schedulePatch, { passive: true }); window.addEventListener('scroll', schedulePatch, { passive: true }); function forwardClicksWithinButtonBounds(event) { if (event.type !== 'pointerdown') return; const menuButton = document.getElementById('expand-user-drawer-button'); if (!menuButton || menuButton.contains(event.target)) return; const rect = menuButton.getBoundingClientRect(); const x = event.clientX; const y = event.clientY; if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) return; event.preventDefault(); event.stopImmediatePropagation(); menuButton.click(); } document.addEventListener('pointerdown', forwardClicksWithinButtonBounds, true); })(); """ }