From 38350ee3b753494492ec28a9ff146e9ff4f46626 Mon Sep 17 00:00:00 2001 From: Darius Kazemi Date: Tue, 18 Nov 2025 20:59:08 -0800 Subject: [PATCH] Re-implement "no alt" badges This work is required to make sure we maintain an important Hometown accessibility feature, originally implemented here: https://github.com/hometown-fork/hometown/pull/1261 --- .../mastodon/components/media_gallery.jsx | 3 + .../mastodon/components/no_alt_text_badge.tsx | 67 +++++++++++++++++++ .../mastodon/features/audio/index.jsx | 5 +- .../mastodon/features/video/index.jsx | 5 +- .../styles/mastodon/components.scss | 21 ++++++ 5 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 app/javascript/mastodon/components/no_alt_text_badge.tsx diff --git a/app/javascript/mastodon/components/media_gallery.jsx b/app/javascript/mastodon/components/media_gallery.jsx index a8d425a83..cf21881f7 100644 --- a/app/javascript/mastodon/components/media_gallery.jsx +++ b/app/javascript/mastodon/components/media_gallery.jsx @@ -11,6 +11,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import { debounce } from 'lodash'; import { AltTextBadge } from 'mastodon/components/alt_text_badge'; +import { NoAltTextBadge } from 'mastodon/components/no_alt_text_badge'; import { Blurhash } from 'mastodon/components/blurhash'; import { formatTime } from 'mastodon/features/video'; @@ -100,6 +101,8 @@ class Item extends PureComponent { const hasMediaDescription = attachment.get('description')?.length > 0; if (hasMediaDescription) { badges.push(); + } else { + badges.push(); } const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); diff --git a/app/javascript/mastodon/components/no_alt_text_badge.tsx b/app/javascript/mastodon/components/no_alt_text_badge.tsx new file mode 100644 index 000000000..2c80cac2c --- /dev/null +++ b/app/javascript/mastodon/components/no_alt_text_badge.tsx @@ -0,0 +1,67 @@ +import { useState, useCallback, useRef } from 'react'; + +import { FormattedMessage } from 'react-intl'; + +import Overlay from 'react-overlays/Overlay'; +import type { + OffsetValue, + UsePopperOptions, +} from 'react-overlays/esm/usePopper'; + +import { Icon } from 'mastodon/components/icon'; +import WarningIcon from '@/material-icons/400-24px/warning.svg?react'; + +const offset = [0, 4] as OffsetValue; +const popperConfig = { strategy: 'fixed' } as UsePopperOptions; + +export const NoAltTextBadge: React.FC<{}> = () => { + const anchorRef = useRef(null); + const [open, setOpen] = useState(false); + + const handleClick = useCallback(() => { + setOpen((v) => !v); + }, [setOpen]); + + const handleClose = useCallback(() => { + setOpen(false); + }, [setOpen]); + + return ( + <> + + + + {({ props }) => ( +
+
+

+ +

+
+
+ )} +
+ + ); +}; diff --git a/app/javascript/mastodon/features/audio/index.jsx b/app/javascript/mastodon/features/audio/index.jsx index 22c992a82..f51a2edc1 100644 --- a/app/javascript/mastodon/features/audio/index.jsx +++ b/app/javascript/mastodon/features/audio/index.jsx @@ -17,6 +17,8 @@ import VolumeOffIcon from '@/material-icons/400-24px/volume_off-fill.svg?react'; import VolumeUpIcon from '@/material-icons/400-24px/volume_up-fill.svg?react'; import { Icon } from 'mastodon/components/icon'; import { formatTime, getPointerPosition, fileNameFromURL } from 'mastodon/features/video'; +import { AltTextBadge } from 'mastodon/components/alt_text_badge'; +import { NoAltTextBadge } from 'mastodon/components/no_alt_text_badge'; import { Blurhash } from '../../components/blurhash'; import { displayMedia, useBlurhash } from '../../initial_state'; @@ -582,7 +584,8 @@ class Audio extends PureComponent {
- {!alt && } + {alt && } + {!alt && } {!editable && } diff --git a/app/javascript/mastodon/features/video/index.jsx b/app/javascript/mastodon/features/video/index.jsx index bbab84de1..2a0fefea5 100644 --- a/app/javascript/mastodon/features/video/index.jsx +++ b/app/javascript/mastodon/features/video/index.jsx @@ -20,6 +20,8 @@ import VolumeUpIcon from '@/material-icons/400-24px/volume_up-fill.svg?react'; import { Blurhash } from 'mastodon/components/blurhash'; import { Icon } from 'mastodon/components/icon'; import { playerSettings } from 'mastodon/settings'; +import { AltTextBadge } from 'mastodon/components/alt_text_badge'; +import { NoAltTextBadge } from 'mastodon/components/no_alt_text_badge'; import { displayMedia, useBlurhash } from '../../initial_state'; import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen'; @@ -647,7 +649,8 @@ class Video extends PureComponent {
- {!alt && } + {alt && } + {!alt && } {(!onCloseVideo && !editable && !fullscreen && !this.props.alwaysVisible) && } {(!fullscreen && onOpenVideo) && } {onCloseVideo && } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 4ed05ead3..e1613575f 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -7018,6 +7018,27 @@ a.status-card { } } +.media-gallery__no-alt__label { + display: block; + text-align: center; + color: $white; + border: 0; + background: rgba($black, 0.65); + backdrop-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%); + padding: 2px 4px 0px; + border-radius: 4px; + font-size: 12px; + font-weight: 700; + z-index: 1; + line-height: 20px; + cursor: pointer; + pointer-events: auto; + + &--non-interactive { + pointer-events: none; + } +} + .media-gallery__alt__popover { background: rgba($black, 0.65); backdrop-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%);