// ==UserScript== // @name Wintage — Win95 Dark Golden Vintage Theme // @namespace https://github.com/vacterro/Wintage // @version 1.1.4 // @description Dark Golden Windows 95 vintage theme for every site: pixel-sharp 3D bevels, zero rounded corners, zero animations, site hover-highlighting fully disabled, gray surfaces remapped to warm browns, Verdana forced everywhere. // @author vacterro // @license MIT // @homepageURL https://github.com/vacterro/Wintage // @supportURL https://github.com/vacterro/Wintage/issues // @updateURL https://raw.githubusercontent.com/vacterro/Wintage/main/wintage.user.js // @downloadURL https://raw.githubusercontent.com/vacterro/Wintage/main/wintage.user.js // @match *://*/* // @include about:blank // @run-at document-start // @grant none // ==/UserScript== (function () { 'use strict'; // ─── AUTH GUARD ────────────────────────────────────────────────────────────── const AUTH = [/oauth/i, /captcha/i, /accounts\.google/i, /login\.microsoft/i, /paypal/i, /stripe/i, /bank/i]; if (AUTH.some(r => r.test(location.href))) return; // ─── IMMEDIATE DARK GOLDEN BACKGROUND ──────────────────────────────────────── document.documentElement.style.setProperty('background-color', '#1A0F05', 'important'); document.documentElement.style.setProperty('color', '#D4B87A', 'important'); document.documentElement.setAttribute('data-w95-dark', '1'); // ─── VINTAGE TOKENS (from /vintage SKILL.md) ──────────────────────────────── // background #1A0F05 | backgroundSoft #1E1408 | surface #2A1C0A | surfaceRaised #362812 // surfaceAlt #3A2A15 | borderDark #0E0803 | borderHighlight #C0A060 | borderMuted #4A3820 // textPrimary #D4B87A | textSecondary #B09558 | textMuted #7A6838 | compareBack #0F0A04 // Verdana forced 100% everywhere. Verdana_m1 = locally installed modified Verdana. const FONT = 'Verdana_m1, Verdana, Tahoma, "MS Sans Serif", sans-serif'; // ─── STRUCTURAL BEVEL CONSTANTS (PHYSICAL BORDERS + 1 INSET) ─────────────── const B_OUTER = 'border-top: 1px solid #C0A060 !important; border-left: 1px solid #C0A060 !important; border-bottom: 1px solid #0E0803 !important; border-right: 1px solid #0E0803 !important; box-shadow: inset 1px 1px 0 #7A6838, inset -1px -1px 0 #1E1408 !important;'; const B_INNER = 'border-top: 1px solid #0E0803 !important; border-left: 1px solid #0E0803 !important; border-bottom: 1px solid #C0A060 !important; border-right: 1px solid #C0A060 !important; box-shadow: inset 1px 1px 0 #1E1408, inset -1px -1px 0 #7A6838 !important;'; const B_SUNK = 'border-top: 1px solid #4A3820 !important; border-left: 1px solid #4A3820 !important; border-bottom: 1px solid #C0A060 !important; border-right: 1px solid #C0A060 !important; box-shadow: inset 1px 1px 0 #0E0803 !important;'; // ═══════════════════════════════════════════════════════════════════════════════ // GLOBAL CSS — v29.0 // ═══════════════════════════════════════════════════════════════════════════════ const GLOBAL_CSS = ` :root { color-scheme: dark !important; --w95-canvas: #1A0F05; --w95-soft: #1E1408; --w95-surface: #2A1C0A; --w95-raised: #362812; --w95-alt: #3A2A15; --w95-hi: #C0A060; --w95-dk: #0E0803; --w95-muted: #4A3820; --w95-text: #D4B87A; --w95-text2: #B09558; --w95-dim: #7A6838; --w95-accent: #9DD9F9; --radius: 0px; --radius-none: 0px; --radius-2xs: 0px; --radius-xs: 0px; --radius-sm: 0px; --radius-md: 0px; --radius-lg: 0px; --radius-xl: 0px; --radius-2xl: 0px; --radius-full: 0px; --radius-round: 0px; --radius-pill: 0px; --radius-circle: 0px; --border-radius: 0px; --border-radius-full: 0px; --border-radius-pill: 0px; --bs-border-radius: 0px; --bs-border-radius-pill: 0px; --mdc-shape-small: 0px; --md-sys-shape-corner-full: 0px; --shreddit-border-radius: 0px; --post-action-border-radius: 0px; --yt-border-radius: 0px; --ytd-searchbox-border-radius: 0px; } /* 🚨 STRICT RADIUS KILLER, NO GLOBAL BOX-SIZING TO PREVENT FLEX BREAKS 🚨 */ * { border-radius: 0 !important; } /* 🚨 MOTION IS MOSTLY FORBIDDEN (SKILL.md), WITH A NARROW CARVE-OUT 🚨 transition-duration is 0.001s, NOT "transition: none" — a none/zero transition never fires transitionend, and spoiler/accordion/modal JS commonly waits for that event to set height:auto and release scroll locks. transition:none left forum spoilers stuck mid-open with broken page scroll (aechat.ru report). 1ms still reads as instant but the event pipeline keeps working. transition-property is ONLY height/max-height/min-height — the exact properties collapse/spoiler code actually toggles (max-height:0->N is the standard accordion trick, since height:auto itself doesn't transition). v1.0.7–v1.1.0 forced a much longer list here (opacity, transform, width, margin, padding, top/left/right/bottom, flex-basis, grid-template-*, and at one point animation-duration/visibility too) on the theory that other component libraries might need it too. Confirmed via live reproduction on chat.qwen.ai (v1.1.1): Ant Design's rc-trigger positions dropdown panels via transitioned top/left/width, and forcing those to ~0ms made the panel's final position/size unreliable — document.elementFromPoint at the menu item never resolved to the item itself, so the "+" and thinking-mode dropdowns opened visually but were NOT clickable. Isolated with a live binary search (each candidate property list re-tested against a real dispatched click + elementFromPoint hit-test, not just visual inspection) down to this minimal set, which fixes Qwen and covers every confirmed height-based spoiler case. Do not widen this list again without a live repro proving the wider set is both necessary AND doesn't break a real interactive component — "might help some other site" is not sufficient justification — this exact reasoning broke two unrelated real sites already (see button-descendant transform note above HOVER-HIGHLIGHT KILLER, and this one). animation-duration is deliberately NOT forced — CSS-keyframe-animation state machines (rc-motion's ant-slide-up-appear/-active classes, driven by animationend) are just as fragile against near-zero durations as the transition-based case was. */ *, *::before, *::after { transition-property: height, max-height, min-height !important; transition-duration: 0.001s !important; transition-delay: 0s !important; } html { scroll-behavior: auto !important; } /* 🚨 HOVER-HIGHLIGHT KILLER — CSS FREEZE LAYER (v29.1) 🚨 Sites' own :hover paints (white flashbang rows, gray tint blocks) are killed two ways: (1) JS surgery strips paint props from every readable :hover rule; (2) this rule covers unreadable cross-origin sheets — while an element is hovered, any paint-property change rides a 99999s step-end transition, so it visually never happens; on unhover the global 0.001s duration snaps it back instantly. Functional hover behavior (display/visibility/opacity/transform for dropdown menus) is deliberately NOT in the property list, so hover-opened menus keep working. Our own themed controls are excluded so buttons/links keep their instant bevel feedback, and :active/:focus are excluded so click feedback on focusable elements is never frozen. */ :root body *:hover:not(button):not(a):not(input):not(select):not(textarea):not(summary):not(.btn):not([class~="button" i]):not([class~="btn" i]):not([role="button"]):not(:active):not(:focus), :root body *:hover::before, :root body *:hover::after { transition-property: background-color, background-image, background-position, box-shadow, filter, backdrop-filter, color, border-color, outline-color, text-decoration-color, text-shadow !important; transition-duration: 99999s !important; transition-delay: 0s !important; transition-timing-function: step-end !important; } html { background-color: #1A0F05 !important; color: #D4B87A !important; } body { background-color: #1E1408 !important; color: #D4B87A !important; margin: 0 !important; padding: 0 !important; } /* 🚨 VERDANA 100% FORCED EVERYWHERE — inputs/textareas included 🚨 Only true icon-font carriers are excluded (glyphs would turn into letters). */ *:not(svg):not(path):not(i):not([class*="icon" i]):not([class*="fa-" i]):not([class*="symbols" i]):not([class*="glyph" i]):not([class*="mdi" i]):not([class*="bi-" i]) { font-family: ${FONT} !important; -webkit-font-smoothing: none !important; font-smooth: never !important; } input, textarea, select, option, button, code, pre, kbd, samp, tt, [class*="code" i], [class*="mono" i] { font-family: ${FONT} !important; } a, a:visited { color: #9DD9F9 !important; text-decoration: none !important; background-color: transparent !important; } foreignObject { mask: none !important; -webkit-mask: none !important; } rect { rx: 0 !important; ry: 0 !important; } svg { background: transparent !important; } [class*="avatar" i]:not(svg):not(path), img { clip-path: none !important; } img, video, canvas, iframe, picture { max-width: 100% !important; } /* Ad-network iframes (Reddit/most sites' "Advertisement" slots) flash white in the letterbox before their creative paints. Cross-origin iframe CONTENTS are fundamentally outside any userscript's reach — browser security sandbox, not fixable — but the iframe ELEMENT's own background, painted by the parent page, is ours, and covers that load-flash moment. Scoped to known ad-serving hosts ONLY: forcing this on every iframe would also hit deliberately-transparent overlay iframes (chat widgets, cookie banners, payment forms like Stripe/Intercom/Crisp commonly cover large or full-page areas with a transparent iframe so the page shows through except their own widget) — an opaque background on those paints a solid dark rectangle over otherwise normal pages, a worse bug than the one it fixes. */ iframe[src*="doubleclick.net" i], iframe[src*="googlesyndication.com" i], iframe[src*="google.com/ads" i], iframe[id*="google_ads_iframe" i], iframe[id*="gpt_unit" i], iframe[src*="adservice.google" i], iframe[src*="amazon-adsystem.com" i], iframe[src*="taboola.com" i], iframe[src*="outbrain.com" i] { background-color: #1E1408 !important; } main, section, article, aside, footer, .container, .wrapper, .main, #main, #wrapper { background-color: transparent !important; } ::selection { background-color: #4A3820 !important; color: #D4B87A !important; } header, nav, [role="navigation"], [role="banner"], [class*="header" i]:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not([class*="heading" i]), [class*="navbar" i], [class*="nav-bar" i], [class*="topbar" i], [class*="top-bar" i], [class*="toolbar" i]:not([class*="ytp" i]), [id*="header" i]:not(h1):not(h2):not(h3), [id*="navbar" i], [id*="topbar" i] { background-color: #2A1C0A !important; background-image: none !important; color: #D4B87A !important; } /* 🚨 3D BEVELED BUTTONS 🚨 Coverage beyond real