Skip to content

[codex] Fix web interface review issues#3

Merged
jamiepine merged 1 commit intospacedriveapp:mainfrom
vsumner:codex/web-guidelines-fixes
Apr 12, 2026
Merged

[codex] Fix web interface review issues#3
jamiepine merged 1 commit intospacedriveapp:mainfrom
vsumner:codex/web-guidelines-fixes

Conversation

@vsumner
Copy link
Copy Markdown
Contributor

@vsumner vsumner commented Apr 11, 2026

Summary

  • Remove unsafe raw HTML rendering from AI Markdown content.
  • Improve accessibility for form controls, icon-only controls, explorer grid items, and task row actions.
  • Replace reviewed transition-all usages with explicit transitions and add stronger focus-visible states.

Validation

  • bun install --frozen-lockfile
  • bun run typecheck in packages/ai, packages/forms, packages/explorer, and packages/primitives
  • bun run build in packages/ai, packages/forms, packages/explorer, and packages/primitives
  • git diff --check

Notes

Root bun run typecheck reaches the changed packages successfully, then fails in .storybook because its typecheck command invokes bare tsc --noEmit and prints TypeScript help. examples/showcase also has existing type errors around Tooltip, ToolCall, and duplicate react-hook-form types.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dc8b6330-1697-4c6e-84f6-974ba6ffaa89

📥 Commits

Reviewing files that changed from the base of the PR and between 0982540 and 5350cce.

📒 Files selected for processing (16)
  • packages/ai/src/ChatComposer.tsx
  • packages/ai/src/Markdown.tsx
  • packages/ai/src/MessageBubble.tsx
  • packages/ai/src/TaskCreateForm.tsx
  • packages/ai/src/TaskRow.tsx
  • packages/explorer/src/GridItem.tsx
  • packages/explorer/src/TagPill.tsx
  • packages/forms/src/Form.tsx
  • packages/primitives/src/Button.tsx
  • packages/primitives/src/CircleButton.tsx
  • packages/primitives/src/Input.tsx
  • packages/primitives/src/NumberStepper.tsx
  • packages/primitives/src/SearchBar.tsx
  • packages/primitives/src/Select.tsx
  • packages/primitives/src/ShinyButton.tsx
  • packages/primitives/src/TabBar.tsx
💤 Files with no reviewable changes (1)
  • packages/ai/src/Markdown.tsx
✅ Files skipped from review due to trivial changes (7)
  • packages/primitives/src/ShinyButton.tsx
  • packages/ai/src/MessageBubble.tsx
  • packages/primitives/src/Select.tsx
  • packages/primitives/src/CircleButton.tsx
  • packages/primitives/src/SearchBar.tsx
  • packages/explorer/src/TagPill.tsx
  • packages/primitives/src/NumberStepper.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/primitives/src/Button.tsx
  • packages/primitives/src/TabBar.tsx
  • packages/ai/src/TaskRow.tsx
  • packages/explorer/src/GridItem.tsx

Walkthrough

Accessibility and styling updates across components: added focus-visible rings and aria-labels, narrowed transition utilities, semantic element/keyboard handling adjustments, FormControl refactor to clone children and merge aria-describedby, and removal of the rehype-raw plugin from Markdown rendering.

Changes

Cohort / File(s) Summary
Focus-visible & input styling
packages/ai/src/ChatComposer.tsx, packages/ai/src/TaskCreateForm.tsx, packages/primitives/src/Button.tsx, packages/explorer/src/GridItem.tsx
Added focus-visible:ring-2 focus-visible:ring-accent and related focus-visible/ring classes to inputs and interactive roots, replacing prior focus suppression in several components.
Aria labels & keyboard semantics
packages/ai/src/MessageBubble.tsx, packages/ai/src/ChatComposer.tsx, packages/ai/src/TaskRow.tsx, packages/primitives/src/Input.tsx, packages/primitives/src/SearchBar.tsx, packages/primitives/src/TabBar.tsx, packages/primitives/src/CircleButton.tsx
Added or updated aria-label attributes on buttons; changed interactive wrappers and keyboard handling (e.g., TaskRow: <button><div> with role/button and key handlers; TabBar close control → real button).
Transition scoping (replace transition-all)
packages/primitives/src/CircleButton.tsx, packages/primitives/src/Input.tsx, packages/primitives/src/NumberStepper.tsx, packages/primitives/src/Select.tsx, packages/primitives/src/ShinyButton.tsx, packages/primitives/src/TabBar.tsx, packages/explorer/src/TagPill.tsx, packages/primitives/src/SearchBar.tsx
Replaced broad transition-all with targeted Tailwind transition lists (e.g., transition-[width], transition-[background-color,...], transition-colors) to limit animated properties.
Structural / API changes
packages/forms/src/Form.tsx, packages/explorer/src/GridItem.tsx, packages/ai/src/TaskRow.tsx
Refactored FormControl to require a single React child and clone it while merging aria-describedby/IDs; adjusted GridItem to render as <button type="button">; TaskRow made non-button root with conditional interactivity and explicit keyboard handling.
Markdown processing
packages/ai/src/Markdown.tsx
Removed rehype-raw import and stopped passing rehypePlugins={[rehypeRaw]} to ReactMarkdown, disabling raw HTML rehype processing.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title '[codex] Fix web interface review issues' is vague and doesn't clearly convey the main changes, using a generic term 'review issues' that obscures the actual scope of work. Consider a more specific title that highlights the primary changes, such as '[codex] Improve accessibility and transitions in UI components' or '[codex] Remove unsafe HTML rendering and enhance focus states'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is well-related to the changeset, providing a clear summary of the three main objectives and validation steps performed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vsumner vsumner marked this pull request as ready for review April 11, 2026 23:26
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/primitives/src/TabBar.tsx (1)

95-103: ⚠️ Potential issue | 🟠 Major

Close control is still not an accessible, semantic action target.

Line 95-Line 103 uses a clickable <span> inside a parent <button>, which leaves the close action without proper keyboard/button semantics and creates an interactive-within-interactive pattern. Please make the close affordance a real button control with an explicit accessible name, and place it outside the parent button’s interactive element structure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/primitives/src/TabBar.tsx` around lines 95 - 103, The close
affordance is implemented as a clickable <span> nested inside a parent
interactive <button>; replace that span with a real <button> element (e.g., the
close control in TabBar.tsx where the span is rendered), move it so it is not a
descendant of the parent tab button to avoid interactive-within-interactive,
give it an explicit accessible name via aria-label (or visually hidden text)
such as "Close tab", preserve the existing onClick handler behavior (including
e.stopPropagation()) on the new button, and keep the same styling/positioning so
the visual layout is unchanged while ensuring proper keyboard and semantic
accessibility.
packages/ai/src/TaskCreateForm.tsx (1)

48-57: ⚠️ Potential issue | 🟠 Major

Add programmatic labels for the title and description fields.

The controls at Line 57 and Line 87 still depend on placeholder text only. That’s not a sufficient accessible name and can break form usability for screen-reader users.

♿ Suggested fix
-import { useCallback, useState } from "react";
+import { useCallback, useId, useState } from "react";
@@
 export function TaskCreateForm({
@@
 }: TaskCreateFormProps) {
+	const titleInputId = useId();
+	const descriptionInputId = useId();
 	const [title, setTitle] = useState("");
 	const [description, setDescription] = useState("");
@@
 		<div className={clsx("flex flex-col gap-2", className)}>
 			<div className="flex items-center gap-2">
+				<label htmlFor={titleInputId} className="sr-only">
+					Task title
+				</label>
 				<input
+					id={titleInputId}
 					type="text"
@@
 			</div>
+			<label htmlFor={descriptionInputId} className="sr-only">
+				Description
+			</label>
 			<textarea
+				id={descriptionInputId}
 				value={description}

Also applies to: 82-87

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ai/src/TaskCreateForm.tsx` around lines 48 - 57, The title and
description inputs in TaskCreateForm lack programmatic labels and rely only on
placeholders; add accessible names by giving each control an id and associating
a visible or visually-hidden <label> (or aria-label/aria-labelledby) with that
id for both the title input (controlled by title/setTitle and onKeyDown handling
calling handleSubmit/onCancel) and the description textarea (controlled by
description/setDescription); ensure the ids are unique (e.g., title-input,
description-input) and update any tests/styles that depend on markup so screen
readers receive a proper label for each field.
🧹 Nitpick comments (1)
packages/ai/src/TaskRow.tsx (1)

33-38: Refine keyboard activation parity for the row button role.

Line 34 currently activates on keydown for both Space and Enter, repeating while the key is held. Align with native button semantics: Enter activates on keydown, Space activates on keyup, and ignore repeat events.

Suggested patch
 	const isRowInteractive = typeof onClick === "function";
 	const handleRowKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
-		if (e.key === "Enter" || e.key === " ") {
+		if (e.repeat) return;
+		if (e.key === "Enter") {
 			e.preventDefault();
 			onClick?.(task);
 		}
 	};
+	const handleRowKeyUp = (e: React.KeyboardEvent<HTMLDivElement>) => {
+		if (e.repeat) return;
+		if (e.key === " ") {
+			e.preventDefault();
+			onClick?.(task);
+		}
+	};
 
 	return (
 		<div
-			onKeyDown={isRowInteractive ? handleRowKeyDown : undefined}
+			onKeyDown={isRowInteractive ? handleRowKeyDown : undefined}
+			onKeyUp={isRowInteractive ? handleRowKeyUp : undefined}

Also applies to: line 45

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ai/src/TaskRow.tsx` around lines 33 - 38, The row keyboard handlers
currently activate on keydown for both Space and Enter and don't ignore repeat
events; update handleRowKeyDown to ignore e.repeat and only activate on Enter
(preventDefault + onClick(task) on keydown for Enter), and add/adjust
handleRowKeyUp (or the existing keyup handler referenced) to activate on Space
on keyup (preventDefault + onClick(task)) while also ignoring repeat events;
ensure both handlers use the same onClick?.(task) call and maintain
accessibility semantics for role="button".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/forms/src/Form.tsx`:
- Around line 93-103: The code currently overwrites any aria-describedby on the
child when cloning (computed as describedBy using formDescriptionId and
formMessageId); update the clone to preserve and merge the child's existing
aria-describedby with describedBy instead of replacing it: read
existingDescribed = (children.props && children.props['aria-describedby']) as
string | undefined, split and combine unique tokens from existingDescribed and
describedBy (space-separated), trim and join them into a final string, and pass
that merged value to cloneElement along with id: formItemId and aria-invalid:
!!error so that cloneElement(children, { id: formItemId, 'aria-describedby':
mergedDescribed, 'aria-invalid': !!error }) preserves any child-provided
descriptions.

---

Outside diff comments:
In `@packages/ai/src/TaskCreateForm.tsx`:
- Around line 48-57: The title and description inputs in TaskCreateForm lack
programmatic labels and rely only on placeholders; add accessible names by
giving each control an id and associating a visible or visually-hidden <label>
(or aria-label/aria-labelledby) with that id for both the title input
(controlled by title/setTitle and onKeyDown handling calling
handleSubmit/onCancel) and the description textarea (controlled by
description/setDescription); ensure the ids are unique (e.g., title-input,
description-input) and update any tests/styles that depend on markup so screen
readers receive a proper label for each field.

In `@packages/primitives/src/TabBar.tsx`:
- Around line 95-103: The close affordance is implemented as a clickable <span>
nested inside a parent interactive <button>; replace that span with a real
<button> element (e.g., the close control in TabBar.tsx where the span is
rendered), move it so it is not a descendant of the parent tab button to avoid
interactive-within-interactive, give it an explicit accessible name via
aria-label (or visually hidden text) such as "Close tab", preserve the existing
onClick handler behavior (including e.stopPropagation()) on the new button, and
keep the same styling/positioning so the visual layout is unchanged while
ensuring proper keyboard and semantic accessibility.

---

Nitpick comments:
In `@packages/ai/src/TaskRow.tsx`:
- Around line 33-38: The row keyboard handlers currently activate on keydown for
both Space and Enter and don't ignore repeat events; update handleRowKeyDown to
ignore e.repeat and only activate on Enter (preventDefault + onClick(task) on
keydown for Enter), and add/adjust handleRowKeyUp (or the existing keyup handler
referenced) to activate on Space on keyup (preventDefault + onClick(task)) while
also ignoring repeat events; ensure both handlers use the same onClick?.(task)
call and maintain accessibility semantics for role="button".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c39793d0-46dc-49d9-9e03-9cec5157e985

📥 Commits

Reviewing files that changed from the base of the PR and between bc3ae93 and 0982540.

📒 Files selected for processing (16)
  • packages/ai/src/ChatComposer.tsx
  • packages/ai/src/Markdown.tsx
  • packages/ai/src/MessageBubble.tsx
  • packages/ai/src/TaskCreateForm.tsx
  • packages/ai/src/TaskRow.tsx
  • packages/explorer/src/GridItem.tsx
  • packages/explorer/src/TagPill.tsx
  • packages/forms/src/Form.tsx
  • packages/primitives/src/Button.tsx
  • packages/primitives/src/CircleButton.tsx
  • packages/primitives/src/Input.tsx
  • packages/primitives/src/NumberStepper.tsx
  • packages/primitives/src/SearchBar.tsx
  • packages/primitives/src/Select.tsx
  • packages/primitives/src/ShinyButton.tsx
  • packages/primitives/src/TabBar.tsx
💤 Files with no reviewable changes (1)
  • packages/ai/src/Markdown.tsx

@vsumner vsumner force-pushed the codex/web-guidelines-fixes branch from 0982540 to 5350cce Compare April 11, 2026 23:38
@jamiepine jamiepine merged commit 54e01a5 into spacedriveapp:main Apr 12, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants