Skip to content

Comments

Dhan experiments#9

Open
Lemirq wants to merge 3 commits intomasterfrom
dhan-experiments
Open

Dhan experiments#9
Lemirq wants to merge 3 commits intomasterfrom
dhan-experiments

Conversation

@Lemirq
Copy link
Owner

@Lemirq Lemirq commented Jan 10, 2026

Summary by CodeRabbit

  • New Features
    • Directional, smooth animated tab transitions with preserved layout and reset behavior when context changes.
    • Animated sidebar elements: motion-enabled back button, per-item entrance/staggered animations for agent list and transcript, and improved scroll/overflow handling.
  • Chores
    • Simplified build configuration in project settings.

✏️ Tip: You can customize this high-level summary in your review settings.

…ate management

- Added `react-photo-view` dependency for improved image handling.
- Implemented tab state management in IntersectionSidebar to reset to the overview tab on hotspot change.
- Enhanced PersonaSidebar with animated transitions for persona changes and message display.
- Updated Next.js configuration to remove unnecessary turbopack settings.
@coderabbitai
Copy link

coderabbitai bot commented Jan 10, 2026

📝 Walkthrough

Walkthrough

IntersectionSidebar gained controlled tab state (activeTab) with directional animations and per-tab animated panels; PersonaSidebar now uses motion-based entrance/staggered animations for list items, transcripts, and interactive controls. next.config.ts had turbopack.root removed and nextConfig set to an empty object.

Changes

Cohort / File(s) Summary
Intersection Sidebar (tabs & animations)
components/sidebar/intersection-sidebar.tsx
Added local activeTab state and direction, computed tab-order-based animation direction, replaced static Tabs with controlled Tabs, and introduced AnimatePresence + motion variants for enter/center/exit transitions across overview, audit, reimagine panels.
Persona Sidebar (motion & staggered items)
components/sidebar/persona-sidebar.tsx
Wrapped header, back button, agent list, conversation and transcript items with motion elements; added hover/tap animations, per-item initial/animate props and index-based staggered delays; constrained overflow and updated layout wrappers.
Next.js config
next.config.ts
Removed turbopack.root config; nextConfig reassigned to an empty object while keeping the NextConfig type and same default export.

Sequence Diagram(s)

(Skipped — changes are UI-focused animations within components and do not introduce a multi-component sequential flow suitable for a sequence diagram.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Voice agents #6: Modifies components/sidebar/persona-sidebar.tsx with a rewrite toward voice-agent flows; likely to conflict with motion and layout changes in this PR.

Poem

🐰 I hopped in to tweak each tab and scroll,
Panels slide softly, a tiny fluffy roll.
Agents appear with a hop and a cheer,
Transcripts tumble in, one by one, dear.
A config pruned — a light jump, austere. 🥕✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'Dhan experiments' is vague and does not clearly convey the specific changes made (tab animations, sidebar improvements, and build config updates). Use a more descriptive title that summarizes the main changes, such as 'Add directional tab animations to sidebars' or 'Implement animated transitions for sidebar components'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

Copy link

@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

🤖 Fix all issues with AI agents
In @components/sidebar/persona-sidebar.tsx:
- Around line 467-470: The animation delay currently multiplies the message
index (delay: index * 0.05), causing growing latency for long transcripts;
locate the transition prop where delay is computed (the transition object using
duration, delay, ease) and replace the index-based delay with a fixed small
delay or zero (e.g., delay: 0 or delay: 0.05) so new messages render immediately
regardless of transcript length while keeping duration and ease unchanged.
🧹 Nitpick comments (2)
components/sidebar/intersection-sidebar.tsx (2)

96-151: Consider using TabsContent for accessibility semantics.

The current approach bypasses Radix UI's TabsContent component, which provides important accessibility attributes (role="tabpanel", aria-labelledby). This means screen readers won't properly associate tab panels with their triggers.

You can combine animations with TabsContent using forceMount:

Example approach
<TabsContent value="overview" forceMount asChild>
  <AnimatePresence mode="wait">
    {activeTab === "overview" && (
      <motion.div
        key="overview"
        initial={{ opacity: 0, x: 20 }}
        animate={{ opacity: 1, x: 0 }}
        exit={{ opacity: 0, x: -20 }}
        transition={{ duration: 0.3, ease: "easeInOut" }}
      >
        <OverviewTab hotspot={selectedHotspot} />
      </motion.div>
    )}
  </AnimatePresence>
</TabsContent>

108-108: willChange may be redundant with Framer Motion.

Framer Motion typically promotes animated elements to their own compositor layer automatically. Explicit willChange can be removed unless you've observed specific performance issues.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b533a6 and a99b709.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • components/sidebar/intersection-sidebar.tsx
  • components/sidebar/persona-sidebar.tsx
  • next.config.ts
🔇 Additional comments (7)
next.config.ts (1)

3-3: LGTM!

The empty configuration object is valid for Next.js. Removing the turbopack.root configuration simplifies the setup.

components/sidebar/persona-sidebar.tsx (4)

211-216: LGTM!

Clean dynamic variable extraction from the selected hotspot for conversation context.


275-285: LGTM!

The staggered animation with index-based delays works well for the fixed set of 3 personas, creating a polished sequential reveal effect.


350-357: LGTM!

The motion.button wrapper provides good interactive feedback with hover and tap animations.


250-250: LGTM!

Adding overflow-hidden properly constrains scrolling and ensures animations are clipped within the sidebar boundaries.

components/sidebar/intersection-sidebar.tsx (2)

13-20: LGTM!

Good pattern for resetting the active tab when the hotspot changes. Using selectedHotspot?.id as the dependency ensures the reset only fires on actual hotspot changes, not on every re-render.


75-94: LGTM with minor consideration.

The scale animations on tab triggers provide good interactive feedback. Note that scale transforms in a grid layout may cause subtle visual shifts, but at 5% the effect should be minimal.

Comment on lines +467 to +470
transition={{
duration: 0.3,
delay: index * 0.05,
ease: "easeOut",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Transcript animation delay accumulates unboundedly for long conversations.

The delay: index * 0.05 grows linearly with the transcript length. For a conversation with 50+ messages, new messages will appear with noticeable delays (e.g., 2.5s+ for message 50). Since these are real-time messages, users expect immediate feedback.

Consider removing the index-based delay or using a fixed small delay for all new messages:

Suggested fix
                    transition={{
                      duration: 0.3,
-                     delay: index * 0.05,
+                     delay: 0.05,
                      ease: "easeOut",
                    }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
transition={{
duration: 0.3,
delay: index * 0.05,
ease: "easeOut",
transition={{
duration: 0.3,
delay: 0.05,
ease: "easeOut",
🤖 Prompt for AI Agents
In @components/sidebar/persona-sidebar.tsx around lines 467 - 470, The animation
delay currently multiplies the message index (delay: index * 0.05), causing
growing latency for long transcripts; locate the transition prop where delay is
computed (the transition object using duration, delay, ease) and replace the
index-based delay with a fixed small delay or zero (e.g., delay: 0 or delay:
0.05) so new messages render immediately regardless of transcript length while
keeping duration and ease unchanged.

Copy link

@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: 3

🤖 Fix all issues with AI agents
In @components/sidebar/intersection-sidebar.tsx:
- Around line 93-99: The Close button is icon-only and lacks an accessible
label; update the button that calls selectHotspot(null) (the element rendering
the <X /> icon) to include an accessible name—e.g., add aria-label="Close" or
aria-label="Close hotspot" (or include a visually hidden <span> with readable
text) so screen readers can announce its purpose; keep the onClick and styling
unchanged and ensure the label conveys the action (Close).
- Around line 103-189: The tab panels are rendered directly with motion.div
which bypasses Radix's TabsContent and breaks ARIA/keyboard semantics; wrap each
panel in TabsContent (using the matching value prop: "overview", "audit",
"reimagine") and put your motion.div (and its props: key, custom={direction},
variants, initial, animate, exit, transition, style, className) as the child of
that TabsContent (do not use asChild since Radix v1.1.13 doesn't support it),
keeping OverviewTab inside the "overview" TabsContent and the static <p> nodes
inside the corresponding "audit" and "reimagine" TabsContent so role="tabpanel"
and aria-labelledby/keyboard behavior are restored.

In @components/sidebar/persona-sidebar.tsx:
- Around line 350-357: The Back button is icon-only (motion.button with
onClick={goBack} containing <ArrowLeft />) and needs an accessible label; add an
aria-label (e.g., aria-label="Go back" or "Back") to the motion.button or
include hidden descriptive text for screen readers (visually-hidden span) so
assistive tech can announce the button purpose while keeping the icon-only
visual; ensure the label is concise and matches the button action (goBack).
🧹 Nitpick comments (3)
components/sidebar/intersection-sidebar.tsx (1)

16-34: Guard against unknown tab values (indexOf = -1) and no-op tab changes.
Right now tabOrder.indexOf(newTab) can be -1, producing misleading direction and potentially setting activeTab to an unsupported value.

Proposed fix
-  const handleTabChange = (newTab: string) => {
-    const currentIndex = tabOrder.indexOf(activeTab);
-    const newIndex = tabOrder.indexOf(newTab);
-    const newDirection = newIndex > currentIndex ? 1 : -1;
-    setDirection(newDirection);
-    setActiveTab(newTab);
-  };
+  const handleTabChange = (newTab: string) => {
+    if (newTab === activeTab) return;
+    const currentIndex = tabOrder.indexOf(activeTab);
+    const newIndex = tabOrder.indexOf(newTab);
+    if (newIndex === -1 || currentIndex === -1) return; // defensive: ignore unsupported values
+    setDirection(newIndex > currentIndex ? 1 : -1);
+    setActiveTab(newTab);
+  };
components/sidebar/persona-sidebar.tsx (2)

461-471: Cap transcript animation delay (current delay grows with total message count).
delay: index * 0.05 means message #60 waits ~3s before animating, which will feel broken in longer calls.

Proposed fix
                     transition={{
                       duration: 0.3,
-                      delay: index * 0.05,
+                      delay: Math.min(index * 0.05, 0.3),
                       ease: "easeOut",
                     }}

275-286: Optional: consider parent-level stagger instead of per-item delay math.
Not a blocker—just easier to tune/maintain as the list grows.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a99b709 and e660823.

📒 Files selected for processing (2)
  • components/sidebar/intersection-sidebar.tsx
  • components/sidebar/persona-sidebar.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
components/sidebar/intersection-sidebar.tsx (2)
components/ui/tabs.tsx (3)
  • Tabs (52-52)
  • TabsList (52-52)
  • TabsTrigger (52-52)
components/sidebar/overview-tab.tsx (1)
  • OverviewTab (13-41)

Comment on lines +93 to +99
<button
onClick={() => selectHotspot(null)}
className="rounded-lg p-2 hover:bg-zinc-800"
>
<X className="h-5 w-5 text-zinc-400" />
</button>
</div>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add an accessible label to the icon-only Close button.

Proposed fix
               <button
+                aria-label="Close sidebar"
                 onClick={() => selectHotspot(null)}
                 className="rounded-lg p-2 hover:bg-zinc-800"
               >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
onClick={() => selectHotspot(null)}
className="rounded-lg p-2 hover:bg-zinc-800"
>
<X className="h-5 w-5 text-zinc-400" />
</button>
</div>
<button
aria-label="Close sidebar"
onClick={() => selectHotspot(null)}
className="rounded-lg p-2 hover:bg-zinc-800"
>
<X className="h-5 w-5 text-zinc-400" />
</button>
</div>
🤖 Prompt for AI Agents
In @components/sidebar/intersection-sidebar.tsx around lines 93 - 99, The Close
button is icon-only and lacks an accessible label; update the button that calls
selectHotspot(null) (the element rendering the <X /> icon) to include an
accessible name—e.g., add aria-label="Close" or aria-label="Close hotspot" (or
include a visually hidden <span> with readable text) so screen readers can
announce its purpose; keep the onClick and styling unchanged and ensure the
label conveys the action (Close).

Comment on lines +103 to +189
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">
<TabsList className="grid w-full grid-cols-3 bg-zinc-900">
<TabsTrigger
value="overview"
className="transition-all hover:scale-105 active:scale-95"
>
Overview
</TabsTrigger>
<TabsTrigger
value="audit"
className="transition-all hover:scale-105 active:scale-95"
>
Safety Audit
</TabsTrigger>
<TabsTrigger
value="reimagine"
className="transition-all hover:scale-105 active:scale-95"
>
Re-imagine
</TabsTrigger>
</TabsList>

<TabsContent value="overview" className="mt-4">
<OverviewTab hotspot={selectedHotspot} />
</TabsContent>
<div className="relative mt-4 overflow-hidden">
<AnimatePresence mode="wait" custom={direction}>
{activeTab === "overview" && (
<motion.div
key="overview"
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
duration: 0.25,
ease: [0.25, 0.1, 0.25, 1],
}}
style={{ willChange: "transform, opacity" }}
>
<OverviewTab hotspot={selectedHotspot} />
</motion.div>
)}

<TabsContent value="audit" className="mt-4">
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4">
{activeTab === "audit" && (
<motion.div
key="audit"
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
duration: 0.25,
ease: [0.25, 0.1, 0.25, 1],
}}
className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4"
style={{ willChange: "transform, opacity" }}
>
<p className="text-sm text-zinc-400">
Generate a safety audit to see AI analysis
</p>
</div>
</TabsContent>
</motion.div>
)}

<TabsContent value="reimagine" className="mt-4">
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4">
{activeTab === "reimagine" && (
<motion.div
key="reimagine"
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
duration: 0.25,
ease: [0.25, 0.1, 0.25, 1],
}}
className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4"
style={{ willChange: "transform, opacity" }}
>
<p className="text-sm text-zinc-400">
Re-imagine this intersection with AI
</p>
</div>
</TabsContent>
</Tabs>
</PhotoProvider>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
</Tabs>
</PhotoProvider>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "tabs.tsx" | grep -E "components/ui"

Repository: Lemirq/cision

Length of output: 81


🏁 Script executed:

cat -n ./components/ui/tabs.tsx

Repository: Lemirq/cision

Length of output: 2279


🏁 Script executed:

cat ./package.json | grep -A 2 "@radix-ui/react-tabs"

Repository: Lemirq/cision

Length of output: 171


🏁 Script executed:

head -50 ./components/sidebar/intersection-sidebar.tsx

Repository: Lemirq/cision

Length of output: 1503


🏁 Script executed:

sed -n '103,189p' ./components/sidebar/intersection-sidebar.tsx

Repository: Lemirq/cision

Length of output: 3830


Skip TabsContent breaks ARIA/keyboard semantics; use it to wrap each tab panel instead.

Not using TabsContent removes the role="tabpanel", aria-labelledby, and automatic keyboard navigation that Radix UI provides. The suggested asChild workaround won't work—Radix UI v1.1.13's Content component doesn't support that prop.

Instead, wrap each tab's content directly with TabsContent:

Correct approach
-                  <div className="relative mt-4 overflow-hidden">
-                    <AnimatePresence mode="wait" custom={direction}>
-                      {activeTab === "overview" && (
-                        <motion.div key="overview" ...>
-                          <OverviewTab hotspot={selectedHotspot} />
-                        </motion.div>
-                      )}
-                      ...
-                    </AnimatePresence>
-                  </div>
+                  <AnimatePresence mode="wait" custom={direction}>
+                    <TabsContent value="overview" forceMount className="relative mt-4 overflow-hidden">
+                      {activeTab === "overview" && (
+                        <motion.div key="overview" ...>
+                          <OverviewTab hotspot={selectedHotspot} />
+                        </motion.div>
+                      )}
+                    </TabsContent>
+                    <TabsContent value="audit" forceMount className="relative mt-4 overflow-hidden">
+                      {activeTab === "audit" && (
+                        <motion.div key="audit" ...>
+                          ...
+                        </motion.div>
+                      )}
+                    </TabsContent>
+                    <TabsContent value="reimagine" forceMount className="relative mt-4 overflow-hidden">
+                      {activeTab === "reimagine" && (
+                        <motion.div key="reimagine" ...>
+                          ...
+                        </motion.div>
+                      )}
+                    </TabsContent>
+                  </AnimatePresence>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">
<TabsList className="grid w-full grid-cols-3 bg-zinc-900">
<TabsTrigger
value="overview"
className="transition-all hover:scale-105 active:scale-95"
>
Overview
</TabsTrigger>
<TabsTrigger
value="audit"
className="transition-all hover:scale-105 active:scale-95"
>
Safety Audit
</TabsTrigger>
<TabsTrigger
value="reimagine"
className="transition-all hover:scale-105 active:scale-95"
>
Re-imagine
</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="mt-4">
<OverviewTab hotspot={selectedHotspot} />
</TabsContent>
<div className="relative mt-4 overflow-hidden">
<AnimatePresence mode="wait" custom={direction}>
{activeTab === "overview" && (
<motion.div
key="overview"
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
duration: 0.25,
ease: [0.25, 0.1, 0.25, 1],
}}
style={{ willChange: "transform, opacity" }}
>
<OverviewTab hotspot={selectedHotspot} />
</motion.div>
)}
<TabsContent value="audit" className="mt-4">
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4">
{activeTab === "audit" && (
<motion.div
key="audit"
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
duration: 0.25,
ease: [0.25, 0.1, 0.25, 1],
}}
className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4"
style={{ willChange: "transform, opacity" }}
>
<p className="text-sm text-zinc-400">
Generate a safety audit to see AI analysis
</p>
</div>
</TabsContent>
</motion.div>
)}
<TabsContent value="reimagine" className="mt-4">
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4">
{activeTab === "reimagine" && (
<motion.div
key="reimagine"
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
duration: 0.25,
ease: [0.25, 0.1, 0.25, 1],
}}
className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4"
style={{ willChange: "transform, opacity" }}
>
<p className="text-sm text-zinc-400">
Re-imagine this intersection with AI
</p>
</div>
</TabsContent>
</Tabs>
</PhotoProvider>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
</Tabs>
</PhotoProvider>
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">
<TabsList className="grid w-full grid-cols-3 bg-zinc-900">
<TabsTrigger
value="overview"
className="transition-all hover:scale-105 active:scale-95"
>
Overview
</TabsTrigger>
<TabsTrigger
value="audit"
className="transition-all hover:scale-105 active:scale-95"
>
Safety Audit
</TabsTrigger>
<TabsTrigger
value="reimagine"
className="transition-all hover:scale-105 active:scale-95"
>
Re-imagine
</TabsTrigger>
</TabsList>
<AnimatePresence mode="wait" custom={direction}>
<TabsContent value="overview" forceMount className="relative mt-4 overflow-hidden">
{activeTab === "overview" && (
<motion.div
key="overview"
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
duration: 0.25,
ease: [0.25, 0.1, 0.25, 1],
}}
style={{ willChange: "transform, opacity" }}
>
<OverviewTab hotspot={selectedHotspot} />
</motion.div>
)}
</TabsContent>
<TabsContent value="audit" forceMount className="relative mt-4 overflow-hidden">
{activeTab === "audit" && (
<motion.div
key="audit"
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
duration: 0.25,
ease: [0.25, 0.1, 0.25, 1],
}}
style={{ willChange: "transform, opacity" }}
>
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4">
<p className="text-sm text-zinc-400">
Generate a safety audit to see AI analysis
</p>
</div>
</motion.div>
)}
</TabsContent>
<TabsContent value="reimagine" forceMount className="relative mt-4 overflow-hidden">
{activeTab === "reimagine" && (
<motion.div
key="reimagine"
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
duration: 0.25,
ease: [0.25, 0.1, 0.25, 1],
}}
style={{ willChange: "transform, opacity" }}
>
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4">
<p className="text-sm text-zinc-400">
Re-imagine this intersection with AI
</p>
</div>
</motion.div>
)}
</TabsContent>
</AnimatePresence>
</Tabs>
</PhotoProvider>
🤖 Prompt for AI Agents
In @components/sidebar/intersection-sidebar.tsx around lines 103 - 189, The tab
panels are rendered directly with motion.div which bypasses Radix's TabsContent
and breaks ARIA/keyboard semantics; wrap each panel in TabsContent (using the
matching value prop: "overview", "audit", "reimagine") and put your motion.div
(and its props: key, custom={direction}, variants, initial, animate, exit,
transition, style, className) as the child of that TabsContent (do not use
asChild since Radix v1.1.13 doesn't support it), keeping OverviewTab inside the
"overview" TabsContent and the static <p> nodes inside the corresponding "audit"
and "reimagine" TabsContent so role="tabpanel" and aria-labelledby/keyboard
behavior are restored.

Comment on lines +350 to +357
<motion.button
onClick={goBack}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="p-2 -ml-2 hover:bg-zinc-800 rounded-lg transition-colors"
>
<ArrowLeft className="h-5 w-5 text-zinc-400" />
</button>
</motion.button>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add an accessible label to the icon-only Back button.

Proposed fix
                 <motion.button
                   onClick={goBack}
+                  aria-label="Back"
                   whileHover={{ scale: 1.1 }}
                   whileTap={{ scale: 0.9 }}
                   className="p-2 -ml-2 hover:bg-zinc-800 rounded-lg transition-colors"
                 >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<motion.button
onClick={goBack}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="p-2 -ml-2 hover:bg-zinc-800 rounded-lg transition-colors"
>
<ArrowLeft className="h-5 w-5 text-zinc-400" />
</button>
</motion.button>
<motion.button
onClick={goBack}
aria-label="Back"
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="p-2 -ml-2 hover:bg-zinc-800 rounded-lg transition-colors"
>
<ArrowLeft className="h-5 w-5 text-zinc-400" />
</motion.button>
🤖 Prompt for AI Agents
In @components/sidebar/persona-sidebar.tsx around lines 350 - 357, The Back
button is icon-only (motion.button with onClick={goBack} containing <ArrowLeft
/>) and needs an accessible label; add an aria-label (e.g., aria-label="Go back"
or "Back") to the motion.button or include hidden descriptive text for screen
readers (visually-hidden span) so assistive tech can announce the button purpose
while keeping the icon-only visual; ensure the label is concise and matches the
button action (goBack).

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