diff options
| author | Nolan Darilek <[email protected]> | 2026-01-22 05:10:53 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-01-22 05:10:53 -0600 |
| commit | 3435327bc074a7ba8c3fe8939c97de54bbdefd65 (patch) | |
| tree | d906b07c16e8e90808e5f78b72f72e8737f53eeb /packages/ui/src | |
| parent | 8a043edfd5a504421301cc183eedf4ecdb4c57b0 (diff) | |
| download | opencode-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.tsx | 2 | ||||
| -rw-r--r-- | packages/ui/src/components/image-preview.tsx | 2 | ||||
| -rw-r--r-- | packages/ui/src/components/list.tsx | 2 | ||||
| -rw-r--r-- | packages/ui/src/components/message-part.tsx | 1 | ||||
| -rw-r--r-- | packages/ui/src/components/popover.tsx | 16 | ||||
| -rw-r--r-- | packages/ui/src/components/session-turn.tsx | 14 | ||||
| -rw-r--r-- | packages/ui/src/components/text-field.tsx | 1 | ||||
| -rw-r--r-- | packages/ui/src/components/toast.tsx | 2 |
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>) { |
