fix(a11y): improve accessibility of top-level auth and onboarding components (#21710)

Adds critical accessibility fixes across various app components:
- auth/+page: provide alt text for logo, turn on screenReader support for password input, add aria-required, hide decorative SVGs from AT
- AppSidebar: wrap navigation icons in a <nav> structure, provide ARIA labels for Home and Chat icons
- s/[id]/+page: convert structural divs into semantically accurate h1 heading and time element, wrap message display in main region
- OnBoarding: replace flawed aria-labelledby with direct aria-label on start button
- NotificationToast: provide role='status' and aria-live='polite' for proper screen reader broadcasting
- ChangelogModal: add required heading semantics for structure
- AddFilesPlaceholder: provide heading element role for standalone text content
- ImportModal: provide aria-label for close button

Addresses WCAG 4.1.3, 1.1.1, 3.3.2, and 1.3.1.
This commit is contained in:
Classic298
2026-02-22 21:18:53 +01:00
committed by GitHub
parent bf0fb1c449
commit a0dbd41551
8 changed files with 30 additions and 19 deletions

View File

@@ -7,7 +7,7 @@
</script>
<div class="px-3">
<div class="text-center dark:text-white text-2xl font-medium z-50">
<div class="text-center dark:text-white text-2xl font-medium z-50" role="heading" aria-level="2">
{#if title}
{title}
{:else}

View File

@@ -38,11 +38,11 @@
<Modal bind:show size="xl">
<div class="px-6 pt-5 dark:text-white text-black">
<div class="flex justify-between items-start">
<div class="text-xl font-medium">
<h2 class="text-xl font-medium m-0">
{$i18n.t("What's New in")}
{$WEBUI_NAME}
<Confetti x={[-1, -0.25]} y={[0, 0.5]} />
</div>
</h2>
<button class="self-center" on:click={closeModal} aria-label={$i18n.t('Close')}>
<XMark className={'size-5'}>
<p class="sr-only">{$i18n.t('Close')}</p>
@@ -64,9 +64,9 @@
{#if changelog}
{#each Object.keys(changelog) as version}
<div class=" mb-3 pr-2">
<div class="font-semibold text-xl mb-1 dark:text-white">
<h3 class="font-semibold text-xl mb-1 dark:text-white m-0">
v{version} - {changelog[version].date}
</div>
</h3>
<hr class="border-gray-50/50 dark:border-gray-850/50 my-2" />

View File

@@ -67,6 +67,7 @@
<div class=" text-lg font-medium self-center">{$i18n.t('Import')}</div>
<button
class="self-center"
aria-label={$i18n.t('Close')}
on:click={() => {
show = false;
}}

View File

@@ -82,9 +82,9 @@
});
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
role="status"
aria-live="polite"
class="group relative flex gap-2.5 text-left min-w-[var(--width)] w-full dark:bg-gray-850 dark:text-white bg-white text-black border border-gray-100 dark:border-gray-800 rounded-3xl px-4 py-3.5 cursor-pointer select-none"
on:dragstart|preventDefault
on:pointerdown={onPointerDown}

View File

@@ -87,15 +87,15 @@
<div class="flex justify-center mt-8">
<div class="flex flex-col justify-center items-center">
<button
aria-labelledby="get-started"
aria-label={$i18n.t('Get started')}
class="relative z-20 flex p-1 rounded-full bg-white/5 hover:bg-white/10 transition font-medium text-sm"
on:click={() => {
getStartedHandler();
}}
>
<ArrowRightCircle className="size-6" />
<ArrowRightCircle className="size-6" aria-hidden="true" />
</button>
<div id="get-started" class="mt-1.5 font-primary text-base font-medium">
<div class="mt-1.5 font-primary text-base font-medium" aria-hidden="true">
{$i18n.t(`Get started`)}
</div>
</div>

View File

@@ -6,7 +6,7 @@
let selected = '';
</script>
<div class="min-w-[4.5rem] bg-gray-50 dark:bg-gray-950 flex gap-2.5 flex-col pt-8">
<nav aria-label="App navigation" class="min-w-[4.5rem] bg-gray-50 dark:bg-gray-950 flex gap-2.5 flex-col pt-8">
<div class="flex justify-center relative">
{#if selected === 'home'}
<div class="absolute top-0 left-0 flex h-full">
@@ -16,6 +16,7 @@
<Tooltip content="Home" placement="right">
<button
aria-label="Home"
class=" cursor-pointer {selected === 'home' ? 'rounded-2xl' : 'rounded-full'}"
on:click={() => {
selected = 'home';
@@ -44,6 +45,7 @@
</div>
{/if}
<button
aria-label="Chat"
class=" cursor-pointer bg-transparent"
on:click={() => {
selected = '';
@@ -63,4 +65,4 @@
<Plus className="size-4" strokeWidth="2" />
</button>
</div> -->
</div>
</nav>

View File

@@ -243,7 +243,7 @@
crossorigin="anonymous"
src="{WEBUI_BASE_URL}/static/favicon.png"
class="size-24 rounded-full"
alt=""
alt="{$WEBUI_NAME} logo"
/>
</div>
{/if}
@@ -342,8 +342,9 @@
placeholder={$i18n.t('Enter Your Password')}
autocomplete={mode === 'signup' ? 'new-password' : 'current-password'}
name="password"
screenReader={false}
screenReader={true}
required
aria-required="true"
/>
</div>
@@ -439,6 +440,7 @@
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 48 48"
class="size-6 mr-3"
aria-hidden="true"
>
<path
fill="#EA4335"
@@ -468,6 +470,7 @@
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 21 21"
class="size-6 mr-3"
aria-hidden="true"
>
<rect x="1" y="1" width="9" height="9" fill="#f25022" /><rect
x="1"
@@ -498,6 +501,7 @@
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="size-6 mr-3"
aria-hidden="true"
>
<path
fill="currentColor"
@@ -521,6 +525,7 @@
stroke-width="1.5"
stroke="currentColor"
class="size-6 mr-3"
aria-hidden="true"
>
<path
stroke-linecap="round"

View File

@@ -161,19 +161,22 @@
: 'max-w-5xl'} mx-auto"
>
<div class="px-3">
<div class=" text-2xl font-medium line-clamp-1">
<h1 class=" text-2xl font-medium line-clamp-1 m-0">
{title}
</div>
</h1>
<div class="flex text-sm justify-between items-center mt-1">
<div class="text-gray-400">
<time
class="text-gray-400"
datetime={new Date(chat?.chat?.timestamp || Date.now()).toISOString()}
>
{dayjs(chat.chat.timestamp).format('LLL')}
</div>
</time>
</div>
</div>
</div>
<div class=" h-full w-full flex flex-col py-2">
<div class=" h-full w-full flex flex-col py-2" role="main">
<div class="w-full">
<Messages
className="h-full flex pt-4 pb-8 "