├── README.md ├── add_alt_text_overlay.css ├── dim_media_without_alt_text.css ├── dim_tweets_without_alt_text.js ├── screenshots ├── dialog_with_alt_text.png └── dialog_without_alt_text.png ├── show_alt_text_in_dialog.js ├── thread_with_dimmed_tweet.png ├── tweet_with_alt_text_shown.png └── tweet_without_alt_text_with_dimmed_image.png /README.md: -------------------------------------------------------------------------------- 1 | # highlight-twitter-alt-text 2 | 3 | This repo has some tools I use to highlight the (in)accessibility of my Twitter timeline. 4 | 5 | 6 | 7 | ## Motivation 8 | 9 | Twitter supports [adding alt text to images](https://help.twitter.com/en/using-twitter/picture-descriptions), so they're accessible to more people -- in particular, people who are blind or low-vision, who often use screen readers. 10 | 11 | I try to remember to add alt text to every image that I post, and I'm trying not to retweet/quote tweet images that don't have alt text -- but Twitter doesn't make this easy. 12 | The alt text isn't shown if you're not using a screen reader, so at a glance it's not obvious if a particular image does or doesn't have alt text. 13 | 14 | This repo contains a couple of CSS and JavaScript snippets I use to make alt text more visible, or hide tweets that don't include it. 15 | 16 | 17 | 18 | ## Inspiration 19 | 20 | I didn't come up with these ideas -- I saw some tweets from [@lunasorcery](https://twitter.com/lunasorcery) and [@thingskatedid](https://twitter.com/thingskatedid) that put the idea in my head, and I created this GitHub repo so I had something I could easily link "here's how to highlight alt text on Twitter". 21 | 22 | 23 | 24 | ## Tools 25 | 26 | ### Show alt text on hover with CSS 27 | 28 | I have a couple of CSS snippets that will highlight the presence/absence of alt text. 29 | One adds an overlay that shows the alt text on the image when you hover over the image, the other dims images that don't have any: 30 | 31 | 32 | 33 | 37 | 41 | 42 |
34 | Screenshot of a tweet with an image. At the top of the image, there's a black overlay with white text showing the alt text for the image. 35 | A tweet using add_alt_text_overlay.css 36 | 38 | Screenshot of a tweet with an image.  The image has been heavily dimmed, so it's mostly white. 39 | A tweet using dim_media_without_alt_text.css 40 |
43 | 44 | I include both of these snippets in my browser's custom stylesheet on my desktop, so it applies everywhere on Twitter. 45 | 46 | ### Hide tweets that don't have alt text 47 | 48 | If you want to completely ignore tweets that don't include alt text, look at [`dim_tweets_without_alt_text.js`](dim_tweets_without_alt_text.js). 49 | This includes a function that will hide all the inaccessible tweets on a timeline, so you can scroll past them quickly. 50 | 51 | Here's what that looks like in practice: 52 | 53 | A thread with two tweets. Both tweets have a photo of a black cat; the first tweet is shown with an alt text overlay, the second is heavily dimmed, so it's practically illegible. 54 | 55 | This JS is a prototype; I haven't added it to my browser yet. 56 | I wrote it after [a suggestion from Kate](https://twitter.com/thingskatedid/status/1371990357441835013), when I thought of a neat trick for how you'd do this. 57 | You need to run it in a loop, because new tweets get added to the page as you scroll, and you want the function to pick them up. 58 | 59 | ### Show alt text in a dialog box 60 | 61 | I mostly use Twitter on my phone, which doesn't have hover states or a particularly easy way to inject custom CSS -- but it does support [bookmarklets](https://en.wikipedia.org/wiki/Bookmarklet). 62 | I've written [a bookmarklet](show_alt_text_in_dialog.js) which displays a dialog box showing me the alt text in any images it can find: 63 | 64 | 65 | 66 | 70 | 74 | 75 |
67 | Screenshot of mobile web browser with a dialog box saying 'Alt text. Image 1: (no alt text). Image 2: (no alt text). Image 3: (no alt text). 68 | A tweet with no alt text using show_alt_text_in_dialog.js 69 | 71 | Screenshot of mobile web browser with a dialog box saying 'Alt text. Image 1 … Image 2 … Image 3' followed by several sentences of description. There's a scroll bar showing the alert continues off the bottom. 72 | A tweet with alt text using show_alt_text_in_dialog.js 73 |
76 | 77 | I designed this bookmarklet to work in Safari on iOS, but it should be possible to make work in other browsers. 78 | 79 | There's a bug where sometimes the alert doesn't fire -- reloading the page seems to fix it. 80 | -------------------------------------------------------------------------------- /add_alt_text_overlay.css: -------------------------------------------------------------------------------- 1 | /** This snippet adds an overlay to images on Twitter, so the alt text 2 | * (if there is any) is shown at the top of an image. 3 | */ 4 | div[data-testid="tweetPhoto"]:not([aria-label="Image"]), 5 | div[data-testid="previewInterstitial"]:not([aria-label="Embedded video"]) { 6 | margin: 0 !important; 7 | } 8 | 9 | /** If you want to see alt text all the time, remove :hover from these two rules */ 10 | div[data-testid="tweetPhoto"]:hover:not([aria-label="Image"]):after, 11 | div[data-testid="previewInterstitial"]:hover:not([aria-label="Embedded video"]):after { 12 | content: attr(aria-label); 13 | background: rgba(0, 0, 0, .75); 14 | color: #fff; 15 | padding: 10px; 16 | font-family: sans-serif; 17 | line-height: 130%; 18 | z-index: 1; 19 | } 20 | -------------------------------------------------------------------------------- /dim_media_without_alt_text.css: -------------------------------------------------------------------------------- 1 | /** This snippet dims images on Twitter if they don't have any alt text. */ 2 | div[aria-label="Image"], 3 | div[aria-label="Embedded video"] { 4 | border: 20px dashed #aaa; 5 | width: 100%; 6 | height: 100%; 7 | background: #fff; 8 | opacity: 0.15; 9 | margin: 0 !important; 10 | } 11 | -------------------------------------------------------------------------------- /dim_tweets_without_alt_text.js: -------------------------------------------------------------------------------- 1 | // This function 2 | 3 | function hideInaccessibleTweets() { 4 | var articles = document.getElementsByTagName("article"); 5 | for (i = 0; i < articles.length; i++) { 6 | var art = articles[i]; 7 | var isMissingAltText = false; 8 | 9 | // Is this a retweet? Crude but simple heuristic. 10 | // 11 | // Note: this is probably expensive; don't use it if you don't need it. 12 | // You might use this if you only wanted to dim retweets. 13 | // var isRetweet = art.outerHTML.includes("Retweeted"); 14 | // console.log(isRetweet) 15 | 16 | var images = art.getElementsByTagName("img"); 17 | for (j = 0; j < images.length; j++) { 18 | var img = images[j]; 19 | 20 | // Emoji are rendered as custom images, which always get alt text 21 | // and aren't interesting for this script. e.g. 22 | // 23 | // 🌟 24 | // 25 | // Skip them. 26 | if (img.getAttribute("src").startsWith("https://abs-0.twimg.com/emoji")) { 27 | continue 28 | } 29 | 30 | // Profile pictures are rendered as images without alt text. 31 | // If we hid those, we wouldn't see any tweets! e.g. 32 | // 33 | // 34 | // 35 | // Skip them. 36 | if (img.getAttribute("src").startsWith("https://pbs.twimg.com/profile_images")) { 37 | continue 38 | } 39 | 40 | // JavaScript types are finnicky; I don't know if I've caught all 41 | // of the "undefined" alt text values here. 42 | var altText = img.getAttribute("alt") 43 | 44 | if (altText === "Image") { 45 | isMissingAltText = true 46 | break 47 | } 48 | } 49 | 50 | // Change the style of the tweet to reflect the fact that it 51 | // includes inaccessible media. Here I'm dimming the opacity -- 52 | // you could also add a red border, or a warning, or hide it entirely. 53 | if (isMissingAltText) { 54 | art.style.opacity = 0.1 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /screenshots/dialog_with_alt_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwlchan/highlight-twitter-alt-text/45c704aa9a39f49d36e68946abb57777e82bfdc5/screenshots/dialog_with_alt_text.png -------------------------------------------------------------------------------- /screenshots/dialog_without_alt_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwlchan/highlight-twitter-alt-text/45c704aa9a39f49d36e68946abb57777e82bfdc5/screenshots/dialog_without_alt_text.png -------------------------------------------------------------------------------- /show_alt_text_in_dialog.js: -------------------------------------------------------------------------------- 1 | javascript:divs = document.querySelectorAll('div[data-testid="tweetPhoto"], div[data-testid="previewInterstitial"]'); 2 | 3 | lines = ["Alt text:"]; 4 | 5 | for (i = 1; i <= divs.length; i++) { 6 | altText = divs[i - 1].attributes["aria-label"].value; 7 | 8 | if (altText === "Image") { 9 | altText = "(no alt text)"; 10 | } 11 | 12 | lines.push("Image " + i + ":\n" + altText); 13 | }; 14 | 15 | alert(lines.join("\n\n")); 16 | -------------------------------------------------------------------------------- /thread_with_dimmed_tweet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwlchan/highlight-twitter-alt-text/45c704aa9a39f49d36e68946abb57777e82bfdc5/thread_with_dimmed_tweet.png -------------------------------------------------------------------------------- /tweet_with_alt_text_shown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwlchan/highlight-twitter-alt-text/45c704aa9a39f49d36e68946abb57777e82bfdc5/tweet_with_alt_text_shown.png -------------------------------------------------------------------------------- /tweet_without_alt_text_with_dimmed_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwlchan/highlight-twitter-alt-text/45c704aa9a39f49d36e68946abb57777e82bfdc5/tweet_without_alt_text_with_dimmed_image.png --------------------------------------------------------------------------------