Skip to content

Commit 24013bb

Browse files
committed
webui : improve accessibility for visually impaired people
1 parent 3198405 commit 24013bb

File tree

8 files changed

+119
-41
lines changed

8 files changed

+119
-41
lines changed

tools/server/public/index.html.gz

262 Bytes
Binary file not shown.

tools/server/webui/src/components/ChatMessage.tsx

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,20 @@ export default function ChatMessage({
8383

8484
if (!viewingChat) return null;
8585

86+
const isUser = msg.role === 'user';
87+
8688
return (
87-
<div className="group" id={id}>
89+
<div
90+
className="group"
91+
id={id}
92+
role="group"
93+
aria-description={`Message from ${msg.role}`}
94+
>
8895
<div
8996
className={classNames({
9097
chat: true,
91-
'chat-start': msg.role !== 'user',
92-
'chat-end': msg.role === 'user',
98+
'chat-start': !isUser,
99+
'chat-end': isUser,
93100
})}
94101
>
95102
{msg.extra && msg.extra.length > 0 && (
@@ -99,7 +106,7 @@ export default function ChatMessage({
99106
<div
100107
className={classNames({
101108
'chat-bubble markdown': true,
102-
'chat-bubble bg-transparent': msg.role !== 'user',
109+
'chat-bubble bg-transparent': !isUser,
103110
})}
104111
>
105112
{/* textarea for editing message */}
@@ -142,7 +149,7 @@ export default function ChatMessage({
142149
) : (
143150
<>
144151
{/* render message as markdown */}
145-
<div dir="auto">
152+
<div dir="auto" tabIndex={0}>
146153
{thought && (
147154
<ThoughtProcess
148155
isThinking={!!isThinking && !!isPending}
@@ -196,13 +203,18 @@ export default function ChatMessage({
196203
})}
197204
>
198205
{siblingLeafNodeIds && siblingLeafNodeIds.length > 1 && (
199-
<div className="flex gap-1 items-center opacity-60 text-sm">
206+
<div
207+
className="flex gap-1 items-center opacity-60 text-sm"
208+
role="navigation"
209+
aria-description={`Message version ${siblingCurrIdx + 1} of ${siblingLeafNodeIds.length}`}
210+
>
200211
<button
201212
className={classNames({
202213
'btn btn-sm btn-ghost p-1': true,
203214
'opacity-20': !prevSibling,
204215
})}
205216
onClick={() => prevSibling && onChangeSibling(prevSibling)}
217+
aria-label="Previous message version"
206218
>
207219
<ChevronLeftIcon className="h-4 w-4" />
208220
</button>
@@ -215,6 +227,7 @@ export default function ChatMessage({
215227
'opacity-20': !nextSibling,
216228
})}
217229
onClick={() => nextSibling && onChangeSibling(nextSibling)}
230+
aria-label="Next message version"
218231
>
219232
<ChevronRightIcon className="h-4 w-4" />
220233
</button>
@@ -223,7 +236,7 @@ export default function ChatMessage({
223236
{/* user message */}
224237
{msg.role === 'user' && (
225238
<BtnWithTooltips
226-
className="btn-mini show-on-hover w-8 h-8"
239+
className="btn-mini w-8 h-8"
227240
onClick={() => setEditingContent(msg.content)}
228241
disabled={msg.content === null}
229242
tooltipsContent="Edit message"
@@ -236,7 +249,7 @@ export default function ChatMessage({
236249
<>
237250
{!isPending && (
238251
<BtnWithTooltips
239-
className="btn-mini show-on-hover w-8 h-8"
252+
className="btn-mini w-8 h-8"
240253
onClick={() => {
241254
if (msg.content !== null) {
242255
onRegenerateMessage(msg as Message);
@@ -250,10 +263,7 @@ export default function ChatMessage({
250263
)}
251264
</>
252265
)}
253-
<CopyButton
254-
className="btn-mini show-on-hover w-8 h-8"
255-
content={msg.content}
256-
/>
266+
<CopyButton className="btn-mini w-8 h-8" content={msg.content} />
257267
</div>
258268
)}
259269
</div>
@@ -271,6 +281,8 @@ function ThoughtProcess({
271281
}) {
272282
return (
273283
<div
284+
role="button"
285+
aria-label="Toggle thought process display"
274286
tabIndex={0}
275287
className={classNames({
276288
'collapse bg-none': true,
@@ -292,7 +304,11 @@ function ThoughtProcess({
292304
)}
293305
</div>
294306
</div>
295-
<div className="collapse-content text-base-content/70 text-sm p-1">
307+
<div
308+
className="collapse-content text-base-content/70 text-sm p-1"
309+
tabIndex={0}
310+
aria-description="Thought process content"
311+
>
296312
<div className="border-l-2 border-base-content/20 pl-4 mb-4">
297313
<MarkdownDisplay content={content} />
298314
</div>

tools/server/webui/src/components/ChatScreen.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,11 @@ export default function ChatScreen() {
279279
function ServerInfo() {
280280
const { serverProps } = useAppContext();
281281
return (
282-
<div className="card card-sm shadow-sm border-1 border-base-content/20 text-base-content/70 mb-6">
282+
<div
283+
className="card card-sm shadow-sm border-1 border-base-content/20 text-base-content/70 mb-6"
284+
tabIndex={0}
285+
aria-description="Server information"
286+
>
283287
<div className="card-body">
284288
<b>Server Info</b>
285289
<p>
@@ -310,6 +314,8 @@ function ChatInput({
310314

311315
return (
312316
<div
317+
role="group"
318+
aria-label="Chat input"
313319
className={classNames({
314320
'flex items-end pt-8 pb-6 sticky bottom-0 bg-base-100': true,
315321
'opacity-50': isDrag, // simply visual feedback to inform user that the file will be accepted
@@ -378,13 +384,15 @@ function ChatInput({
378384
'btn w-8 h-8 p-0 rounded-full': true,
379385
'btn-disabled': isGenerating,
380386
})}
387+
aria-label="Upload file"
388+
tabIndex={0}
389+
role="button"
381390
>
382391
<PaperClipIcon className="h-5 w-5" />
383392
</label>
384393
<input
385394
id="file-upload"
386395
type="file"
387-
className="hidden"
388396
disabled={isGenerating}
389397
{...getInputProps()}
390398
hidden
@@ -400,6 +408,7 @@ function ChatInput({
400408
<button
401409
className="btn btn-primary w-8 h-8 p-0 rounded-full"
402410
onClick={onSend}
411+
aria-label="Send message"
403412
>
404413
<ArrowUpIcon className="h-5 w-5" />
405414
</button>

tools/server/webui/src/components/Header.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,34 @@ export default function Header() {
3838

3939
{/* action buttons (top right) */}
4040
<div className="flex items-center">
41-
<div className="tooltip tooltip-bottom" data-tip="Settings">
42-
<button className="btn" onClick={() => setShowSettings(true)}>
41+
<div
42+
className="tooltip tooltip-bottom"
43+
data-tip="Settings"
44+
aria-disabled={true}
45+
>
46+
<button
47+
className="btn"
48+
onClick={() => setShowSettings(true)}
49+
aria-label="Settings"
50+
>
4351
{/* settings button */}
4452
<Cog8ToothIcon className="w-5 h-5" />
4553
</button>
4654
</div>
4755

4856
{/* theme controller is copied from https://daisyui.com/components/theme-controller/ */}
49-
<div className="tooltip tooltip-bottom" data-tip="Themes">
57+
<div
58+
className="tooltip tooltip-bottom"
59+
data-tip="Themes"
60+
aria-disabled={true}
61+
>
5062
<div className="dropdown dropdown-end dropdown-bottom">
51-
<div tabIndex={0} role="button" className="btn m-1">
63+
<div
64+
tabIndex={0}
65+
role="button"
66+
className="btn m-1"
67+
aria-label="Themes"
68+
>
5269
<MoonIcon className="w-5 h-5" />
5370
</div>
5471
<ul

tools/server/webui/src/components/SettingDialog.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -325,14 +325,20 @@ export default function SettingDialog({
325325
};
326326

327327
return (
328-
<dialog className={classNames({ modal: true, 'modal-open': show })}>
328+
<dialog
329+
className={classNames({ modal: true, 'modal-open': show })}
330+
aria-label="Settings dialog"
331+
>
329332
<div className="modal-box w-11/12 max-w-3xl">
330333
<h3 className="text-lg font-bold mb-6">Settings</h3>
331334
<div className="flex flex-col md:flex-row h-[calc(90vh-12rem)]">
332335
{/* Left panel, showing sections - Desktop version */}
333-
<div className="hidden md:flex flex-col items-stretch pr-4 mr-4 border-r-2 border-base-200">
336+
<div
337+
className="hidden md:flex flex-col items-stretch pr-4 mr-4 border-r-2 border-base-200"
338+
role="complementary"
339+
>
334340
{SETTING_SECTIONS.map((section, idx) => (
335-
<div
341+
<button
336342
key={idx}
337343
className={classNames({
338344
'btn btn-ghost justify-start font-normal w-44 mb-1': true,
@@ -342,12 +348,16 @@ export default function SettingDialog({
342348
dir="auto"
343349
>
344350
{section.title}
345-
</div>
351+
</button>
346352
))}
347353
</div>
348354

349355
{/* Left panel, showing sections - Mobile version */}
350-
<div className="md:hidden flex flex-row gap-2 mb-4">
356+
{/* This menu is skipped on a11y, otherwise it's repeated the desktop version */}
357+
<div
358+
className="md:hidden flex flex-row gap-2 mb-4"
359+
aria-disabled={true}
360+
>
351361
<details className="dropdown">
352362
<summary className="btn bt-sm w-full m-1">
353363
{SETTING_SECTIONS[sectionIdx].title}

tools/server/webui/src/components/Sidebar.tsx

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,44 +50,64 @@ export default function Sidebar() {
5050
id="toggle-drawer"
5151
type="checkbox"
5252
className="drawer-toggle"
53+
aria-label="Toggle sidebar"
5354
defaultChecked
5455
/>
5556

56-
<div className="drawer-side h-screen lg:h-screen z-50 lg:max-w-64">
57+
<div
58+
className="drawer-side h-screen lg:h-screen z-50 lg:max-w-64"
59+
role="complementary"
60+
aria-label="Sidebar"
61+
tabIndex={0}
62+
>
5763
<label
5864
htmlFor="toggle-drawer"
59-
aria-label="close sidebar"
65+
aria-label="Close sidebar"
6066
className="drawer-overlay"
6167
></label>
6268
<div className="flex flex-col bg-base-200 min-h-full max-w-64 py-4 px-4">
6369
<div className="flex flex-row items-center justify-between mb-4 mt-4">
64-
<h2 className="font-bold ml-4">Conversations</h2>
70+
<h2 className="font-bold ml-4" role="heading">
71+
Conversations
72+
</h2>
6573

6674
{/* close sidebar button */}
67-
<label htmlFor="toggle-drawer" className="btn btn-ghost lg:hidden">
75+
<label
76+
htmlFor="toggle-drawer"
77+
className="btn btn-ghost lg:hidden"
78+
aria-label="Close sidebar"
79+
role="button"
80+
tabIndex={0}
81+
>
6882
<XMarkIcon className="w-5 h-5" />
6983
</label>
7084
</div>
7185

7286
{/* new conversation button */}
73-
<div
87+
<button
7488
className={classNames({
7589
'btn btn-ghost justify-start px-2': true,
7690
'btn-soft': !currConv,
7791
})}
7892
onClick={() => navigate('/')}
93+
aria-label="New conversation"
7994
>
8095
<PencilSquareIcon className="w-5 h-5" />
8196
New conversation
82-
</div>
97+
</button>
8398

8499
{/* list of conversations */}
85100
{groupedConv.map((group, i) => (
86-
<div key={i}>
101+
<div key={i} role="group">
87102
{/* group name (by date) */}
88103
{group.title ? (
89104
// we use btn class here to make sure that the padding/margin are aligned with the other items
90-
<b className="btn btn-ghost btn-xs bg-none btn-disabled block text-xs text-base-content text-start px-2 mb-0 mt-6 font-bold">
105+
<b
106+
className="btn btn-ghost btn-xs bg-none btn-disabled block text-xs text-base-content text-start px-2 mb-0 mt-6 font-bold"
107+
role="note"
108+
aria-description={group.title}
109+
tabIndex={0}
110+
>
91111
{group.title}
92112
</b>
93113
) : (
@@ -184,20 +204,23 @@ function ConversationItem({
184204
}) {
185205
return (
186206
<div
207+
role="menuitem"
208+
tabIndex={0}
209+
aria-label={conv.name}
187210
className={classNames({
188211
'group flex flex-row btn btn-ghost justify-start items-center font-normal px-2 h-9':
189212
true,
190213
'btn-soft': isCurrConv,
191214
})}
192215
>
193-
<div
216+
<button
194217
key={conv.id}
195218
className="w-full overflow-hidden truncate text-start"
196219
onClick={onSelect}
197220
dir="auto"
198221
>
199222
{conv.name}
200-
</div>
223+
</button>
201224
<div className="dropdown dropdown-end h-5">
202225
<BtnWithTooltips
203226
// on mobile, we always show the ellipsis icon
@@ -211,22 +234,23 @@ function ConversationItem({
211234
</BtnWithTooltips>
212235
{/* dropdown menu */}
213236
<ul
237+
aria-label="More options"
214238
tabIndex={0}
215239
className="dropdown-content menu bg-base-100 rounded-box z-[1] p-2 shadow"
216240
>
217-
<li onClick={onRename}>
241+
<li onClick={onRename} tabIndex={0}>
218242
<a>
219243
<PencilIcon className="w-4 h-4" />
220244
Rename
221245
</a>
222246
</li>
223-
<li onClick={onDownload}>
247+
<li onClick={onDownload} tabIndex={0}>
224248
<a>
225249
<ArrowDownTrayIcon className="w-4 h-4" />
226250
Download
227251
</a>
228252
</li>
229-
<li className="text-error" onClick={onDelete}>
253+
<li className="text-error" onClick={onDelete} tabIndex={0}>
230254
<a>
231255
<TrashIcon className="w-4 h-4" />
232256
Delete

tools/server/webui/src/index.scss

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ html {
3434
/* TODO: fix markdown table */
3535
}
3636

37-
.show-on-hover {
38-
@apply md:opacity-0 md:group-hover:opacity-100;
39-
}
4037
.btn-mini {
4138
@apply cursor-pointer;
4239
}

0 commit comments

Comments
 (0)