[bugfix] Fix a couple accessibility issues with :focus elements (#3979)

* [bugfix/frontend] Fix accessibility/focus issues in settings + web ui

* fix little error

* tweaks
This commit is contained in:
tobi
2025-04-09 14:14:20 +02:00
committed by GitHub
parent 365b575341
commit 19cfa8d126
24 changed files with 405 additions and 152 deletions

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import React, { useRef } from "react";
import type {
ReactNode,
@@ -119,23 +119,36 @@ export interface FileInputProps extends React.DetailedHTMLProps<
}
export function FileInput({ label, field, ...props }: FileInputProps) {
const { onChange, ref, infoComponent } = field;
const ref = useRef<HTMLInputElement>(null);
const { onChange, infoComponent } = field;
const id = nanoid();
const onClick = () => {
ref.current?.click();
};
return (
<div className="form-field file">
<label className="label-label" htmlFor={id}>
{label}
</label>
<label className="label-button" htmlFor={id}>
<div className="file-input button">Browse</div>
<label
className="label-wrapper"
htmlFor={id}
tabIndex={0}
onClick={onClick}
onKeyDown={e => e.key === "Enter" && onClick()}
role="button"
>
<div className="label-label">
{label}
</div>
<div className="label-button">
<div className="file-input button">Browse</div>
</div>
</label>
<input
id={id}
type="file"
className="hidden"
onChange={onChange}
ref={ref ? ref as RefObject<HTMLInputElement> : undefined}
ref={ref}
{...props}
/>
{infoComponent}

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import React, { useRef } from "react";
import { useVerifyCredentialsQuery } from "../lib/query/login";
import { MediaAttachment, Status as StatusType } from "../lib/types/status";
import sanitize from "sanitize-html";
@@ -122,10 +122,26 @@ function StatusBody({ status }: { status: StatusType }) {
content = sanitize(status.content);
}
const detailsRef = useRef<HTMLDetailsElement>(null);
const detailsOnClick = () => {
detailsRef.current?.click();
};
const summaryRef = useRef<HTMLElement>(null);
const summaryOnClick = () => {
summaryRef.current?.click();
};
return (
<div className="status-body">
<details className="text-spoiler">
<summary>
<details
className="text-spoiler"
ref={detailsRef}
>
<summary
tabIndex={-1}
ref={summaryRef}
>
<div
className="spoiler-content"
lang={status.language}
@@ -140,6 +156,8 @@ function StatusBody({ status }: { status: StatusType }) {
role="button"
tabIndex={0}
aria-label="Toggle content visibility"
onClick={detailsOnClick}
onKeyDown={e => e.key === "Enter" && summaryOnClick()}
>
Toggle content visibility
</span>
@@ -183,23 +201,41 @@ function StatusMedia({ status }: { status: StatusType }) {
}
function StatusMediaEntry({ media }: { media: MediaAttachment }) {
const detailsRef = useRef<HTMLDetailsElement>(null);
const detailsOnClick = () => {
detailsRef.current?.click();
};
const summaryRef = useRef<HTMLElement>(null);
const summaryOnClick = () => {
summaryRef.current?.click();
};
return (
<div className="media-wrapper">
<details className="image-spoiler media-spoiler">
<summary>
<div className="show sensitive button" aria-hidden="true">Show media</div>
<span className="eye button" role="button" tabIndex={0} aria-label="Toggle show media">
<summary tabIndex={-1} ref={summaryRef}>
<div
className="show sensitive button"
role="button"
tabIndex={0}
aria-hidden="true"
onClick={detailsOnClick}
onKeyDown={e => e.key === "Enter" && summaryOnClick()}
>
Show media
</div>
<span
className="eye button"
role="button"
tabIndex={0}
aria-label="Toggle show media"
onClick={detailsOnClick}
onKeyDown={e => e.key === "Enter" && summaryOnClick()}
>
<i className="hide fa fa-fw fa-eye-slash" aria-hidden="true"></i>
<i className="show fa fa-fw fa-eye" aria-hidden="true"></i>
</span>
<img
src={media.preview_url}
loading="lazy"
alt={media.description}
title={media.description}
width={media.meta.small.width}
height={media.meta.small.height}
/>
</summary>
<a
href={media.url}

View File

@@ -150,19 +150,21 @@ function ReadyUsernameLozenge({ account, linkTo, backLocation, classNames }: Rea
if (linkTo) {
className += " pseudolink";
const onClick = () => {
// When clicking on an account, direct
// to the detail view for that account.
setLocation(linkTo, {
// Store the back location in history so
// the detail view can use it to return to
// this page (including query parameters).
state: { backLocation: backLocation }
});
};
return (
<span
className={className}
onClick={() => {
// When clicking on an account, direct
// to the detail view for that account.
setLocation(linkTo, {
// Store the back location in history so
// the detail view can use it to return to
// this page (including query parameters).
state: { backLocation: backLocation }
});
}}
onClick={onClick}
onKeyDown={e => e.key === "Enter" && onClick()}
role="link"
tabIndex={0}
>