summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src
diff options
context:
space:
mode:
authorNolan Darilek <[email protected]>2026-01-22 05:10:53 -0600
committerGitHub <[email protected]>2026-01-22 05:10:53 -0600
commit3435327bc074a7ba8c3fe8939c97de54bbdefd65 (patch)
treed906b07c16e8e90808e5f78b72f72e8737f53eeb /packages/ui/src
parent8a043edfd5a504421301cc183eedf4ecdb4c57b0 (diff)
downloadopencode-3435327bc074a7ba8c3fe8939c97de54bbdefd65.tar.gz
opencode-3435327bc074a7ba8c3fe8939c97de54bbdefd65.zip
fix(app): session screen accessibility improvements (#9907)
Diffstat (limited to 'packages/ui/src')
-rw-r--r--packages/ui/src/components/dialog.tsx2
-rw-r--r--packages/ui/src/components/image-preview.tsx2
-rw-r--r--packages/ui/src/components/list.tsx2
-rw-r--r--packages/ui/src/components/message-part.tsx1
-rw-r--r--packages/ui/src/components/popover.tsx16
-rw-r--r--packages/ui/src/components/session-turn.tsx14
-rw-r--r--packages/ui/src/components/text-field.tsx1
-rw-r--r--packages/ui/src/components/toast.tsx2
8 files changed, 24 insertions, 16 deletions
diff --git a/packages/ui/src/components/dialog.tsx b/packages/ui/src/components/dialog.tsx
index 4b871732d..cff9edaf2 100644
--- a/packages/ui/src/components/dialog.tsx
+++ b/packages/ui/src/components/dialog.tsx
@@ -40,7 +40,7 @@ export function Dialog(props: DialogProps) {
<Switch>
<Match when={props.action}>{props.action}</Match>
<Match when={true}>
- <Kobalte.CloseButton data-slot="dialog-close-button" as={IconButton} icon="close" variant="ghost" />
+ <Kobalte.CloseButton data-slot="dialog-close-button" as={IconButton} icon="close" variant="ghost" aria-label="Close" />
</Match>
</Switch>
</div>
diff --git a/packages/ui/src/components/image-preview.tsx b/packages/ui/src/components/image-preview.tsx
index 88bf38980..1ab868576 100644
--- a/packages/ui/src/components/image-preview.tsx
+++ b/packages/ui/src/components/image-preview.tsx
@@ -14,7 +14,7 @@ export function ImagePreview(props: ImagePreviewProps) {
<div data-slot="image-preview-container">
<Kobalte.Content data-slot="image-preview-content">
<div data-slot="image-preview-header">
- <Kobalte.CloseButton data-slot="image-preview-close" as={IconButton} icon="close" variant="ghost" />
+ <Kobalte.CloseButton data-slot="image-preview-close" as={IconButton} icon="close" variant="ghost" aria-label="Close" />
</div>
<div data-slot="image-preview-body">
<img src={props.src} alt={props.alt ?? i18n.t("ui.imagePreview.alt")} data-slot="image-preview-image" />
diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx
index aeb2769d6..41e528e79 100644
--- a/packages/ui/src/components/list.tsx
+++ b/packages/ui/src/components/list.tsx
@@ -230,7 +230,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
/>
</div>
<Show when={internalFilter()}>
- <IconButton icon="circle-x" variant="ghost" onClick={() => setInternalFilter("")} />
+ <IconButton icon="circle-x" variant="ghost" onClick={() => setInternalFilter("")} aria-label="Clear filter" />
</Show>
</div>
{searchAction()}
diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx
index d1788f1df..d639c5224 100644
--- a/packages/ui/src/components/message-part.tsx
+++ b/packages/ui/src/components/message-part.tsx
@@ -429,6 +429,7 @@ export function UserMessageDisplay(props: { message: UserMessage; parts: PartTyp
event.stopPropagation()
handleCopy()
}}
+ aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")}
/>
</Tooltip>
</div>
diff --git a/packages/ui/src/components/popover.tsx b/packages/ui/src/components/popover.tsx
index 3262098e5..3de7cb516 100644
--- a/packages/ui/src/components/popover.tsx
+++ b/packages/ui/src/components/popover.tsx
@@ -1,21 +1,23 @@
import { Popover as Kobalte } from "@kobalte/core/popover"
-import { ComponentProps, JSXElement, ParentProps, Show, splitProps } from "solid-js"
+import { ComponentProps, JSXElement, ParentProps, Show, splitProps, ValidComponent } from "solid-js"
import { IconButton } from "./icon-button"
-export interface PopoverProps extends ParentProps, Omit<ComponentProps<typeof Kobalte>, "children"> {
- trigger: JSXElement
+export interface PopoverProps<T extends ValidComponent = "div"> extends ParentProps, Omit<ComponentProps<typeof Kobalte>, "children"> {
+ trigger?: JSXElement
+ triggerAs?: T
+ triggerProps?: ComponentProps<T>
title?: JSXElement
description?: JSXElement
class?: ComponentProps<"div">["class"]
classList?: ComponentProps<"div">["classList"]
}
-export function Popover(props: PopoverProps) {
- const [local, rest] = splitProps(props, ["trigger", "title", "description", "class", "classList", "children"])
+export function Popover<T extends ValidComponent = "div">(props: PopoverProps<T>) {
+ const [local, rest] = splitProps(props, ["trigger", "triggerAs", "triggerProps", "title", "description", "class", "classList", "children"])
return (
<Kobalte gutter={4} {...rest}>
- <Kobalte.Trigger as="div" data-slot="popover-trigger">
+ <Kobalte.Trigger as={local.triggerAs ?? "div"} data-slot="popover-trigger" {...(local.triggerProps as any)}>
{local.trigger}
</Kobalte.Trigger>
<Kobalte.Portal>
@@ -30,7 +32,7 @@ export function Popover(props: PopoverProps) {
<Show when={local.title}>
<div data-slot="popover-header">
<Kobalte.Title data-slot="popover-title">{local.title}</Kobalte.Title>
- <Kobalte.CloseButton data-slot="popover-close-button" as={IconButton} icon="close" variant="ghost" />
+ <Kobalte.CloseButton data-slot="popover-close-button" as={IconButton} icon="close" variant="ghost" aria-label="Close" />
</div>
</Show>
<Show when={local.description}>
diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx
index 06e421882..1cd210499 100644
--- a/packages/ui/src/components/session-turn.tsx
+++ b/packages/ui/src/components/session-turn.tsx
@@ -506,13 +506,13 @@ export function SessionTurn(
</Match>
<Match when={true}>
<Show when={attachmentParts().length > 0}>
- <div data-slot="session-turn-attachments">
+ <div data-slot="session-turn-attachments" aria-live="off">
<Message message={msg()} parts={attachmentParts()} />
</div>
</Show>
<div data-slot="session-turn-sticky" ref={setStickyRef}>
{/* User Message */}
- <div data-slot="session-turn-message-content">
+ <div data-slot="session-turn-message-content" aria-live="off">
<Message message={msg()} parts={stickyParts()} />
</div>
@@ -525,6 +525,7 @@ export function SessionTurn(
variant="ghost"
size="small"
onClick={props.onStepsExpandedToggle ?? (() => {})}
+ aria-expanded={props.stepsExpanded}
>
<Show when={working()}>
<Spinner />
@@ -552,8 +553,8 @@ export function SessionTurn(
<Match when={props.stepsExpanded}>{i18n.t("ui.sessionTurn.steps.hide")}</Match>
<Match when={!props.stepsExpanded}>{i18n.t("ui.sessionTurn.steps.show")}</Match>
</Switch>
- <span>·</span>
- <span>{store.duration}</span>
+ <span aria-hidden="true">·</span>
+ <span aria-live="off">{store.duration}</span>
<Show when={assistantMessages().length > 0}>
<Icon name="chevron-grabber-vertical" size="small" />
</Show>
@@ -563,7 +564,7 @@ export function SessionTurn(
</div>
{/* Response */}
<Show when={props.stepsExpanded && assistantMessages().length > 0}>
- <div data-slot="session-turn-collapsible-content-inner">
+ <div data-slot="session-turn-collapsible-content-inner" aria-hidden={working()}>
<For each={assistantMessages()}>
{(assistantMessage) => (
<AssistantMessageItem
@@ -589,6 +590,9 @@ export function SessionTurn(
</div>
</Show>
{/* Response */}
+ <div class="sr-only" aria-live="polite">
+ {!working() && response() ? response() : ""}
+ </div>
<Show when={!working() && (response() || hasDiffs())}>
<div data-slot="session-turn-summary-section">
<div data-slot="session-turn-summary-header">
diff --git a/packages/ui/src/components/text-field.tsx b/packages/ui/src/components/text-field.tsx
index a4eafa561..0694e421e 100644
--- a/packages/ui/src/components/text-field.tsx
+++ b/packages/ui/src/components/text-field.tsx
@@ -103,6 +103,7 @@ export function TextField(props: TextFieldProps) {
variant="ghost"
onClick={handleCopy}
data-slot="input-copy-button"
+ aria-label={copied() ? i18n.t("ui.textField.copied") : i18n.t("ui.textField.copyToClipboard")}
/>
</Tooltip>
</Show>
diff --git a/packages/ui/src/components/toast.tsx b/packages/ui/src/components/toast.tsx
index f34c46d42..9493e50ca 100644
--- a/packages/ui/src/components/toast.tsx
+++ b/packages/ui/src/components/toast.tsx
@@ -62,7 +62,7 @@ function ToastActions(props: ComponentProps<"div">) {
}
function ToastCloseButton(props: ToastCloseButtonProps & ComponentProps<"button">) {
- return <Kobalte.CloseButton data-slot="toast-close-button" as={IconButton} icon="close" variant="ghost" {...props} />
+ return <Kobalte.CloseButton data-slot="toast-close-button" as={IconButton} icon="close" variant="ghost" aria-label="Dismiss" {...props} />
}
function ToastProgressTrack(props: ComponentProps<typeof Kobalte.ProgressTrack>) {