feat: add aria-label to buttons and links

This commit is contained in:
trafficlunar 2025-07-18 22:40:24 +01:00
parent 029c7eb84a
commit a37759622a
33 changed files with 113 additions and 69 deletions

View file

@ -217,7 +217,7 @@ export default async function MiiPage({ params }: Props) {
<div className="flex gap-3 w-fit bg-amber-50 border-2 border-amber-500 rounded-2xl shadow-lg p-4 text-3xl text-orange-400 max-md:place-self-center *:size-12 *:flex *:flex-col *:items-center *:gap-1 **:transition-discrete **:duration-150 *:hover:brightness-75 *:hover:scale-[1.08] *:[&_span]:text-xs">
{session && (Number(session.user.id) === mii.userId || Number(session.user.id) === Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) && (
<>
<Link href={`/edit/${mii.id}`}>
<Link aria-label="Edit Mii" href={`/edit/${mii.id}`}>
<Icon icon="mdi:pencil" />
<span>Edit</span>
</Link>
@ -226,7 +226,7 @@ export default async function MiiPage({ params }: Props) {
)}
<ShareMiiButton miiId={mii.id} />
<Link href={`/report/mii/${mii.id}`}>
<Link aria-label="Report Mii" href={`/report/mii/${mii.id}`}>
<Icon icon="material-symbols:flag-rounded" />
<span>Report</span>
</Link>

View file

@ -41,7 +41,7 @@ export default async function SubmitPage() {
<div className="bg-amber-50 border-2 border-amber-500 rounded-2xl shadow-lg p-8 max-w-xs w-full text-center flex flex-col">
<h2 className="text-5xl font-black">Sorry</h2>
<p className="mt-1">Submissions are disabled</p>
<Link href="/" className="pill button gap-2 mt-8 w-fit self-center">
<Link href="/" aria-label="Return to Home Page" className="pill button gap-2 mt-8 w-fit self-center">
<Icon icon="ic:round-home" fontSize={24} />
Return Home
</Link>

View file

@ -49,7 +49,7 @@ export default function PunishmentDeletionDialog({ punishmentId }: Props) {
return (
<>
<button onClick={() => setIsOpen(true)} className="text-red-500 cursor-pointer hover:text-red-600 text-lg">
<button onClick={() => setIsOpen(true)} aria-label="Delete Punishment" className="text-red-500 cursor-pointer hover:text-red-600 text-lg">
<Icon icon="material-symbols:close-rounded" />
</button>
@ -70,7 +70,7 @@ export default function PunishmentDeletionDialog({ punishmentId }: Props) {
>
<div className="flex justify-between items-center mb-2">
<h2 className="text-xl font-bold">Punishment Deletion</h2>
<button onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<button onClick={close} aria-label="Close" className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<Icon icon="material-symbols:close-rounded" />
</button>
</div>

View file

@ -108,10 +108,11 @@ export default async function Reports() {
<button
type="submit"
aria-label="Open"
className="cursor-pointer text-orange-400 flex items-center gap-1 p-1.5 rounded-lg transition-colors hover:bg-orange-400/15"
>
<Icon icon="mdi:alert-circle" className="text-xl" />
<span className="text-sm">Open</span>{" "}
<span className="text-sm">Open</span>
</button>
</form>
<form action={updateStatus}>
@ -120,6 +121,7 @@ export default async function Reports() {
<button
type="submit"
aria-label="Resolve"
className="cursor-pointer text-green-500 flex items-center gap-1 p-1.5 rounded-lg transition-colors hover:bg-green-500/15"
>
<Icon icon="mdi:check-circle" className="text-xl" />
@ -132,6 +134,7 @@ export default async function Reports() {
<button
type="submit"
aria-label="Dismiss"
className="cursor-pointer text-zinc-400 flex items-center gap-1 p-1.5 rounded-lg transition-colors hover:bg-zinc-400/15"
>
<Icon icon="mdi:close-circle" className="text-xl" />

View file

@ -44,7 +44,7 @@ export default function ReturnToIsland({ hasExpired }: Props) {
<hr className="border-zinc-300 mt-3 mb-4" />
{error && <span className="text-red-400 font-bold mb-2.5">Error: {error}</span>}
<button disabled={!isChecked} onClick={handleClick} className="pill button gap-2 w-fit self-center">
<button disabled={!isChecked} aria-label="Travel Back Home" onClick={handleClick} className="pill button gap-2 w-fit self-center">
<Icon icon="ic:round-home" fontSize={24} />
Travel Back
</button>

View file

@ -274,7 +274,7 @@ export default function Punishments() {
value={newMii.reason}
onChange={(e) => setNewMii({ ...newMii, reason: e.target.value })}
/>
<button type="button" onClick={addMiiToList} className="pill button aspect-square !p-2.5">
<button type="button" aria-label="Add Mii" onClick={addMiiToList} className="pill button aspect-square !p-2.5">
<Icon icon="ic:baseline-plus" className="size-4" />
</button>
</div>
@ -295,6 +295,7 @@ export default function Punishments() {
</div>
<button
type="button"
aria-label="Remove Mii"
onClick={() => removeMiiFromList(index)}
className="cursor-pointer text-red-500 hover:text-red-700 transition-colors"
>

View file

@ -62,6 +62,7 @@ export default function Carousel({ images, className }: Props) {
<>
<button
type="button"
aria-label="Scroll Carousel Left"
onClick={() => emblaApi?.scrollPrev()}
disabled={!emblaApi?.canScrollPrev()}
className={`absolute left-2 top-1/2 -translate-y-1/2 bg-white p-1 rounded-full shadow text-xl transition-opacity ${
@ -72,6 +73,7 @@ export default function Carousel({ images, className }: Props) {
</button>
<button
type="button"
aria-label="Scroll Carousel Right"
onClick={() => emblaApi?.scrollNext()}
disabled={!emblaApi?.canScrollNext()}
className={`absolute right-2 top-1/2 -translate-y-1/2 bg-white p-1 rounded-full shadow text-xl transition-opacity ${
@ -86,6 +88,7 @@ export default function Carousel({ images, className }: Props) {
<button
key={index}
type="button"
aria-label={`Go to ${index} in Carousel`}
onClick={() => emblaApi?.scrollTo(index)}
className={`size-1.5 cursor-pointer rounded-full ${index === selectedIndex ? "bg-black" : "bg-black/25"}`}
/>

View file

@ -51,12 +51,18 @@ export default function DeleteMiiButton({ miiId, miiName, likes, inMiiPage }: Pr
return (
<>
{inMiiPage ? (
<button onClick={() => setIsOpen(true)} className="cursor-pointer">
<button onClick={() => setIsOpen(true)} aria-label="Delete Mii" className="cursor-pointer">
<Icon icon="mdi:trash" />
<span>Delete</span>
</button>
) : (
<button onClick={() => setIsOpen(true)} title="Delete Mii" data-tooltip="Delete" className="cursor-pointer aspect-square">
<button
onClick={() => setIsOpen(true)}
aria-label="Delete Mii"
title="Delete Mii"
data-tooltip="Delete"
className="cursor-pointer aspect-square"
>
<Icon icon="mdi:trash" />
</button>
)}
@ -78,7 +84,7 @@ export default function DeleteMiiButton({ miiId, miiName, likes, inMiiPage }: Pr
>
<div className="flex justify-between items-center mb-2">
<h2 className="text-xl font-bold">Delete Mii</h2>
<button onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<button onClick={close} aria-label="Close" className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<Icon icon="material-symbols:close-rounded" />
</button>
</div>

View file

@ -13,7 +13,11 @@ export default async function Header() {
return (
<header className="sticky top-0 z-50 w-full p-4 grid grid-cols-3 gap-2 gap-x-4 items-center bg-amber-50 border-b-4 border-amber-500 shadow-md max-lg:grid-cols-2 max-md:grid-cols-1">
<Link href={"/"} className="font-black text-3xl text-orange-400 flex items-center gap-2 max-md:justify-center max-md:col-span-2">
<Link
href={"/"}
aria-label="Go to Home Page"
className="font-black text-3xl text-orange-400 flex items-center gap-2 max-md:justify-center max-md:col-span-2"
>
<Image src="/logo.svg" width={56} height={45} alt="logo" />
TomodachiShare
</Link>

View file

@ -48,7 +48,6 @@ export default function ImageViewer({ src, alt, width, height, className, images
setSelectedIndex(index);
}
// Scroll snaps
setScrollSnaps(emblaApi.scrollSnapList());
emblaApi.on("select", () => setSelectedIndex(emblaApi.selectedScrollSnap()));
}, [emblaApi, images, src]);
@ -58,19 +57,9 @@ export default function ImageViewer({ src, alt, width, height, className, images
if (!isOpen || !emblaApi) return;
const handleKeyDown = (event: KeyboardEvent) => {
switch (event.key) {
case "ArrowLeft":
emblaApi.scrollPrev();
break;
case "ArrowRight":
emblaApi.scrollNext();
break;
case "Escape":
close();
break;
default:
break;
}
if (event.key === "ArrowLeft") emblaApi.scrollPrev();
else if (event.key === "ArrowRight") emblaApi.scrollNext();
else if (event.key === "Escape") close();
};
window.addEventListener("keydown", handleKeyDown);
@ -79,6 +68,8 @@ export default function ImageViewer({ src, alt, width, height, className, images
};
}, [isOpen, emblaApi]);
const imagesMap = images.length === 0 ? [src] : images;
return (
<>
<Image src={src} alt={alt} width={width} height={height} className={`cursor-pointer ${className}`} onClick={() => setIsOpen(true)} />
@ -94,29 +85,23 @@ export default function ImageViewer({ src, alt, width, height, className, images
/>
<div
className={`z-50 bg-orange-50 border-2 border-amber-500 rounded-2xl mx-4 shadow-lg w-full max-w-xl relative transition-discrete duration-300 ${
className={`z-50 bg-orange-50 border-2 border-amber-500 rounded-2xl mx-4 shadow-lg aspect-square w-full max-w-xl relative transition-discrete duration-300 ${
isVisible ? "scale-100 opacity-100" : "scale-75 opacity-0"
}`}
>
<div className="z-50 absolute right-0 bg-amber-500 rounded-tr-xl rounded-bl-md p-1 flex justify-between items-center">
<button type="button" onClick={close} className="text-2xl cursor-pointer">
<button type="button" aria-label="Close" onClick={close} className="text-2xl cursor-pointer">
<Icon icon="material-symbols:close-rounded" />
</button>
</div>
<div className="overflow-hidden rounded-2xl" ref={emblaRef}>
<div className="flex">
{images.length == 0 ? (
<Image src={src} alt={alt} width={576} height={576} className="w-full" />
) : (
<>
{images.map((image, index) => (
<div key={index} className="flex-shrink-0 w-full">
<Image src={image} alt={alt} width={576} height={576} className="w-full h-full object-contain" />
</div>
))}
</>
)}
<div className="overflow-hidden rounded-2xl h-full" ref={emblaRef}>
<div className="flex h-full items-center">
{imagesMap.map((image, index) => (
<div key={index} className="flex-shrink-0 w-full">
<Image src={image} alt={alt} width={576} height={576} className="object-contain" />
</div>
))}
</div>
</div>
</div>
@ -132,6 +117,7 @@ export default function ImageViewer({ src, alt, width, height, className, images
>
<button
type="button"
aria-label="Scroll Carousel Left"
onClick={() => emblaApi?.scrollPrev()}
disabled={!emblaApi?.canScrollPrev()}
className={`bg-white p-1 rounded-full shadow text-4xl transition-opacity ${
@ -149,6 +135,7 @@ export default function ImageViewer({ src, alt, width, height, className, images
>
<button
type="button"
aria-label="Scroll Carousel Right"
onClick={() => emblaApi?.scrollNext()}
disabled={!emblaApi?.canScrollNext()}
className={`bg-white p-1 rounded-full shadow text-4xl transition-opacity ${
@ -168,6 +155,7 @@ export default function ImageViewer({ src, alt, width, height, className, images
{scrollSnaps.map((_, index) => (
<button
key={index}
aria-label={`Go to ${index} in Carousel`}
onClick={() => emblaApi?.scrollTo(index)}
className={`size-2.5 cursor-pointer rounded-full ${index === selectedIndex ? "bg-black" : "bg-black/25"}`}
/>

View file

@ -51,7 +51,11 @@ export default function LikeButton({ likes, isLiked, miiId, isLoggedIn, disabled
}, []);
return (
<button onClick={onClick} className={`flex items-center gap-2 text-red-400 ${disabled ? "" : "cursor-pointer"} ${big ? "text-3xl" : "text-xl"}`}>
<button
onClick={onClick}
aria-label="Like"
className={`flex items-center gap-2 text-red-400 ${disabled ? "" : "cursor-pointer"} ${big ? "text-3xl" : "text-xl"}`}
>
<div className="relative">
<Icon icon={isLikedState ? "icon-park-solid:like" : "icon-park-outline:like"} className={`${isAnimating ? "animate-like " : ""}`} />
<div

View file

@ -8,6 +8,7 @@ export default function LoginButtons() {
<div className="flex flex-col items-center gap-2">
<button
onClick={() => signIn("discord", { redirectTo: "/create-username" })}
aria-label="Login with Discord"
className="pill button gap-2 !px-3 !bg-indigo-400 !border-indigo-500 hover:!bg-indigo-500"
>
<Icon icon="ic:baseline-discord" fontSize={32} />
@ -15,6 +16,7 @@ export default function LoginButtons() {
</button>
<button
onClick={() => signIn("github", { redirectTo: "/create-username" })}
aria-label="Login with GitHub"
className="pill button gap-2 !px-3 !bg-zinc-700 !border-zinc-800 hover:!bg-zinc-800 text-white"
>
<Icon icon="mdi:github" fontSize={32} />

View file

@ -6,7 +6,7 @@ import { signOut } from "next-auth/react";
export default function LogoutButton() {
return (
<li title="Logout">
<button onClick={() => signOut()} className="pill button !p-0 aspect-square h-full" data-tooltip="Log Out">
<button onClick={() => signOut()} aria-label="Log Out" className="pill button !p-0 aspect-square h-full" data-tooltip="Log Out">
<Icon icon="ic:round-logout" fontSize={24} />
</button>
</li>

View file

@ -32,7 +32,7 @@ export default function FilterSelect() {
return (
<div className="relative">
<button onClick={() => setIsOpen((prev) => !prev)} className="pill button gap-1 text-nowrap">
<button onClick={() => setIsOpen((prev) => !prev)} aria-label="Filter dropdown" className="pill button gap-1 text-nowrap">
Filter{" "}
{tags.length > 0 ? (
<span>

View file

@ -192,7 +192,7 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
{userId && Number(session?.user.id) == userId && (
<div className="flex gap-1 text-2xl justify-end text-zinc-400">
<Link href={`/edit/${mii.id}`} title="Edit Mii" data-tooltip="Edit">
<Link href={`/edit/${mii.id}`} title="Edit Mii" aria-label="Edit Mii" data-tooltip="Edit">
<Icon icon="mdi:pencil" />
</Link>
<DeleteMiiButton miiId={mii.id} miiName={mii.name} likes={mii.likes} />

View file

@ -41,6 +41,7 @@ export default function Pagination({ lastPage }: Props) {
{/* Go to first page */}
<Link
href={page === 1 ? "#" : createPageUrl(1)}
aria-label="Go to First Page"
aria-disabled={page === 1}
tabIndex={page === 1 ? -1 : undefined}
className={`pill button !bg-orange-100 !p-0.5 aspect-square text-2xl ${
@ -53,6 +54,7 @@ export default function Pagination({ lastPage }: Props) {
{/* Previous page */}
<Link
href={page === 1 ? "#" : createPageUrl(page - 1)}
aria-label="Go to Previous Page"
aria-disabled={page === 1}
tabIndex={page === 1 ? -1 : undefined}
className={`pill !bg-orange-100 !p-0.5 aspect-square text-2xl ${page === 1 ? "pointer-events-none opacity-50" : "hover:!bg-orange-400"}`}
@ -66,6 +68,7 @@ export default function Pagination({ lastPage }: Props) {
<Link
key={number}
href={createPageUrl(number)}
aria-label={`Go to Page ${number}`}
aria-current={number === page ? "page" : undefined}
className={`pill !p-0 w-8 h-8 text-center !rounded-md ${number == page ? "!bg-orange-400" : "!bg-orange-100 hover:!bg-orange-400"}`}
>
@ -77,6 +80,7 @@ export default function Pagination({ lastPage }: Props) {
{/* Next page */}
<Link
href={page === lastPage ? "#" : createPageUrl(page + 1)}
aria-label="Go to Next Page"
aria-disabled={page === lastPage}
tabIndex={page === lastPage ? -1 : undefined}
className={`pill button !bg-orange-100 !p-0.5 aspect-square text-2xl ${
@ -89,6 +93,7 @@ export default function Pagination({ lastPage }: Props) {
{/* Go to last page */}
<Link
href={page === lastPage ? "#" : createPageUrl(lastPage)}
aria-label="Go to Last Page"
aria-disabled={page === lastPage}
tabIndex={page === lastPage ? -1 : undefined}
className={`pill button !bg-orange-100 !p-0.5 aspect-square text-2xl ${

View file

@ -28,7 +28,7 @@ export default function SortSelect() {
return (
<div className="relative w-full">
{/* Toggle button to open the dropdown */}
<button type="button" {...getToggleButtonProps()} className="pill input w-full gap-1 !justify-between text-nowrap">
<button type="button" {...getToggleButtonProps()} aria-label="Sort dropdown" className="pill input w-full gap-1 !justify-between text-nowrap">
<span>Sort by </span>
{selectedItem || "Select a way to sort"}
<Icon icon="tabler:chevron-down" className="ml-2 size-5" />

View file

@ -68,31 +68,31 @@ export default async function ProfileInformation({ userId, page }: Props) {
{/* Buttons */}
<div className="flex gap-1 w-fit text-3xl text-orange-400 max-md:place-self-center *:size-17 *:flex *:flex-col *:items-center *:gap-1 **:transition-discrete **:duration-150 *:hover:brightness-75 *:hover:scale-[1.08] *:[&_span]:text-sm">
{!isOwnProfile && (
<Link href={`/report/user/${id}`}>
<Link aria-label="Report User" href={`/report/user/${id}`}>
<Icon icon="material-symbols:flag-rounded" />
<span>Report</span>
</Link>
)}
{isOwnProfile && isAdmin && (
<Link href="/admin">
<Link aria-label="Go to Admin" href="/admin">
<Icon icon="mdi:shield-moon" />
<span>Admin</span>
</Link>
)}
{isOwnProfile && page !== "likes" && (
<Link href="/profile/likes">
<Link aria-label="Go to My Likes" href="/profile/likes">
<Icon icon="icon-park-solid:like" />
<span>My Likes</span>
</Link>
)}
{isOwnProfile && page !== "settings" && (
<Link href="/profile/settings">
<Link aria-label="Go to Settings" href="/profile/settings">
<Icon icon="material-symbols:settings-rounded" />
<span>Settings</span>
</Link>
)}
{page && (
<Link href={`/profile/${id}`}>
<Link aria-label="Go Back to Profile" href={`/profile/${id}`}>
<Icon icon="tabler:chevron-left" />
<span>Back</span>
</Link>

View file

@ -7,7 +7,12 @@ export default async function ProfileOverview() {
return (
<li title="Your profile">
<Link href={`/profile/${session?.user.id}`} className="pill button !gap-2 !p-0 h-full max-w-64" data-tooltip="Your Profile">
<Link
href={`/profile/${session?.user.id}`}
aria-label="Go to profile"
className="pill button !gap-2 !p-0 h-full max-w-64"
data-tooltip="Your Profile"
>
<Image
src={session?.user?.image ?? "/guest.webp"}
alt="profile picture"

View file

@ -64,7 +64,7 @@ export default function DeleteAccount() {
>
<div className="flex justify-between items-center mb-2">
<h2 className="text-xl font-bold">Delete Account</h2>
<button onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<button onClick={close} aria-label="Close" className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<Icon icon="material-symbols:close-rounded" />
</button>
</div>

View file

@ -72,6 +72,7 @@ export default function ProfilePictureSettings() {
{newPicture && (
<button
data-tooltip="Delete Picture"
aria-label="Delete Picture"
onClick={() => setNewPicture(undefined)}
className="pill button aspect-square !p-1 text-2xl !bg-red-400 !border-red-500"
>

View file

@ -37,7 +37,7 @@ export default function SubmitDialogButton({ title, description, onSubmit, error
return (
<>
<button onClick={() => setIsOpen(true)} className="pill button size-11 !p-1 text-2xl">
<button onClick={() => setIsOpen(true)} aria-label="Open Submit Dialog" className="pill button size-11 !p-1 text-2xl">
<Icon icon="material-symbols:check-rounded" />
</button>
@ -58,7 +58,7 @@ export default function SubmitDialogButton({ title, description, onSubmit, error
>
<div className="flex justify-between items-center mb-2">
<h2 className="text-xl font-bold">{title}</h2>
<button onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<button onClick={close} aria-label="Close" className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<Icon icon="material-symbols:close-rounded" />
</button>
</div>

View file

@ -5,7 +5,7 @@ import { Icon } from "@iconify/react";
export default function RandomLink() {
return (
<Link href={"/random"} className="pill button !p-0 h-full aspect-square" data-tooltip="Go to a Random Mii">
<Link href={"/random"} aria-label="Go to Random Link" className="pill button !p-0 h-full aspect-square" data-tooltip="Go to a Random Mii">
<Icon icon="mdi:dice-3" fontSize={28} />
</Link>
);

View file

@ -36,7 +36,12 @@ export default function ReasonSelector({ reason, setReason }: Props) {
return (
<div className="relative w-full col-span-2">
{/* Toggle button to open the dropdown */}
<button type="button" {...getToggleButtonProps()} className="pill input w-full gap-1 !justify-between text-nowrap">
<button
type="button"
{...getToggleButtonProps()}
aria-label="Report reason dropdown"
className="pill input w-full gap-1 !justify-between text-nowrap"
>
{selectedItem?.label || <span className="text-black/40">Select a reason for the report...</span>}
<Icon icon="tabler:chevron-down" className="ml-2 size-5" />
</button>

View file

@ -37,6 +37,7 @@ export default function SearchBar() {
/>
<button
onClick={handleSearch}
aria-label="Search"
data-tooltip="Search"
className="bg-orange-400 p-2 w-12 rounded-r-xl flex justify-center items-center cursor-pointer text-2xl"
>

View file

@ -60,7 +60,7 @@ export default function ShareMiiButton({ miiId }: Props) {
return (
<>
<button onClick={() => setIsOpen(true)} className="cursor-pointer">
<button onClick={() => setIsOpen(true)} aria-label="Share" className="cursor-pointer">
<Icon icon="material-symbols:share" />
<span>Share</span>
</button>
@ -82,7 +82,7 @@ export default function ShareMiiButton({ miiId }: Props) {
>
<div className="flex justify-between items-center mb-2">
<h2 className="text-xl font-bold">Share Mii</h2>
<button onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<button onClick={close} aria-label="Close" className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<Icon icon="material-symbols:close-rounded" />
</button>
</div>
@ -140,6 +140,7 @@ export default function ShareMiiButton({ miiId }: Props) {
<a
href={`/mii/${miiId}/image?type=metadata`}
className="pill button !p-0 aspect-square cursor-pointer text-xl"
aria-label="Save Image"
data-tooltip="Save Image"
download={"hello.png"}
>
@ -149,6 +150,7 @@ export default function ShareMiiButton({ miiId }: Props) {
{/* Copy button */}
<button
className="pill button !p-0 aspect-square cursor-pointer"
aria-label="Copy Image"
data-tooltip={hasCopiedImage ? "Copied!" : "Copy Image"}
onClick={handleCopyImage}
>

View file

@ -24,7 +24,7 @@ export default function SubmitButton({ onClick, text = "Submit", className }: Pr
};
return (
<button type="submit" onClick={handleClick} className={`pill button w-min ${className}`}>
<button type="submit" aria-label={text} onClick={handleClick} className={`pill button w-min ${className}`}>
{text}
{isLoading && <Icon icon="svg-spinners:180-ring-with-bg" className="ml-2" />}
</button>

View file

@ -210,7 +210,7 @@ export default function SubmitForm() {
<QrUpload setQrBytesRaw={setQrBytesRaw} />
<span>or</span>
<button type="button" onClick={() => setIsQrScannerOpen(true)} className="pill button gap-2">
<button type="button" aria-label="Use your camera" onClick={() => setIsQrScannerOpen(true)} className="pill button gap-2">
<Icon icon="mdi:camera" fontSize={20} />
Use your camera
</button>

View file

@ -145,7 +145,7 @@ export default function QrScanner({ isOpen, setIsOpen, setQrBytesRaw }: Props) {
>
<div className="flex justify-between items-center mb-2">
<h2 className="text-xl font-bold">Scan QR Code</h2>
<button type="button" onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<button type="button" aria-label="Close" onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<Icon icon="material-symbols:close-rounded" />
</button>
</div>
@ -157,6 +157,7 @@ export default function QrScanner({ isOpen, setIsOpen, setQrBytesRaw }: Props) {
{/* Toggle button to open the dropdown */}
<button
type="button"
aria-label="Select camera dropdown"
{...getToggleButtonProps({}, { suppressRefError: true })}
className="pill input w-full !px-2 !py-0.5 !justify-between text-sm"
>

View file

@ -74,6 +74,7 @@ export default function TagSelector({ tags, setTags }: Props) {
{tag}
<button
type="button"
aria-label="Delete Tag"
className="text-black cursor-pointer"
onClick={(e) => {
e.stopPropagation();
@ -99,12 +100,12 @@ export default function TagSelector({ tags, setTags }: Props) {
{/* Control buttons */}
<div className="flex items-center gap-1">
{hasSelectedItems && (
<button type="button" className="text-black cursor-pointer" onClick={() => setTags([])}>
<button type="button" aria-label="Remove All Tags" className="text-black cursor-pointer" onClick={() => setTags([])}>
<Icon icon="mdi:close" />
</button>
)}
<button type="button" {...getToggleButtonProps()} className="text-black cursor-pointer text-xl">
<button type="button" aria-label="Toggle Tag Dropdown" {...getToggleButtonProps()} className="text-black cursor-pointer text-xl">
<Icon icon="mdi:chevron-down" />
</button>
</div>

View file

@ -36,7 +36,7 @@ export default function ScanTutorialButton() {
return (
<>
<button type="button" onClick={() => setIsOpen(true)} className="text-3xl cursor-pointer">
<button aria-label="Tutorial" type="button" onClick={() => setIsOpen(true)} className="text-3xl cursor-pointer">
<Icon icon="fa:question-circle" />
<span>Tutorial</span>
</button>
@ -58,7 +58,7 @@ export default function ScanTutorialButton() {
>
<div className="flex justify-between items-center mb-2 p-6 pb-0">
<h2 className="text-xl font-bold">Tutorial</h2>
<button onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<button onClick={close} aria-label="Close" className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<Icon icon="material-symbols:close-rounded" />
</button>
</div>
@ -76,13 +76,21 @@ export default function ScanTutorialButton() {
</div>
<div className="flex justify-between items-center mt-2 px-6 pb-6">
<button onClick={() => emblaApi?.scrollPrev()} className="pill button !p-1 aspect-square text-2xl">
<button
onClick={() => emblaApi?.scrollPrev()}
aria-label="Scroll Carousel Left"
className="pill button !p-1 aspect-square text-2xl"
>
<Icon icon="tabler:chevron-left" />
</button>
<span className="text-sm">Adding Mii to Island</span>
<button onClick={() => emblaApi?.scrollNext()} className="pill button !p-1 aspect-square text-2xl">
<button
onClick={() => emblaApi?.scrollNext()}
aria-label="Scroll Carousel Right"
className="pill button !p-1 aspect-square text-2xl"
>
<Icon icon="tabler:chevron-right" />
</button>
</div>

View file

@ -25,6 +25,7 @@ export default function StartingPage({ emblaApi }: Props) {
<div className="grid grid-cols-2 gap-4 h-full">
<button
onClick={() => goToTutorial(1)}
aria-label="Allow Copying Tutorial"
className="flex flex-col justify-center items-center bg-zinc-50 rounded-xl p-4 shadow-md border-2 border-zinc-300 cursor-pointer text-center text-sm transition hover:scale-[1.03] hover:bg-cyan-100 hover:border-cyan-600"
>
<Image
@ -40,6 +41,7 @@ export default function StartingPage({ emblaApi }: Props) {
<button
onClick={() => goToTutorial(10)}
aria-label="Create QR Code Tutorial"
className="flex flex-col justify-center items-center bg-zinc-50 rounded-xl p-4 shadow-md border-2 border-zinc-300 cursor-pointer text-center text-sm transition hover:scale-[1.03] hover:bg-cyan-100 hover:border-cyan-600"
>
<Image

View file

@ -61,7 +61,7 @@ export default function SubmitTutorialButton() {
>
<div className="flex justify-between items-center mb-2 p-6 pb-0">
<h2 className="text-xl font-bold">Tutorial</h2>
<button onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<button onClick={close} aria-label="Close" className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
<Icon icon="material-symbols:close-rounded" />
</button>
</div>
@ -105,6 +105,7 @@ export default function SubmitTutorialButton() {
onClick={() => emblaApi?.scrollPrev()}
disabled={isStartingPage}
className={`pill button !p-1 aspect-square text-2xl ${isStartingPage && "!cursor-auto"}`}
aria-label="Scroll Carousel Left"
>
<Icon icon="tabler:chevron-left" />
</button>
@ -115,6 +116,7 @@ export default function SubmitTutorialButton() {
onClick={() => emblaApi?.scrollNext()}
disabled={isStartingPage}
className={`pill button !p-1 aspect-square text-2xl ${isStartingPage && "!cursor-auto"}`}
aria-label="Scroll Carousel Right"
>
<Icon icon="tabler:chevron-right" />
</button>