// ==UserScript== // @name Reddit Tweaks (Apollo) // @version 0.3.0 // @match *://old.reddit.com/* // @match *://www.reddit.com/* // @downloadURL https://gist.githubusercontent.com/evanreichard/968eee1cf83da38c81c155fb775cf3c6/raw/old-reddit-mobile.user.js // @grant GM.addStyle // @grant GM.registerMenuCommand // @grant GM.unregisterMenuCommand // @grant GM.getValue // @grant GM.setValue // @grant GM_getValue // @noframes // @run-at document-start // ==/UserScript== /* ============================================================================ * REDIRECT: www.reddit.com -> old.reddit.com * ---------------------------------------------------------------------------- * Run before anything else so we don't waste cycles styling the new design. * The /media path is a special case: Reddit's image viewer wraps the image * in heavy chrome, so we strip everything down to just the tag. * ========================================================================== */ if (document.location.host === "www.reddit.com") { if (document.location.pathname === "/media") { document.addEventListener("DOMContentLoaded", () => { const img = document.querySelector("img"); if (img) document.body.innerHTML = img.outerHTML; }); } else { window.location.href = "https://old.reddit.com" + window.location.pathname + window.location.search + window.location.hash; } } /* ============================================================================ * STATE * ---------------------------------------------------------------------------- * `isEnabled` is read synchronously at script start so we can decide whether * to inject CSS at document-start (before paint). Toggling the script ALWAYS * reloads the page — see toggleStyle() for the reasoning. * ========================================================================== */ let isEnabled = GM_getValue("enabled", true); /* ============================================================================ * STYLES * ---------------------------------------------------------------------------- * All visual styling lives here. Uses CSS custom properties for theming so * dark mode is just a media query override on the :root tokens. * ========================================================================== */ function applyStyle() { GM.addStyle(` /* -------------------------------------------------------------- DESIGN TOKENS -------------------------------------------------------------- */ :root { --bg: #ffffff; --bg-elev: #f7f8fa; --bg-hover: #eef0f3; --surface: #ffffff; --surface-2: #f4f5f7; --border: #e3e6ea; --border-soft: #ecedef; --fg: #1c1c1e; --fg-2: #3c3c43; --fg-dim: #6e6e73; --fg-faint: #b0b3b8; --accent: #ff4500; --accent-2: #ff6a33; --link: #2f6feb; --visited: #8a8d96; --upvote: #ff4500; --downvote: #5b8def; /* Apollo-style thread depth colors (cycle every 6) */ --depth-1: #4f9cf9; /* blue */ --depth-2: #34c759; /* green */ --depth-3: #ffcc00; /* yellow */ --depth-4: #ff9f0a; /* orange */ --depth-5: #ff453a; /* red */ --depth-6: #bf5af2; /* purple */ --radius-sm: 6px; --radius: 10px; --radius-lg: 14px; --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", "Segoe UI", "Inter", Roboto, system-ui, sans-serif; --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace; --shadow-sm: 0 1px 2px rgba(0,0,0,0.04); --shadow: 0 2px 8px rgba(0,0,0,0.06); --tap: 44px; } @media (prefers-color-scheme: dark) { :root { --bg: #0e0f12; --bg-elev: #16181d; --bg-hover: #1e2128; --surface: #16181d; --surface-2: #1c1f25; --border: #26292f; --border-soft: #1e2128; --fg: #f2f2f7; --fg-2: #d1d1d6; --fg-dim: #9b9ba1; --fg-faint: #5a5d63; --link: #5b8def; --visited: #6e6f78; --shadow-sm: 0 1px 2px rgba(0,0,0,0.3); --shadow: 0 4px 12px rgba(0,0,0,0.4); } } /* -------------------------------------------------------------- GLOBAL RESET / BASE -------------------------------------------------------------- */ html, body { background: var(--bg) !important; color: var(--fg) !important; font-family: var(--font-sans) !important; font-size: 15px !important; line-height: 1.4 !important; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: 100%; } a { color: var(--link); text-decoration: none; } a:visited { color: var(--visited); } a:hover { color: var(--fg); } /* Hide all the cruft old.reddit ships with by default */ .side, .footer-parent, .rank, .rank-spacer, #sr-header-area, .mobile-web-redirect-bar, .link.promotedlink, .infobar.listingsignupbar, .infobar.welcome, .listingsignupbar, .promoted, [data-promoted="true"], .res-floatingSideBar, .bottommenu, .sr-list, .sr-bar, .sr-interest-bar, .leavemoderator, .titlebox, .login-form-side, .icon-menu .gold-buy, .reportform, .panestack-title .res-comments-sortMenu, .link .flat-list.buttons li.share, .link .flat-list.buttons .post-sharing-button, .link .flat-list.buttons .reportbtn { display: none !important; } /* -------------------------------------------------------------- HEADER -------------------------------------------------------------- */ #header { background: var(--bg-elev) !important; border-bottom: 1px solid var(--border) !important; box-shadow: var(--shadow-sm); padding: 0 12px !important; min-height: 48px !important; height: auto !important; position: sticky; top: 0; z-index: 1000; backdrop-filter: saturate(180%) blur(12px); -webkit-backdrop-filter: saturate(180%) blur(12px); } #header-bottom-left { display: flex !important; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 14px; padding: 8px 0 !important; } #header-img, .pagename a { color: var(--fg) !important; font-weight: 700 !important; font-size: 17px !important; letter-spacing: -0.01em; } .pagename { padding: 0 !important; } .tabmenu { width: 100%; display: flex !important; gap: 4px; margin: 0 !important; overflow-x: auto; scrollbar-width: none; } .tabmenu::-webkit-scrollbar { display: none; } .tabmenu li { margin: 0 !important; } .tabmenu li a { display: inline-block; padding: 6px 12px !important; border-radius: 999px; font-size: 13px !important; font-weight: 500; color: var(--fg-dim) !important; background: transparent !important; border: 1px solid transparent !important; transition: background 0.15s ease, color 0.15s ease; } .tabmenu li.selected a { background: var(--accent) !important; color: #fff !important; } .tabmenu li:not(.selected) a:hover { background: var(--bg-hover) !important; color: var(--fg) !important; } #header-bottom-right { top: 0; bottom: unset; border-radius: 0 0 var(--radius-sm) var(--radius-sm); background: var(--bg-elev) !important; color: var(--fg-dim) !important; font-size: 12px !important; padding: 6px 10px !important; border-left: 1px solid var(--border); border-bottom: 1px solid var(--border); } #header-bottom-right a { color: var(--fg-dim) !important; } /* -------------------------------------------------------------- CONTENT WRAPPER -------------------------------------------------------------- */ .content[role="main"] { margin: 0 !important; padding: 0 !important; max-width: 100% !important; background: var(--bg) !important; color: var(--fg) !important; } #siteTable { margin: 0 !important; } /* -------------------------------------------------------------- POSTS ---------------------------------------------------------------- Flex layout that preserves Reddit's native expando behavior. Order: thumbnail | entry | (votes hidden) | (wrap) expando -------------------------------------------------------------- */ .link { position: relative; display: flex !important; flex-wrap: wrap; align-items: flex-start; column-gap: 12px; row-gap: 4px; padding: 12px 14px !important; margin: 0 !important; background: var(--surface) !important; color: var(--fg) !important; border: 0 !important; border-bottom: 1px solid var(--border-soft) !important; transition: background 0.12s ease; } .link:active { background: var(--bg-hover) !important; } .link.last-clicked { background: var(--bg-elev) !important; border-bottom: 1px solid var(--border-soft) !important; } .link > .thumbnail { order: 1; flex: 0 0 auto; } .link > .entry { order: 2; flex: 1 1 0; min-width: 0; } .link > .midcol { order: 3; flex: 0 0 auto; } .link > .child, .link > .clearleft { display: none !important; } /* Hide post-level vote arrows and native expando button (we tap thumbnail instead) */ .link > .midcol { display: none !important; } .link .expando-button { display: none !important; } /* Thumbnail container — shared by real and synthetic thumbs */ .link > .thumbnail { width: 64px !important; height: 64px !important; margin: 0 !important; border-radius: var(--radius); overflow: hidden; background: var(--bg-elev); border: 1px solid var(--border-soft); display: flex !important; align-items: center; justify-content: center; cursor: pointer; } .link > .thumbnail img { width: 100% !important; height: 100% !important; object-fit: cover !important; display: block; } /* Placeholder labels when there's no real image */ .link > .thumbnail.self::after, .link > .thumbnail.default::after, .link > .thumbnail.spoiler::after, .link > .thumbnail.nsfw::after { font-size: 10px; font-weight: 700; letter-spacing: 0.05em; color: var(--fg-dim); } .link > .thumbnail.self::after { content: "TEXT"; } .link > .thumbnail.default::after { content: "LINK"; } .link > .thumbnail.spoiler::after { content: "⚠"; font-size: 18px; } .link.over18 > .thumbnail::after { content: "NSFW"; color: #ff453a; } /* Entry / title / meta */ .link .entry { margin: 0 !important; color: var(--fg) !important; } .link .top-matter { padding: 0 !important; margin: 0 !important; } .link .title { margin: 0 0 4px 0 !important; padding: 0 !important; font-size: 15px !important; line-height: 1.3 !important; font-weight: 500; letter-spacing: -0.01em; } .link .title a.title { color: var(--fg) !important; } .link.visited .title a.title, .link .title a.title:visited { color: var(--fg-dim) !important; } .link .title .domain { display: inline-block; font-size: 11px !important; color: var(--fg-dim) !important; font-weight: 400; margin-left: 4px; vertical-align: middle; } .link .title .domain a { color: var(--fg-dim) !important; } .link .linkflairlabel { display: inline-flex; align-items: center; gap: 3px; font-size: 10.5px !important; font-weight: 600; padding: 2px 7px !important; border-radius: 999px; margin-right: 6px; vertical-align: middle; line-height: 1.4; border: 0 !important; } .link .flairemoji { width: 12px !important; height: 12px !important; } .link .tagline { font-size: 12px !important; color: var(--fg-dim) !important; margin: 0 !important; line-height: 1.3 !important; } .link .tagline a, .link .tagline time { color: var(--fg-dim) !important; } .link .tagline .author { color: var(--fg-2) !important; font-weight: 500; } .link .flat-list.buttons { display: flex; gap: 12px; margin: 4px 0 0 0 !important; padding: 0 !important; font-size: 12px !important; list-style: none; } .link .flat-list.buttons li { display: inline-block; margin: 0; padding: 0; } .link .flat-list.buttons a { color: var(--fg-dim) !important; font-weight: 500; } .link .flat-list.buttons .comments { color: var(--accent) !important; font-weight: 600; } /* -------------------------------------------------------------- EXPANDO (full-width row beneath the post) -------------------------------------------------------------- */ .link > .expando { order: 4; flex: 0 0 100%; width: 100% !important; max-width: 100% !important; margin: 8px 0 0 0 !important; padding: 0 !important; background: transparent !important; border: 0 !important; color: var(--fg) !important; box-sizing: border-box; } .link > .expando .media-preview, .link > .expando .media-preview-content, .link > .expando .res-expando-box, .link > .expando .res-expando-box div { max-width: 100% !important; width: 100% !important; } .link .expando img, .link .expando video, .link .expando iframe, .link .expando .preview, .link .expando .media-preview-content > * { max-width: 100% !important; height: auto !important; border-radius: var(--radius); display: block; } /* Self-text body */ .link .expando .usertext-body, .link .usertext-body, .commentarea .link .usertext-body { background: var(--surface-2) !important; color: var(--fg) !important; border: 1px solid var(--border-soft) !important; border-radius: var(--radius); padding: 10px 14px !important; margin: 0 !important; } .link .expando .md, .link .usertext-body .md { background: transparent !important; color: var(--fg) !important; max-width: unset !important; font-size: 14px !important; line-height: 1.55 !important; } .link .expando .md p, .link .expando .md li, .link .expando .md h1, .link .expando .md h2, .link .expando .md h3, .link .expando .md blockquote { color: var(--fg) !important; } /* -------------------------------------------------------------- COMMENTS — bubble-in-bubble with depth-colored left border -------------------------------------------------------------- */ .commentarea { background: var(--bg) !important; color: var(--fg) !important; padding: 8px 10px !important; margin: 0 !important; } .commentarea > .menuarea { background: transparent !important; border: 0 !important; padding: 8px 4px !important; margin: 0 0 4px 0 !important; font-size: 12px; color: var(--fg-dim) !important; } .comment { background: var(--surface) !important; color: var(--fg) !important; border: 1px solid var(--border-soft) !important; border-left: 3px solid var(--depth-1) !important; border-radius: var(--radius); padding: 10px 12px !important; margin: 6px 0 !important; box-shadow: var(--shadow-sm); } .comment .child, .comment .showreplies { border-left: 0 !important; margin: 6px 0 0 6px !important; padding: 0 !important; } /* Cycle border colors by nesting depth */ .comment .comment { border-left-color: var(--depth-2) !important; } .comment .comment .comment { border-left-color: var(--depth-3) !important; } .comment .comment .comment .comment { border-left-color: var(--depth-4) !important; } .comment .comment .comment .comment .comment { border-left-color: var(--depth-5) !important; } .comment .comment .comment .comment .comment .comment { border-left-color: var(--depth-6) !important; } .comment .comment .comment .comment .comment .comment .comment { border-left-color: var(--depth-1) !important; } .comment .comment .comment .comment .comment .comment .comment .comment { border-left-color: var(--depth-2) !important; } .comment .comment .comment .comment .comment .comment .comment .comment .comment { border-left-color: var(--depth-3) !important; } /* Alternate bg shades to emphasize nesting */ .comment .comment { background: var(--surface-2) !important; } .comment .comment .comment { background: var(--surface) !important; } .comment .entry { padding: 0 !important; } .comment .tagline { font-size: 12px !important; color: var(--fg-dim) !important; margin-bottom: 4px; } .comment .tagline .author { color: var(--fg) !important; font-weight: 600; } .comment .tagline .submitter { color: var(--link) !important; font-weight: 600; } .comment .tagline .score { color: var(--fg-dim) !important; font-weight: 500; } .comment .usertext-body, .comment .usertext-body .md, .comment .md { font-size: 14px !important; line-height: 1.5 !important; color: var(--fg) !important; background: transparent !important; } .comment .md p { margin: 4px 0 !important; color: var(--fg) !important; } .comment .md a { color: var(--link) !important; } .comment .md blockquote { border-left: 3px solid var(--border); padding-left: 10px; color: var(--fg-2) !important; margin: 6px 0; } .comment .md code { background: var(--bg-elev) !important; color: var(--fg) !important; border-radius: 4px; padding: 1px 5px; font-family: var(--font-mono); font-size: 12.5px; } .comment .md pre { background: var(--bg-elev) !important; color: var(--fg) !important; padding: 8px 10px; border-radius: var(--radius-sm); overflow-x: auto; } .comment .midcol { width: 16px !important; margin-right: 4px !important; } .comment .arrow { width: 18px !important; height: 18px !important; background-size: 14px !important; border-radius: 4px; } .comment .arrow:hover { background-color: var(--bg-hover); } .comment .flat-list.buttons { font-size: 12px !important; margin-top: 4px !important; } .comment .flat-list.buttons a { color: var(--fg-dim) !important; font-weight: 500; } .comment .flat-list.buttons .reply-button a { color: var(--link) !important; } .comment .expand { color: var(--fg-dim) !important; font-weight: 600; padding: 0 4px; } .comment.collapsed { opacity: 0.7; padding: 6px 12px !important; } /* "load more" buttons */ .morechildren a, .morecomments a { display: inline-block; padding: 6px 12px; border-radius: 999px; background: var(--bg-elev) !important; color: var(--link) !important; font-weight: 600; font-size: 13px; border: 1px solid var(--border) !important; } /* -------------------------------------------------------------- PAGINATION -------------------------------------------------------------- */ .nextprev { display: flex; justify-content: space-between; align-items: stretch; gap: 10px; padding: 16px 14px 28px; margin: 0; } .nextprev .separator { display: none; } .nextprev a { flex: 1 1 0; min-height: var(--tap); display: flex; align-items: center; justify-content: center; padding: 12px 16px; border-radius: var(--radius); font-weight: 600; font-size: 15px; text-decoration: none; background: var(--bg-elev) !important; color: var(--fg) !important; border: 1px solid var(--border); transition: transform 0.06s ease, background 0.15s ease; } .nextprev a:hover { background: var(--bg-hover) !important; } .nextprev a:active { transform: scale(0.98); } .nextprev a[rel*="next"] { background: var(--accent) !important; color: #fff !important; border-color: transparent; } .nextprev a[rel*="next"]:hover { background: var(--accent-2) !important; } /* -------------------------------------------------------------- FORMS / MENUS / MISC -------------------------------------------------------------- */ input[type=text], input[type=password], input[type=email], textarea { background: var(--bg-elev) !important; color: var(--fg) !important; border: 1px solid var(--border) !important; border-radius: var(--radius-sm) !important; padding: 8px 10px !important; font-family: var(--font-sans) !important; font-size: 14px !important; } .btn, button, .morelink a { background: var(--bg-elev) !important; color: var(--fg) !important; border: 1px solid var(--border) !important; border-radius: var(--radius-sm) !important; padding: 6px 12px !important; font-weight: 600; font-family: var(--font-sans) !important; } .menuarea, .panestack-title { background: transparent !important; border: 0 !important; color: var(--fg-dim) !important; padding: 8px 12px !important; } .dropdown, .drop-choices { background: var(--surface) !important; color: var(--fg) !important; border: 1px solid var(--border) !important; border-radius: var(--radius-sm) !important; box-shadow: var(--shadow); } table, th, td { background: var(--surface) !important; color: var(--fg) !important; border-color: var(--border) !important; } pre, code { background: var(--bg-elev) !important; color: var(--fg) !important; font-family: var(--font-mono) !important; } /* Dim Reddit's default vote arrows in dark mode (they're black PNGs) */ @media (prefers-color-scheme: dark) { .arrow:not(.upmod):not(.downmod) { filter: brightness(0.85) invert(0.9); } } a, button { touch-action: manipulation; } body > .content .link .midcol { width: auto !important; } body > .content .link .rank, .rank-spacer { width: 0 !important; } `); // Mobile viewport — Reddit's default doesn't include viewport-fit=cover, // which we want for safe-area handling on notched devices. if (document.head) { const meta = document.createElement("meta"); meta.setAttribute("name", "viewport"); meta.setAttribute( "content", "width=device-width, initial-scale=1, viewport-fit=cover", ); document.head.appendChild(meta); } // Nuke subreddit-specific stylesheet ASAP so it can't fight ours document.head?.querySelector("[ref=applied_subreddit_stylesheet]")?.remove(); } /* ============================================================================ * DOM TWEAKS * ---------------------------------------------------------------------------- * Things CSS alone can't do: rewriting nodes, injecting placeholders, * relocating elements within the post layout. * ========================================================================== */ function applyDOMTweaks() { // Belt-and-suspenders subreddit stylesheet removal (in case it loaded after us) document.querySelector('link[title="applied_subreddit_stylesheet"]')?.remove(); // Replace the subreddit logo/banner anchor with a plain "reddit" home link const headerAnchor = document.querySelector("#header-bottom-left a"); if (headerAnchor) { headerAnchor.outerHTML = `reddit`; } // Process every post on the page document.querySelectorAll(".link").forEach(processLink); // Pagination polish — shorten "view more" wording and add arrows const nextprev = document.querySelector(".nextprev"); if (nextprev) { // The first text node is often "view more:" — drop it const first = nextprev.childNodes[0]; if (first && first.textContent.trim().toLowerCase().startsWith("view more")) { first.remove(); } nextprev.querySelectorAll("a").forEach((a) => { const rel = (a.getAttribute("rel") || "").toLowerCase(); if (rel.includes("next")) a.textContent = "Next ›"; else if (rel.includes("prev")) a.textContent = "‹ Prev"; }); // Center the lone button if there's only one direction available if (nextprev.querySelectorAll("a").length === 1) { nextprev.style.justifyContent = "center"; nextprev.querySelector("a").style.flex = "0 1 280px"; } } } /** * Per-post tweaks: * 1. Move .expando out of .entry so it can become a full-width flex row * (flex order: thumbnail | entry | expando-on-new-line). * 2. Inject a synthetic .thumbnail
when the subreddit suppresses * thumbnails server-side (e.g. r/cars). This guarantees every post * has a tappable thumb area showing "TEXT", "LINK", "NSFW", etc. */ function processLink(link) { // --- 1. Promote .expando to be a direct child of .link --- const expando = link.querySelector(":scope > .entry > .expando"); if (expando) link.appendChild(expando); // --- 2. Inject synthetic thumbnail if missing --- if (!link.querySelector(":scope > .thumbnail")) { const thumb = document.createElement("div"); // Pick the right placeholder class so CSS ::after shows the right label if (link.classList.contains("over18")) { thumb.className = "thumbnail nsfw"; } else if (link.classList.contains("spoiler")) { thumb.className = "thumbnail spoiler"; } else if (link.classList.contains("self")) { thumb.className = "thumbnail self"; } else { thumb.className = "thumbnail default"; } link.prepend(thumb); } } /* ============================================================================ * INTERACTION HANDLERS * ---------------------------------------------------------------------------- * Delegated click handlers — attached once at script start, gated on * `isEnabled` so they no-op when the user disables the script live. * ========================================================================== */ // Tap a comment body to collapse/expand it (Apollo-style) document.addEventListener("click", (evt) => { if (!isEnabled) return; // Ignore clicks on interactive descendants if (evt.target.closest("a")) return; if (evt.target.classList.contains("expand")) return; if (evt.target.closest(".flat-list.buttons")) return; if (evt.target.closest(".usertext-edit")) return; if (evt.target.closest(".midcol")) return; const comment = evt.target.closest(".comment"); if (!comment) return; const expander = comment.querySelector(":scope > .entry .expand"); if (!expander) return; expander.click(); }); // Tap a thumbnail to toggle the post's expando. // On synthetic thumbnails (link posts with no expando) this is a no-op. document.addEventListener("click", (evt) => { if (!isEnabled) return; let thumb; if (evt.target.classList?.contains("thumbnail")) { thumb = evt.target; } else if (evt.target.parentElement?.classList?.contains("thumbnail")) { thumb = evt.target.parentElement; } if (!thumb) return; const link = thumb.closest(".link"); if (!link) return; const btn = link.querySelector(".expando-button"); evt.preventDefault(); evt.stopPropagation(); if (!btn) return; // synthetic thumb, nothing to expand btn.click(); }); /* ============================================================================ * MENU / TOGGLE * ---------------------------------------------------------------------------- * Toggling always reloads the page. Why: * - GM.addStyle injects a