├── 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 |
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 |
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 |
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
--------------------------------------------------------------------------------