Skip to content

Instantly share code, notes, and snippets.

@rebane2001
Last active November 14, 2025 02:10
Show Gist options
  • Select an option

  • Save rebane2001/138e8e64980036a8b7b63a528222b404 to your computer and use it in GitHub Desktop.

Select an option

Save rebane2001/138e8e64980036a8b7b63a528222b404 to your computer and use it in GitHub Desktop.
Twitter Card Mod (paste into console) // Note: Moved to https://github.com/rebane2001/TweetsAgainstHumanity/ (get the extension/userscript there)
twitterCardCount = 5;
function getNextTweets(count) {
const nextTweets = [...document.querySelectorAll("[data-testid='cellInnerDiv']:not(:has(>.HiddenTweet)) [data-testid='tweet']:not([data-twc-used])")].slice(0, count);
nextTweets.forEach(e => e.dataset.twcUsed = true);
return nextTweets;
}
function setStyle(styleText) {
let styleEl = document.querySelector(".twc-style");
if (!styleEl) {
styleEl = document.createElement('style');
styleEl.classList.add("twc-style");
document.head.appendChild(styleEl);
}
styleEl.textContent = styleText;
}
function cardNextTweets(count) {
const nextTweets = getNextTweets(count);
nextTweets.forEach((e,i) => {
e.dataset.twcCard = true;
e.setAttribute("style", (e.getAttribute("style") ?? "") + `; --card-offset: ${i/(count-1)}`);
e.addEventListener("click", (ev)=>{ev.preventDefault();pickCard(e)}, {capture:true,once:true});
});
}
function pickCard(cardEl) {
if (window.cardSnd) {
cardSnd.currentTime = 0;
cardSnd.play();
}
const otherCards = [...document.querySelectorAll("[data-twc-card]:not([data-twc-gone])")].filter(e=>e!==cardEl);
otherCards.forEach(e=>e.dataset.twcGone = true);
cardEl.dataset.twcPick = true;
cardNextTweets(twitterCardCount);
}
setStyle(`
html {
scrollbar-width: none;
}
[data-testid='cellInnerDiv']:not(:has([data-twc-used])) {
opacity: 0;
pointer-events: none;
}
[data-testid='cellInnerDiv']:has([data-twc-used]) {
position: fixed !important;
top: 0;
left: 0;
transform: none!important;
transition: display 1s allow-discrete;
&:has([data-twc-gone]) {
display: none;
}
&>*{border-bottom-color:#0000}
}
[data-twc-card] {
position: absolute;
background: #234;
top: 100dvh;
left: 50dvw;
width: 360px;
width: 480px;
width: 400px;
translate: calc(-50% + (var(--card-offset) - 0.5) * 2 * 512px) -80px;
rotate: calc((var(--card-offset) - 0.5) * 2 * 5deg);
border: 2px solid #123;
border-radius: 42px;
corner-shape: superellipse(1.5);
box-shadow: 2px 2px 8px #0004;
padding-right: 12px;
padding-left: 12px;
transition: translate 0.4s, rotate 0.4s, background 0.4s;
&:hover, &:has(:hover) {
background: #345;
translate: calc(-50% + (var(--card-offset) - 0.5) * 2 * 512px) max(calc(-100% - 32px), -256px);
rotate: calc((var(--card-offset) - 0.5) * 2 * 2deg);
}
&[data-twc-pick] {
rotate: 0deg;
/*width: 480px;*/
translate: -50% calc(-50dvh - 50% - 40px);
}
&[data-twc-gone] {
translate: calc(-50% + (var(--card-offset) - 0.5) * 2 * 512px) 8px;
}
[data-testid="tweetText"], [data-testid="tweetPhoto"] {
transition: filter 0.4s;
filter: none;
}
[role="link"]:has([data-testid="Tweet-User-Avatar"]) {
box-shadow: 4px 4px 16px inset #0128;
outline: none;
}
&:not([data-twc-pick]) {
[data-testid="tweetText"], [data-testid="tweetPhoto"] {
transition: none;
filter: blur(12px);
}
/*
[role="link"]:has([data-testid="Tweet-User-Avatar"]), [data-testid="tweetPhoto"] {
max-width: 280px;
overflow: clip;
}
*/
}
}
[data-testid=primaryColumn] {
border: none;
}
[aria-label="Home timeline"]>*:not(:has([data-testid=tweet])), header[role=banner], [data-testid=sidebarColumn], [data-testid=DMDrawer] {
opacity: 0;
pointer-events: none;
}
[aria-label="Home timeline"]>:first-child {
pointer-events: all;
opacity: 1;
position: fixed;
top: 0;
left: 50vw;
width: 50vw;
translate: -50% 0;
nav {
border-bottom-color: #0000;
}
[aria-selected="true"]>*>*>:not(:first-child) {
opacity: 0;
}
}
/*
[data-twc-card] > * {
width: 452px;
flex-grow: 0;
flex-shrink: 0;
}
*/
`);
cardNextTweets(twitterCardCount);
@sagarchaulagai
Copy link

Here is the userscript.

// ==UserScript==
// @name         Twitter Card Swiper
// @namespace    https://twitter.com/
// @version      1.0
// @description  Turns tweets into swipeable cards with visual effects
// @author       rebane2001
// @match        https://twitter.com/*
// @match        https://x.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const twitterCardCount = 5;

    function getNextTweets(count) {
        const nextTweets = [...document.querySelectorAll("[data-testid='cellInnerDiv']:not(:has(>.HiddenTweet)) [data-testid='tweet']:not([data-twc-used])")].slice(0, count);
        nextTweets.forEach(e => e.dataset.twcUsed = true);
        return nextTweets;
    }

    function setStyle(styleText) {
        let styleEl = document.querySelector(".twc-style");
        if (!styleEl) {
            styleEl = document.createElement('style');
            styleEl.classList.add("twc-style");
            document.head.appendChild(styleEl);
        }
        styleEl.textContent = styleText;
    }

    function cardNextTweets(count) {
        const nextTweets = getNextTweets(count);
        nextTweets.forEach((e,i) => {
            e.dataset.twcCard = true;
            e.setAttribute("style", (e.getAttribute("style") ?? "") + `; --card-offset: ${i/(count-1)}`);
            e.addEventListener("click", (ev)=>{ev.preventDefault();pickCard(e)}, {capture:true,once:true});
        });
    }

    function pickCard(cardEl) {
        if (window.cardSnd) {
            cardSnd.currentTime = 0;
            cardSnd.play();
        }
        const otherCards = [...document.querySelectorAll("[data-twc-card]:not([data-twc-gone])")].filter(e=>e!==cardEl);
        otherCards.forEach(e=>e.dataset.twcGone = true);
        cardEl.dataset.twcPick = true;
        cardNextTweets(twitterCardCount);
    }

    setStyle(`
html {
    scrollbar-width: none;
}

[data-testid='cellInnerDiv']:not(:has([data-twc-used])) {
    opacity: 0;
    pointer-events: none;
}

[data-testid='cellInnerDiv']:has([data-twc-used]) {
    position: fixed !important;
    top: 0;
    left: 0;
    transform: none!important;
    transition: display 1s allow-discrete;
}
[data-testid='cellInnerDiv']:has([data-twc-gone]) {
    display: none;
}
[data-testid='cellInnerDiv']:has([data-twc-used])>*{border-bottom-color:#0000}

[data-twc-card] {
    position: absolute;
    background: #234;
    top: 100dvh;
    left: 50dvw;
    width: 400px;
    translate: calc(-50% + (var(--card-offset) - 0.5) * 2 * 512px) -80px;
    rotate: calc((var(--card-offset) - 0.5) * 2 * 5deg);
    border: 2px solid #123;
    border-radius: 42px;
    box-shadow: 2px 2px 8px #0004;
    padding: 0 12px;
    transition: translate 0.4s, rotate 0.4s, background 0.4s;
}

[data-twc-card]:hover, [data-twc-card]:has(:hover) {
    background: #345;
    translate: calc(-50% + (var(--card-offset) - 0.5) * 2 * 512px) max(calc(-100% - 32px), -256px);
    rotate: calc((var(--card-offset) - 0.5) * 2 * 2deg);
}

[data-twc-card][data-twc-pick] {
    rotate: 0deg;
    translate: -50% calc(-50dvh - 50% - 40px);
}

[data-twc-card][data-twc-gone] {
    translate: calc(-50% + (var(--card-offset) - 0.5) * 2 * 512px) 8px;
}

[data-twc-card] [data-testid="tweetText"],
[data-twc-card] [data-testid="tweetPhoto"] {
    transition: filter 0.4s;
    filter: none;
}

[data-twc-card] [role="link"]:has([data-testid="Tweet-User-Avatar"]) {
    box-shadow: 4px 4px 16px inset #0128;
    outline: none;
}

[data-twc-card]:not([data-twc-pick]) [data-testid="tweetText"],
[data-twc-card]:not([data-twc-pick]) [data-testid="tweetPhoto"] {
    transition: none;
    filter: blur(12px);
}

[data-testid=primaryColumn] {
    border: none;
}

[aria-label="Home timeline"]>*:not(:has([data-testid=tweet])),
header[role=banner],
[data-testid=sidebarColumn],
[data-testid=DMDrawer] {
    opacity: 0;
    pointer-events: none;
}

[aria-label="Home timeline"]>:first-child {
    pointer-events: all;
    opacity: 1;
    position: fixed;
    top: 0;
    left: 50vw;
    width: 50vw;
    translate: -50% 0;
}

[aria-label="Home timeline"]>:first-child nav {
    border-bottom-color: #0000;
}

[aria-label="Home timeline"]>:first-child [aria-selected="true"]>*>*>:not(:first-child) {
    opacity: 0;
}
`);

    // Run after DOM loads
    const observer = new MutationObserver(() => {
        if (document.querySelector("[data-testid='tweet']")) {
            cardNextTweets(twitterCardCount);
            observer.disconnect();
        }
    });
    observer.observe(document.body, {childList: true, subtree: true});

})();

@cat8587
Copy link

cat8587 commented Nov 12, 2025

Support for light and lights out themes

// ==UserScript==
// @name         Twitter Card Swiper
// @namespace    https://twitter.com/
// @version      1.0.1
// @description  Turns tweets into swipeable cards with visual effects
// @author       rebane2001
// @match        https://twitter.com/*
// @match        https://x.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const twitterCardCount = 5;

    function getNextTweets(count) {
        const nextTweets = [...document.querySelectorAll("[data-testid='cellInnerDiv']:not(:has(>.HiddenTweet)) [data-testid='tweet']:not([data-twc-used])")].slice(0, count);
        nextTweets.forEach(e => e.dataset.twcUsed = true);
        return nextTweets;
    }

    function setStyle(styleText) {
        let styleEl = document.querySelector(".twc-style");
        if (!styleEl) {
            styleEl = document.createElement('style');
            styleEl.classList.add("twc-style");
            document.head.appendChild(styleEl);
        }
        styleEl.textContent = styleText;
    }

    function cardNextTweets(count) {
        const nextTweets = getNextTweets(count);
        nextTweets.forEach((e,i) => {
            e.dataset.twcCard = true;
            e.setAttribute("style", (e.getAttribute("style") ?? "") + `; --card-offset: ${i/(count-1)}`);
            e.addEventListener("click", (ev)=>{ev.preventDefault();pickCard(e)}, {capture:true,once:true});
        });
    }

    function pickCard(cardEl) {
        if (window.cardSnd) {
            cardSnd.currentTime = 0;
            cardSnd.play();
        }
        const otherCards = [...document.querySelectorAll("[data-twc-card]:not([data-twc-gone])")].filter(e=>e!==cardEl);
        otherCards.forEach(e=>e.dataset.twcGone = true);
        cardEl.dataset.twcPick = true;
        cardNextTweets(twitterCardCount);
    }

    setStyle(`
html {
    scrollbar-width: none;
}

[data-testid='cellInnerDiv']:not(:has([data-twc-used])) {
    opacity: 0;
    pointer-events: none;
}

[data-testid='cellInnerDiv']:has([data-twc-used]) {
    position: fixed !important;
    top: 0;
    left: 0;
    transform: none!important;
    transition: display 1s allow-discrete;
}
[data-testid='cellInnerDiv']:has([data-twc-gone]) {
    display: none;
}
[data-testid='cellInnerDiv']:has([data-twc-used])>*{border-bottom-color:#0000}

html.Default [data-twc-card] {
    --bg-color-1: #EEE;
    --bg-color-2: #FFF;
    --border-color: #888;
}

html.Dim [data-twc-card] {
    --bg-color-1: #234;
    --bg-color-2: #345;
    --border-color: #123;
}

html.LightsOut [data-twc-card] {
    --bg-color-1: #122;
    --bg-color-2: #233;
    --border-color: #234;
}

[data-twc-card] {
    position: absolute;
    background: var(--bg-color-1);
    top: 100dvh;
    left: 50dvw;
    width: 400px;
    translate: calc(-50% + (var(--card-offset) - 0.5) * 2 * 512px) -80px;
    rotate: calc((var(--card-offset) - 0.5) * 2 * 5deg);
    border: 2px solid var(--border-color);
    border-radius: 42px;
    box-shadow: 2px 2px 8px #0004;
    padding: 0 12px;
    transition: translate 0.4s, rotate 0.4s, background 0.4s;
}

[data-twc-card]:hover, [data-twc-card]:has(:hover) {
    background: var(--bg-color-2);
    translate: calc(-50% + (var(--card-offset) - 0.5) * 2 * 512px) max(calc(-100% - 32px), -256px);
    rotate: calc((var(--card-offset) - 0.5) * 2 * 2deg);
}

[data-twc-card][data-twc-pick] {
    rotate: 0deg;
    translate: -50% calc(-50dvh - 50% - 40px);
}

[data-twc-card][data-twc-gone] {
    translate: calc(-50% + (var(--card-offset) - 0.5) * 2 * 512px) 8px;
}

[data-twc-card] [data-testid="tweetText"],
[data-twc-card] [data-testid="tweetPhoto"] {
    transition: filter 0.4s;
    filter: none;
}

[data-twc-card] [role="link"]:has([data-testid="Tweet-User-Avatar"]) {
    box-shadow: 4px 4px 16px inset #0128;
    outline: none;
}

[data-twc-card]:not([data-twc-pick]) [data-testid="tweetText"],
[data-twc-card]:not([data-twc-pick]) [data-testid="tweetPhoto"] {
    transition: none;
    filter: blur(12px);
}

[data-testid=primaryColumn] {
    border: none;
}

[aria-label="Home timeline"]>*:not(:has([data-testid=tweet])),
header[role=banner],
[data-testid=sidebarColumn],
[data-testid=DMDrawer] {
    opacity: 0;
    pointer-events: none;
}

[aria-label="Home timeline"]>:first-child {
    pointer-events: all;
    opacity: 1;
    position: fixed;
    top: 0;
    left: 50vw;
    width: 50vw;
    translate: -50% 0;
}

[aria-label="Home timeline"]>:first-child nav {
    border-bottom-color: #0000;
}

[aria-label="Home timeline"]>:first-child [aria-selected="true"]>*>*>:not(:first-child) {
    opacity: 0;
}
`);

    // Run after DOM loads
    const observer = new MutationObserver(() => {
        if (document.querySelector("[data-testid='tweet']")) {
            cardNextTweets(twitterCardCount);
            observer.disconnect();
        }
    });
    observer.observe(document.body, {childList: true, subtree: true});

})();

@rebane2001
Copy link
Author

Moved to https://github.com/rebane2001/TweetsAgainstHumanity/ (get the extension/userscript there)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment