diff --git a/app/javascript/flavours/glitch/components/alt_text_badge.tsx b/app/javascript/flavours/glitch/components/alt_text_badge/index.tsx similarity index 54% rename from app/javascript/flavours/glitch/components/alt_text_badge.tsx rename to app/javascript/flavours/glitch/components/alt_text_badge/index.tsx index 93d8d8b753..586dc2415f 100644 --- a/app/javascript/flavours/glitch/components/alt_text_badge.tsx +++ b/app/javascript/flavours/glitch/components/alt_text_badge/index.tsx @@ -1,6 +1,6 @@ -import { useState, useCallback, useRef, useId } from 'react'; +import { useState, useCallback, useRef, useId, Fragment } from 'react'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, useIntl } from 'react-intl'; import type { OffsetValue, @@ -8,24 +8,37 @@ import type { } from 'react-overlays/esm/usePopper'; import Overlay from 'react-overlays/Overlay'; +import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import { useSelectableClick } from 'flavours/glitch/hooks/useSelectableClick'; +import { IconButton } from '../icon_button'; + +import classes from './styles.module.scss'; + const offset = [0, 4] as OffsetValue; const popperConfig = { strategy: 'fixed' } as UsePopperOptions; export const AltTextBadge: React.FC<{ description: string }> = ({ description, }) => { - const accessibilityId = useId(); - const anchorRef = useRef(null); + const intl = useIntl(); + const uniqueId = useId(); + const popoverId = `${uniqueId}-popover`; + const titleId = `${uniqueId}-title`; + const buttonRef = useRef(null); + const popoverRef = useRef(null); const [open, setOpen] = useState(false); const handleClick = useCallback(() => { setOpen((v) => !v); + setTimeout(() => { + popoverRef.current?.focus(); + }, 0); }, [setOpen]); const handleClose = useCallback(() => { setOpen(false); + buttonRef.current?.focus(); }, [setOpen]); const [handleMouseDown, handleMouseUp] = useSelectableClick(handleClose); @@ -34,11 +47,12 @@ export const AltTextBadge: React.FC<{ description: string }> = ({ <> @@ -47,7 +61,7 @@ export const AltTextBadge: React.FC<{ description: string }> = ({ rootClose onHide={handleClose} show={open} - target={anchorRef} + target={buttonRef} placement='top-end' flip offset={offset} @@ -57,17 +71,34 @@ export const AltTextBadge: React.FC<{ description: string }> = ({
-

+

+ + +

{description}

diff --git a/app/javascript/flavours/glitch/components/alt_text_badge/styles.module.scss b/app/javascript/flavours/glitch/components/alt_text_badge/styles.module.scss new file mode 100644 index 0000000000..1b7d5ec788 --- /dev/null +++ b/app/javascript/flavours/glitch/components/alt_text_badge/styles.module.scss @@ -0,0 +1,17 @@ +.closeButton { + position: absolute; + top: 5px; + inset-inline-end: 2px; + padding: 10px; + + --default-icon-color: var(--color-text-on-media); + --default-bg-color: transparent; + --hover-icon-color: var(--color-text-on-media); + --hover-bg-color: rgb(from var(--color-text-on-media) r g b / 10%); + --focus-outline-color: var(--color-text-on-media); + + svg { + width: 20px; + height: 20px; + } +} diff --git a/app/javascript/flavours/glitch/styles/mastodon/components.scss b/app/javascript/flavours/glitch/styles/mastodon/components.scss index 117806878b..484c4d3d65 100644 --- a/app/javascript/flavours/glitch/styles/mastodon/components.scss +++ b/app/javascript/flavours/glitch/styles/mastodon/components.scss @@ -285,6 +285,7 @@ --default-bg-color: transparent; --hover-icon-color: var(--color-text-primary); --hover-bg-color: var(--color-bg-brand-softer); + --focus-outline-color: var(--color-text-brand); display: inline-flex; color: var(--default-icon-color); @@ -313,7 +314,7 @@ } &:focus-visible { - outline: 2px solid var(--color-text-brand); + outline: 2px solid var(--focus-outline-color); } &.disabled { @@ -5160,6 +5161,13 @@ a.status-card { background-color: rgb(from var(--color-bg-media-base) r g b / 90%); } } + + &:focus-visible { + .spoiler-button__overlay__label { + outline: 2px solid var(--color-text-on-media); + outline-offset: -4px; + } + } } } @@ -7380,6 +7388,13 @@ img.modal-warning { font-size: 14px; font-weight: 700; line-height: 20px; + + &:focus-visible { + outline: none; + box-shadow: + inset 0 0 0 2px var(--color-text-on-media), + 0 0 0 2px var(--color-bg-media); + } } } @@ -7409,6 +7424,13 @@ img.modal-warning { cursor: pointer; pointer-events: auto; + &:focus-visible { + outline: none; + box-shadow: + inset 0 0 0 2px var(--color-text-on-media), + 0 0 0 2px var(--color-bg-media); + } + &--non-interactive { pointer-events: none; } @@ -7433,6 +7455,16 @@ img.modal-warning { overflow-y: auto; z-index: 10; + &:focus-visible { + box-shadow: + var(--dropdown-shadow), + inset 0 0 0 2px var(--color-text-on-media); + + // Extend background color for better visibility of the + // inset box-shadow "outline" + outline: 2px solid var(--color-bg-media); + } + &--solid { color: var(--color-text-primary); background: var(--color-bg-primary); @@ -7684,6 +7716,7 @@ img.modal-warning { color: var(--color-text-primary); position: relative; z-index: -1; + border-radius: inherit; &, img { @@ -7732,6 +7765,23 @@ img.modal-warning { height: 100%; object-fit: cover; } + + &:focus { + outline: none; + border-radius: inherit; + } + + // Double focus outline for better visibility on photos + &:focus-visible::after { + content: ''; + position: absolute; + inset: 2px; + z-index: 1; + border-radius: inherit; + border: 2px solid var(--color-text-on-inverted); + outline: 2px solid var(--color-bg-inverted); + pointer-events: none; + } } /* End Media Gallery */