Skip to content

Instantly share code, notes, and snippets.

@tatelax
Created June 28, 2025 03:44
Show Gist options
  • Select an option

  • Save tatelax/287d10935bc34693228426a65552945d to your computer and use it in GitHub Desktop.

Select an option

Save tatelax/287d10935bc34693228426a65552945d to your computer and use it in GitHub Desktop.
fxtwitter share button for X
// ==UserScript==
// @name fxtwitter Share Button for X (Styled + Snackbar + Hover)
// @namespace http://tampermonkey.net/
// @version 2.0
// @description Adds a compact 'fxtwitter' button next to Share on X tweets with hover effect and clipboard notification. Clean layout, Safari-safe.
// @author You
// @match https://twitter.com/*
// @match https://x.com/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function () {
"use strict";
const LABEL = "FX";
const TOOLTIP = "Copy fxtwitter link";
const TOAST_ID = "fxtwitter-toast";
function showToast(message) {
let toast = document.getElementById(TOAST_ID);
if (!toast) {
toast = document.createElement("div");
toast.id = TOAST_ID;
toast.style.position = "fixed";
toast.style.bottom = "24px";
toast.style.left = "50%";
toast.style.transform = "translateX(-50%)";
toast.style.background = "#333";
toast.style.color = "#fff";
toast.style.padding = "8px 16px";
toast.style.borderRadius = "999px";
toast.style.fontSize = "14px";
toast.style.zIndex = "9999";
toast.style.opacity = "0";
toast.style.transition = "opacity 0.3s ease";
toast.style.fontFamily = "inherit";
document.body.appendChild(toast);
}
toast.textContent = message;
toast.style.opacity = "1";
setTimeout(() => {
toast.style.opacity = "0";
}, 1800);
}
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showToast("Copied fxtwitter link!");
} catch (err) {
console.error("[fxtwitter] Clipboard error:", err);
showToast("Failed to copy");
}
}
function createFXButton(tweet) {
if (tweet.querySelector(".fxtwitter-button")) return;
const shareBtn = [
...tweet.querySelectorAll('button[aria-label*="Share post"]'),
].pop();
if (!shareBtn) return;
const buttonGroup = shareBtn.closest('div[role="group"]');
if (!buttonGroup || !(buttonGroup instanceof Node)) return;
const fxButton = document.createElement("button");
fxButton.className = shareBtn.className + " fxtwitter-button";
fxButton.setAttribute("role", "button");
fxButton.setAttribute("tabindex", "0");
fxButton.setAttribute("aria-label", TOOLTIP);
fxButton.title = TOOLTIP;
fxButton.type = "button";
// Base button style
fxButton.style.all = "unset";
fxButton.style.cursor = "pointer";
fxButton.style.display = "flex";
fxButton.style.alignItems = "center";
fxButton.style.justifyContent = "center";
fxButton.style.height = "24px";
fxButton.style.borderRadius = "9999px";
fxButton.style.transition = "background-color 0.2s ease";
// Light/dark theme hover background
const hoverBgLight = "rgba(0, 0, 0, 0.05)";
const hoverBgDark = "rgba(255, 255, 255, 0.1)";
const isDarkMode = () => {
const bg = getComputedStyle(document.body).backgroundColor;
return !bg || bg !== "rgb(255, 255, 255)";
};
fxButton.addEventListener("mouseenter", () => {
fxButton.style.backgroundColor = isDarkMode()
? hoverBgDark
: hoverBgLight;
});
fxButton.addEventListener("mouseleave", () => {
fxButton.style.backgroundColor = "transparent";
});
// Inner wrapper and label
const wrapper = document.createElement("div");
wrapper.style.display = "flex";
wrapper.style.alignItems = "center";
wrapper.style.justifyContent = "center";
wrapper.style.height = "100%";
wrapper.style.padding = "0 4px";
const labelDiv = document.createElement("div");
labelDiv.setAttribute("dir", "ltr");
labelDiv.style.fontSize = "13px";
labelDiv.style.fontWeight = "500";
labelDiv.style.lineHeight = "1";
labelDiv.textContent = LABEL;
wrapper.appendChild(labelDiv);
fxButton.appendChild(wrapper);
fxButton.addEventListener("click", (e) => {
e.stopPropagation();
const link = tweet.querySelector('a[href*="/status/"]');
if (!link) return;
const fullUrl = new URL(link.href, window.location.origin);
const fxUrl = fullUrl.href.replace(
/^(https:\/\/)(x|twitter)\.com/,
"$1fxtwitter.com"
);
copyToClipboard(fxUrl);
});
buttonGroup.appendChild(fxButton);
}
function scanAndInject() {
document
.querySelectorAll('article[data-testid="tweet"]')
.forEach(createFXButton);
}
window.addEventListener("load", () => {
setTimeout(() => {
scanAndInject();
const observer = new MutationObserver(scanAndInject);
observer.observe(document.body, { childList: true, subtree: true });
}, 1000);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment