Improved accessibility by adding focus styles

This commit is contained in:
Prozilla 2023-08-07 13:40:55 +02:00
parent d1eb97f9b6
commit 9c1074061e
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
16 changed files with 71 additions and 44 deletions

View file

@ -68,51 +68,52 @@ export function FileExplorer({ startPath }) {
return (
<div className={styles.Container}>
<div className={styles.Header}>
<button title="Back" className={styles["Icon-button"]}>
<button title="Back" tabIndex={0} className={styles["Icon-button"]}>
<FontAwesomeIcon icon={faCaretLeft}/>
</button>
<button title="Forward" className={styles["Icon-button"]}>
<button title="Forward" tabIndex={0} className={styles["Icon-button"]}>
<FontAwesomeIcon icon={faCaretRight}/>
</button>
<button title="Up" className={styles["Icon-button"]} onClick={() => { changeDirectory(".."); }}>
<button title="Up" tabIndex={0} className={styles["Icon-button"]} onClick={() => { changeDirectory(".."); }}>
<FontAwesomeIcon icon={faArrowUp}/>
</button>
<input
value={path}
type="text"
className={styles["Path-input"]}
tabIndex={0}
onChange={onPathChange}
onKeyDown={onKeyDown}
/>
<button title="Search" className={styles["Icon-button"]}>
<button title="Search" tabIndex={0} className={styles["Icon-button"]}>
<FontAwesomeIcon icon={faSearch}/>
</button>
<button title="Settings" className={styles["Icon-button"]}>
<button title="Settings" tabIndex={0} className={styles["Icon-button"]}>
<FontAwesomeIcon icon={faCog}/>
</button>
</div>
<div className={styles.Body}>
<div className={styles.Sidebar}>
<button title="Home" className={styles["Nav-button"]} onClick={() => { changeDirectory("~"); }}>
<button title="Home" tabIndex={0} className={styles["Nav-button"]} onClick={() => { changeDirectory("~"); }}>
<FontAwesomeIcon icon={faHouse}/>
Home
</button>
<button title="Desktop" className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Desktop"); }}>
<button title="Desktop" tabIndex={0} className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Desktop"); }}>
<FontAwesomeIcon icon={faDesktop}/>
Desktop
</button>
<button title="Documents" className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Documents"); }}>
<button title="Documents" tabIndex={0} className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Documents"); }}>
<FontAwesomeIcon icon={faFileLines}/>
Documents
</button>
<button title="Images" className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Images"); }}>
<button title="Images" tabIndex={0} className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Images"); }}>
<FontAwesomeIcon icon={faImage}/>
Images
</button>
</div>
<div className={styles.Main}>
{currentDirectory?.getFiles(showHidden)?.map((file, index) =>
<button key={index} title={file.id} className={styles["File-button"]} onClick={(event) => {
<button key={index} title={file.id} tabIndex={0} className={styles["File-button"]} onClick={(event) => {
event.preventDefault();
windowsManager.openFile(file);
}}>
@ -121,7 +122,7 @@ export function FileExplorer({ startPath }) {
</button>
)}
{currentDirectory?.getSubFolders(showHidden)?.map(({ name }, index) =>
<button key={index} title={name} className={styles["Folder-button"]} onClick={() => {
<button key={index} title={name} tabIndex={0} className={styles["Folder-button"]} onClick={() => {
changeDirectory(name);
}}>
<FontAwesomeIcon icon={faFolder}/>

View file

@ -46,7 +46,7 @@
transition: all 200ms ease-in-out;
}
.Icon-button:hover::after {
.Icon-button:hover::after, .Icon-button:focus::after {
background-color: rgba(255, 255, 255, 10%);
transform: scale(150%);
}
@ -102,7 +102,7 @@
transition: background-color 100ms ease-in-out;
}
.Nav-button:hover {
.Nav-button:hover, .Nav-button:focus {
background-color: rgba(255, 255, 255, 10%);
}
@ -138,7 +138,8 @@
transition: background-color 100ms ease-in-out;
}
.File-button:hover, .Folder-button:hover {
.File-button:hover, .Folder-button:hover,
.File-button:focus, .Folder-button:focus {
background-color: rgba(255, 255, 255, 10%);
}

View file

@ -36,7 +36,7 @@
transition: background-color 100ms ease-in-out;
}
.Tab-button:hover {
.Tab-button:hover, .Tab-button:focus {
background-color: rgba(255, 255, 255, 10%);
}

View file

@ -36,6 +36,7 @@ function AppButton({ app }) {
return (
<button
tabIndex={0}
className={classNames.join(" ")}
onClick={() => { windowsManager.open(app.id); }}
title={app.name}
@ -102,7 +103,9 @@ export function Taskbar() {
<div className={styles["Menu-icons"]}>
<div className={styles["Home-container"]}>
<OutsideClickListener onOutsideClick={() => { updateShowHome(false); }}>
<button title="Home"
<button
title="Home"
tabIndex={0}
className={`${styles["Menu-button"]} ${styles["Home-button"]}`}
onClick={() => { updateShowHome(!showHome); }}
>
@ -113,7 +116,9 @@ export function Taskbar() {
</div>
<div className={styles["Search-container"]}>
<OutsideClickListener onOutsideClick={() => { updateShowSearch(false); }}>
<button title="Search"
<button
title="Search"
tabIndex={0}
className={`${styles["Menu-button"]} ${styles["Search-button"]}`}
onClick={() => { updateShowSearch(!showSearch); }}
>

View file

@ -28,7 +28,7 @@
z-index: -1;
}
.Task-bar button:hover {
.Task-bar button:hover, .Task-bar button:focus {
background-color: var(--task-bar-button-hover-color);
}
@ -95,7 +95,7 @@
-webkit-transform: translateX(-50%);
}
.App-icon:hover::after {
.App-icon:hover::after, .App-icon:focus::after {
width: 100%;
}

View file

@ -42,7 +42,7 @@ export function Battery() {
battery.removeEventListener("levelchange", updatePercentage);
// battery.removeEventListener("chargingtimechange", updateChargingTime);
// battery.removeEventListener("dischargingtimechange", updateDischargingTime);
}
};
});
}, []);
@ -58,7 +58,7 @@ export function Battery() {
}
return (
<button className={styles.Button} title="Battery">
<button className={styles.Button} title="Battery" tabIndex={0}>
{!isCharging
? <FontAwesomeIcon className={styles["Charging-indicator"]} icon={faMinus}/>
: null

View file

@ -11,7 +11,7 @@ export function Calendar() {
}, []);
return (
<button className={styles.Button} title="Date & Time" style={{ userSelect: "none" }}>
<button className={styles.Button} title="Date & Time" style={{ userSelect: "none" }} tabIndex={0}>
{date.toLocaleString("en-US", {
hour: "numeric",
minute: "numeric",

View file

@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
export function Network() {
return (
<button title="Wifi">
<button title="Wifi" tabIndex={0}>
<FontAwesomeIcon icon={faWifi}/>
</button>
);

View file

@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
export function Volume() {
return (
<button title="Volume">
<button title="Volume" tabIndex={0}>
<FontAwesomeIcon icon={faVolumeHigh}/>
</button>
);

View file

@ -8,6 +8,7 @@ import { ReactSVG } from "react-svg";
import { closeTab } from "../../../features/utils/browser.js";
import { useKeyboardListener } from "../../../hooks/utils/keyboard.js";
import { useVirtualRoot } from "../../../hooks/virtual-drive/VirtualRootContext.js";
import { useEffect, useState } from "react";
/**
* @param {object} props
@ -18,6 +19,11 @@ import { useVirtualRoot } from "../../../hooks/virtual-drive/VirtualRootContext.
export function HomeMenu({ active, setActive, search }) {
const windowsManager = useWindowsManager();
const virtualRoot = useVirtualRoot();
const [tabIndex, setTabIndex] = useState(active ? 0 : -1);
useEffect(() => {
setTabIndex(active ? 0 : -1);
}, [active]);
const classNames = [styles["Container-outer"]];
if (active)
@ -35,7 +41,7 @@ export function HomeMenu({ active, setActive, search }) {
search(event.key);
}
}
}
};
const onKeyUp = (event) => {
if (event.key === "Alt" && onlyAltKey) {
@ -45,7 +51,7 @@ export function HomeMenu({ active, setActive, search }) {
} else {
onlyAltKey = false;
}
}
};
useKeyboardListener({ onKeyDown, onKeyUp });
@ -53,16 +59,16 @@ export function HomeMenu({ active, setActive, search }) {
<div className={classNames.join(" ")}>
<div className={styles["Container-inner"]}>
<div className={styles.Buttons}>
<button title="Power" onClick={() => { closeTab(); }}>
<button title="Power" tabIndex={tabIndex} onClick={() => { closeTab(); }}>
<FontAwesomeIcon icon={faPowerOff}/>
</button>
<button title="Settings" onClick={() => {
<button title="Settings" tabIndex={tabIndex} onClick={() => {
setActive(false);
windowsManager.open("settings");
}}>
<FontAwesomeIcon icon={faGear}/>
</button>
<button title="Info" onClick={() => {
<button title="Info" tabIndex={tabIndex} onClick={() => {
setActive(false);
windowsManager.open("text-editor", {
mode: "view",
@ -71,13 +77,13 @@ export function HomeMenu({ active, setActive, search }) {
}}>
<FontAwesomeIcon icon={faCircleInfo}/>
</button>
<button title="Images" onClick={() => {
<button title="Images" tabIndex={tabIndex} onClick={() => {
setActive(false);
windowsManager.open("file-explorer", { startPath: "~/Images" });
}}>
<FontAwesomeIcon icon={faImage}/>
</button>
<button title="Documents" onClick={() => {
<button title="Documents" tabIndex={tabIndex} onClick={() => {
setActive(false);
windowsManager.open("file-explorer", { startPath: "~/Documents" }); }
}>
@ -91,6 +97,7 @@ export function HomeMenu({ active, setActive, search }) {
<button
key={id}
className={appStyles["App-button"]}
tabIndex={tabIndex}
onClick={() => {
setActive(false);
windowsManager.open(id);

View file

@ -17,6 +17,11 @@ import { useState } from "react";
export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inputRef }) {
const windowsManager = useWindowsManager();
const [apps, setApps] = useState(null);
const [tabIndex, setTabIndex] = useState(active ? 0 : -1);
useEffect(() => {
setTabIndex(active ? 0 : -1);
}, [active]);
useEffect(() => {
if (inputRef.current)
@ -32,7 +37,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
const onChange = (event) => {
const value = event.target.value;
setSearchQuery(value);
}
};
const classNames = [styles["Container-outer"]];
if (active && apps)
@ -44,6 +49,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
<input
ref={inputRef}
className={styles.Input}
tabIndex={tabIndex}
value={searchQuery}
onChange={onChange}
spellCheck={false}
@ -54,6 +60,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
<button
key={id}
className={appStyles["App-button"]}
tabIndex={tabIndex}
onClick={() => {
setActive(false);
windowsManager.open(id);

View file

@ -12,16 +12,20 @@ import { formatShortcut } from "../../features/utils/string.js";
export function DropdownButton({ label, options, shortcuts }) {
const [open, setOpen] = useState(false);
const tabIndex = () => {
return open ? 0 : -1;
};
return (
<OutsideClickListener onOutsideClick={() => { setOpen(false); }}>
<div className={styles.Container}>
<button title={label} className={styles.Button} onClick={() => { setOpen(!open); }}>
<button title={label} className={styles.Button} tabIndex={0} onClick={() => { setOpen(!open); }}>
{label}
</button>
{open && options
? (<div className={styles.Dropdown}>
{Object.entries(options).map(([label, callback]) =>
<button title={label} key={label} onClick={() => {
<button title={label} key={label} tabIndex={tabIndex} onClick={() => {
setOpen(false);
callback();
}}>

View file

@ -14,7 +14,7 @@
cursor: pointer;
}
.Button:hover {
.Button:hover, .Button:focus {
background-color: rgba(255, 255, 255, 5%);
}
@ -42,7 +42,7 @@
cursor: pointer;
}
.Dropdown > button:hover {
.Dropdown > button:hover, .Dropdown > button:focus {
background-color: rgba(255, 255, 255, 5%);
}

View file

@ -43,7 +43,7 @@ export function Window({ id, app, size, position, focused = false, onInteract, o
});
const close = (event) => {
event.preventDefault();
event?.preventDefault();
windowsManager.close(id);
};
@ -89,15 +89,18 @@ export function Window({ id, app, size, position, focused = false, onInteract, o
onClick={focus}
>
<div className={`${styles.Header} Handle`}>
<ReactSVG className={styles["Window-icon"]} src={process.env.PUBLIC_URL + `/media/applications/icons/${app.id}.svg`}/>
<ReactSVG
className={styles["Window-icon"]}
src={process.env.PUBLIC_URL + `/media/applications/icons/${app.id}.svg`}
/>
<p>{title}</p>
<button title="Minimize" onClick={() => setMinimized(!minimized)}>
<button title="Minimize" tabIndex={0} onClick={() => setMinimized(!minimized)}>
<FontAwesomeIcon icon={faMinus}/>
</button>
<button title="Maximize" onClick={() => setMaximized(!maximized)}>
<button title="Maximize" tabIndex={0} onClick={() => setMaximized(!maximized)}>
<FontAwesomeIcon icon={faSquare}/>
</button>
<button title="Close" id="close-window" onClick={close}>
<button title="Close" tabIndex={0} id="close-window" onClick={close}>
<FontAwesomeIcon icon={faXmark}/>
</button>
</div>

View file

@ -92,7 +92,7 @@
outline: none;
}
.Header button:hover {
.Header button:hover, .Header button:focus {
background-color: var(--header-button-hover-color);
}

View file

@ -1,4 +1,3 @@
/* eslint-disable eqeqeq */
import Application from "./application.js";
import { FileExplorer } from "../../components/applications/file-explorer/FileExplorer.jsx";
import { MediaViewer } from "../../components/applications/media-viewer/MediaViewer.jsx";
@ -29,7 +28,7 @@ export default class ApplicationsManager {
let application = null;
this.APPLICATIONS.forEach((app) => {
if (app.id == id) {
if (app.id === id) {
application = app;
return;
}