Skip to content

feat: add missing top and left chevron icons#40

Closed
krockxz wants to merge 5 commits intoitshover:masterfrom
krockxz:feat/missing-chevron-icons
Closed

feat: add missing top and left chevron icons#40
krockxz wants to merge 5 commits intoitshover:masterfrom
krockxz:feat/missing-chevron-icons

Conversation

@krockxz
Copy link
Copy Markdown
Contributor

@krockxz krockxz commented Jan 5, 2026

Added TopCheveron and LeftCheveron to complete the set. Verified consistency with DownCheveron.

Summary by CodeRabbit

  • New Features
    • Added 11 new animated icons: left-chevron, top-chevron, calendar, cloud, download-cloud, map-pin, menu, react, svelte, tailwind, vue.
    • Icons support hover-triggered animations and can be started/stopped for richer interaction.
    • Icons are customizable (size, color, stroke width) and searchable/available in the registry.

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

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Jan 5, 2026

@krockxz is attempting to deploy a commit to the itshover's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 5, 2026

📝 Walkthrough

Walkthrough

Adds 11 new forwardRef animated icon components (each exposing start/stop imperative methods), updates icons/index.ts to include new icons, and registers two chevron icons in root and public registries; plus a minor formatting change in one public manifest.

Changes

Cohort / File(s) Summary
Icon index
icons/index.ts
Imported LeftChevron and TopChevron; appended multiple new icon entries (e.g., left-chevron, top-chevron, react-icon, vue-icon, tailwind-icon, etc.) to ICON_LIST and kept list sorted.
New animated icon components
icons/calendar-icon.tsx, icons/cloud-icon.tsx, icons/download-cloud-icon.tsx, icons/left-chevron.tsx, icons/map-pin-icon.tsx, icons/menu-icon.tsx, icons/react-icon.tsx, icons/svelte-icon.tsx, icons/tailwind-icon.tsx, icons/top-chevron.tsx, icons/vue-icon.tsx
Added 11 forwardRef React TSX components using motion/useAnimate; each accepts AnimatedIconProps, exposes AnimatedIconHandle (startAnimation/stopAnimation) via useImperativeHandle, and wires hover handlers to animations.
Shared types for icons
icons/types.ts
Exports AnimatedIconProps and AnimatedIconHandle referenced by new components and registry entries.
Registry (root) files
registry.json, public/r/registry.json
Added left-chevron and top-chevron registry:ui entries (dependencies include motion) pointing to icons/left-chevron.tsx / icons/top-chevron.tsx and icons/types.ts.
Public registry items
public/r/left-chevron.json, public/r/top-chevron.json
New public UI registry manifests for the two chevrons mapping to their TSX components and shared types.
Minor formatting
public/r/layout-sidebar-right-collapse-icon.json
Trivial formatting change: collapsed an animate call into a single line; no behavioral change.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped in with tiny sparks and cheer,

Chevrons nudged left, and tops popped up near.
Clouds bobbed, calendars flipped with a spin,
Frameworks twirled, menus danced—what a din!
Icons now sparkle; I nibble a carrot to celebrate. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding the missing top and left chevron icons to complete the set, which is the core objective of this PR.
✨ Finishing touches
  • 📝 Generate docstrings

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

@krockxz krockxz force-pushed the feat/missing-chevron-icons branch from 30b7438 to b34814d Compare January 5, 2026 05:58
Copy link
Copy Markdown
Contributor

@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 @public/r/left-cheveron.json:
- Around line 9-12: The start function in LeftCheveron is async (function name:
start) so startAnimation actually returns Promise<void> while the
AnimatedIconHandle interface declares startAnimation: () => void; update the
interface in icons/types.ts to declare startAnimation: () => Promise<void> (and
keep stopAnimation: () => void) so the types match, or alternatively remove
async/await from the start implementation in LeftCheveron to make it return
void—pick one approach and update the AnimatedIconHandle or the start function
accordingly.
- Line 3: The JSON "name" value "left-cheveron" is misspelled and must be
changed to "left-chevron"; update the "name" field in this file and rename the
file itself from left-cheveron.json to left-chevron.json, then search the repo
for the identifier "left-cheveron" (including usages in registry lookups, import
statements, export/component names, routes, and any references to top-chevron)
and replace them with "left-chevron" (and ensure any related component/class
names follow the same corrected spelling, e.g., TopChevron, LeftChevron), update
any path strings and public API entries to match the new filename and symbol,
and run tests/build to verify no broken imports remain.

In @registry.json:
- Around line 1843-1859: The registry.json file was manually edited to add icon
entries like "left-cheveron" (and "top-cheveron"); because registry.json is
auto-generated you should not edit it directly—run the generator instead: revert
manual changes to registry.json and run the build script `npm run
registry:build` (or the project’s registry build command) to regenerate the file
so entries for "left-cheveron" and other icons are produced from their source
files (e.g., icons/left-cheveron.tsx and icons/types.ts) and remain in sync.
🧹 Nitpick comments (3)
icons/index.ts (1)

164-164: Spelling inconsistency: "Cheveron" vs "Chevron"

The correct spelling is "chevron," but "cheveron" is used here and throughout the codebase (DownCheveron, RightCheveron). While this maintains consistency with existing code, consider creating a follow-up issue to standardize the spelling across the codebase to "chevron."

Also applies to: 179-179

icons/left-cheveron.tsx (1)

1-60: Consider extracting shared chevron logic (optional).

Both LeftCheveron and TopCheveron share nearly identical structure, differing only in animation axis, class names, and SVG paths. While the current approach is standard for icon libraries (favoring clarity and component independence), you could consider extracting shared logic into a base component or factory function to reduce duplication.

This is purely optional and can be deferred to a future refactor.

public/r/top-cheveron.json (1)

14-16: Potential type mismatch in AnimatedIconHandle interface.

The AnimatedIconHandle interface defines startAnimation: () => void, but the actual implementation in both components is async (lines 12-23 in the component files), which returns Promise<void>.

Consider updating the interface to:

export interface AnimatedIconHandle {
-  startAnimation: () => void;
+  startAnimation: () => Promise<void>;
   stopAnimation: () => void;
}

This would provide more accurate typing for consumers who want to await the animation completion.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e21481 and 30b7438.

📒 Files selected for processing (11)
  • app/request/page.tsx
  • components/container.tsx
  • components/navbar.tsx
  • icons/index.ts
  • icons/left-cheveron.tsx
  • icons/top-cheveron.tsx
  • public/r/layout-sidebar-right-collapse-icon.json
  • public/r/left-cheveron.json
  • public/r/registry.json
  • public/r/top-cheveron.json
  • registry.json
🧰 Additional context used
🧬 Code graph analysis (2)
app/request/page.tsx (1)
constants.ts (1)
  • LINKS (2-8)
components/container.tsx (1)
lib/utils.ts (1)
  • cn (4-6)
🔇 Additional comments (15)
public/r/layout-sidebar-right-collapse-icon.json (1)

10-10: LGTM - Registry content update

The component content string has been updated with formatting changes to the animation call. The functional behavior remains unchanged.

components/navbar.tsx (2)

62-67: LGTM - Desktop navigation link added

The new "/request" navigation link is properly implemented and consistent with existing desktop navigation items.


149-155: LGTM - Mobile navigation link added

The mobile menu entry for "/request" is properly implemented with the onClick handler to close the menu, consistent with other mobile navigation items.

app/request/page.tsx (1)

1-42: LGTM - Well-implemented request page

The RequestPage component is cleanly implemented with proper patterns:

  • Correct use of "use client" directive for interactivity
  • Appropriate ref typing with AnimatedIconHandle
  • Safe optional chaining for animation control
  • Clean hover interaction handlers
components/container.tsx (1)

2-11: LGTM - Clean className support implementation

The Container component now accepts an optional className prop and properly merges it with base classes using the cn utility. This enhancement maintains backward compatibility while enabling flexible styling.

icons/index.ts (1)

1357-1366: LGTM! Icon entries are well-structured.

The icon entries follow the established pattern with appropriate keywords for discoverability. Keywords like "arrow", "back", "previous" for left-cheveron and "arrow", "top", "up", "collapse" for top-cheveron align well with common use cases.

icons/top-cheveron.tsx (3)

1-9: LGTM! Component structure follows best practices.

The component correctly uses forwardRef with proper type annotations, and default props are sensible. The imports are clean and necessary.


29-32: LGTM! Imperative handle correctly exposes animation controls.

The useImperativeHandle properly exposes startAnimation and stopAnimation methods, allowing parent components to programmatically control the animation.


34-57: LGTM! JSX structure is well-implemented.

The component correctly:

  • Uses motion.div for hover-triggered animations
  • Sets appropriate cursor styling for interactive elements
  • Applies the chevron-top class matching the animation selector
  • Renders an SVG with the correct path for an upward-pointing chevron
icons/left-cheveron.tsx (2)

12-25: Animation implementation mirrors TopCheveron correctly.

The animation moves the icon on the x-axis with [0, -6, 0], creating a left-direction animation. This mirrors TopCheveron's y-axis animation, maintaining consistency.

Please verify these animation values also match the existing chevron icons' pattern, as checked for TopCheveron.


39-54: LGTM! SVG path correctly renders a left-pointing chevron.

The path M15 6l-6 6l6 6 correctly draws a left-pointing chevron shape, appropriate for "back" or "previous" navigation.

public/r/registry.json (1)

1843-1859: LGTM! Registry entries are properly structured.

Both left-cheveron and top-cheveron registry entries correctly:

  • Follow the established schema
  • Declare the motion dependency
  • Include both the component file and shared types file
  • Maintain alphabetical order

Also applies to: 2778-2794

public/r/top-cheveron.json (1)

1-20: LGTM! Registry metadata is well-structured.

The registry metadata file correctly defines the TopCheveron component with all necessary dependencies and file contents for distribution.

public/r/left-cheveron.json (2)

13-17: AnimatedIconProps interface looks good.

The AnimatedIconProps interface is well-structured with:

  • Clear JSDoc documentation
  • Flexible size type (number | string)
  • Sensible optional properties with defaults
  • Proper typing for SVG attributes

The AnimatedIconHandle interface issue (async return type) was already flagged in the previous comment.


4-4: The motion package is correctly installed and properly imported. The component at icons/left-cheveron.tsx correctly imports motion and useAnimate from motion/react and uses them appropriately with motion.div and the useAnimate() hook. The dependency declaration in the registry file is accurate.

Comment thread public/r/left-cheveron.json Outdated
Comment thread public/r/left-cheveron.json Outdated
Comment thread registry.json
Copy link
Copy Markdown
Contributor

@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 @icons/left-cheveron.tsx:
- Around line 1-60: Rename the misspelled component LeftCheveron to LeftChevron:
update the component identifier, the default export, and
LeftCheveron.displayName to LeftChevron; rename the file accordingly
(LeftCheveron -> LeftChevron); search and update all imports/usages/registry
entries/tests/stories that reference LeftCheveron to the new LeftChevron name so
nothing breaks; ensure exported type generics and forwarded ref usage remain
unchanged and run type checks to catch any remaining references.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30b7438 and b34814d.

📒 Files selected for processing (8)
  • icons/index.ts
  • icons/left-cheveron.tsx
  • icons/top-cheveron.tsx
  • public/r/layout-sidebar-right-collapse-icon.json
  • public/r/left-cheveron.json
  • public/r/registry.json
  • public/r/top-cheveron.json
  • registry.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • icons/top-cheveron.tsx
  • icons/index.ts
  • public/r/left-cheveron.json
  • public/r/top-cheveron.json
  • registry.json
🔇 Additional comments (1)
public/r/registry.json (1)

1843-1859: LGTM! Registry entries are correctly structured.

Both new registry entries for left-cheveron and top-cheveron follow the established pattern and are properly configured:

  • Correct type (registry:ui)
  • Appropriate dependency on motion
  • Proper file path references
  • Consistent structure with existing icon entries

Note: This file is auto-generated (per line 2), so the inherited naming issue from the source files will be present here as well.

Also applies to: 2778-2794

Comment thread icons/left-cheveron.tsx Outdated
@Abhijit-Jha
Copy link
Copy Markdown
Member

Hey @krockxz , can you please share a recording of the animations you’ve created?
Also, I think adding only 3 icons in a PR isn’t very effective. It would be great if you could include more related icons together.

You can refer to Lucide, Tabler Icons, or other icon libraries to find similar icons and animate them.

Thank you.

@krockxz
Copy link
Copy Markdown
Contributor Author

krockxz commented Jan 5, 2026

@Abhijit-Jha To make this PR more substantial, I propose adding the following missing icons:

1. Tech Stack

  • react-icon: Atom symbol (Spinning orbits or pulsing core)
  • vue-icon: V logo (Sliding in or opacity pulse)
  • tailwind-icon: Waves (Flowing horizontally)
  • svelte-icon: S logo (Path drawing)

2. UI Components

  • calendar-icon: Standard sheet (Flipping page or shaking)
  • cloud-icon: Simple cloud (Floating or raining)
  • menu-icon: Hamburger (Transforming to X)
  • map-pin-icon: Location marker (Bouncing)

3. Utility

  • download-cloud-icon: Cloud with arrow

Let me know if you'd like me to proceed with these updates

@Abhijit-Jha
Copy link
Copy Markdown
Member

Go ahead @krockxz

Copy link
Copy Markdown

@tanmayagrwl tanmayagrwl left a comment

Choose a reason for hiding this comment

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

Can you address this comment?

Comment thread icons/types.ts Outdated
@krockxz
Copy link
Copy Markdown
Contributor Author

krockxz commented Jan 5, 2026

@Abhijit-Jha updated the PR with the icons
do the registry files need to be committed or are they part of the deployment commands?

Copy link
Copy Markdown
Contributor

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

Fix all issues with AI Agents 🤖
In @icons/cloud-icon.tsx:
- Around line 12-18: The start callback is declared async but the
AnimatedIconHandle.startAnimation interface expects a () => void; remove the
async keyword from start in cloud-icon.tsx (and similarly across other icon
components) so it returns void, and keep the animate(...) call un-awaited
(fire-and-forget) inside start; ensure the function signature and any exported
handle types match AnimatedIconHandle.startAnimation to avoid the Promise<void>
vs void mismatch.

In @icons/left-chevron.tsx:
- Around line 12-21: The async animation in the start function can reject and
cause an unhandled promise; wrap the await animate(...) call inside a try/catch
in the start function to catch and log or handle errors (use
processLogger/console.error or a passed logger) and optionally bail or noop on
failure; target symbols: start function, the animate(...) invocation, and the
".chevron-left" selector.
- Around line 12-21: The start callback is async (returns Promise<void>) but the
interface AnimatedIconHandle declares startAnimation: () => void; update the
interface in icons/types.ts to declare startAnimation: () => Promise<void> (and
any other related methods that should be async), update implementations like
start in left-chevron.tsx and other animated icon files (e.g., top-chevron.tsx)
to match that signature, and wrap the await animate(...) call inside start with
a try/catch that logs or surfaces animation errors (use the existing animate and
process/logger utilities) so failures don’t silently reject.

In @icons/vue-icon.tsx:
- Around line 12-18: The start callback currently awaits animate(...) with
repeat: Infinity which never resolves; change start to trigger the animation
without awaiting so callers won’t hang: remove the async/await (or keep async
but drop the await) in the start useCallback and simply call animate(".inner-v",
{ opacity: [...], scale: [...] }, { duration: 0.8, ease: "easeInOut", repeat:
Infinity }); ensuring the useCallback name start and the selector ".inner-v"
remain unchanged.

In @public/r/top-chevron.json:
- Around line 13-18: The source type for AnimatedIconHandle is inconsistent with
implementations: update the signature of startAnimation in AnimatedIconHandle to
return Promise<void> (matching async implementations like LeftChevron and
TopChevron); locate the AnimatedIconHandle interface (symbols:
AnimatedIconHandle, startAnimation, stopAnimation) and change startAnimation: ()
=> void to startAnimation: () => Promise<void>, leaving stopAnimation as-is so
the registry and source types are consistent.
- Around line 7-12: TopChevron's start/stop animation lacks error handling, uses
a brittle ".chevron-top" selector, and doesn't cancel in-flight animations;
update the component to (1) use a ref-based scoped target from useAnimate (the
existing scope variable) instead of the string selector so animations target the
svg element reliably, (2) wrap the async start callback body in try/catch and
log or swallow errors appropriately, and (3) track the returned animation
controls or a cancellation token from animate so stop can cancel or seek to end
(use the animate return value inside start and call its cancel/stop in stop);
modify the start and stop functions and the useImperativeHandle export
accordingly (functions: start, stop; component: TopChevron; hook:
useAnimate/scope).
♻️ Duplicate comments (2)
public/r/left-chevron.json (2)

7-12: Inline component matches source but inherits its issues.

The inline LeftChevron component content matches the source file icons/left-chevron.tsx reviewed earlier. It inherits the same issues already flagged:

  • Missing error handling in async start callback
  • CSS selector brittleness
  • No animation cancellation mechanism

Refer to the detailed comments on icons/left-chevron.tsx for recommended fixes.


13-18: Type definition mismatch between registry and source types.ts.

The inline types.ts content declares startAnimation: () => Promise<void>, but the relevant_code_snippets for the source icons/types.ts shows () => void.

This is the same critical type inconsistency flagged in public/r/top-chevron.json. Both registry files are consistent with the actual async implementations, but the source icons/types.ts needs to be updated to Promise<void>.

🧹 Nitpick comments (7)
icons/map-pin-icon.tsx (1)

12-18: Remove unnecessary async keyword.

The start callback is marked as async but doesn't use await. This is unnecessary and could be misleading.

🔎 Proposed fix
-  const start = useCallback(async () => {
+  const start = useCallback(() => {
     animate(
       "svg",
       { y: [0, -6, 0] },
       { duration: 0.6, ease: "easeInOut", repeat: Infinity },
     );
   }, [animate]);
icons/cloud-icon.tsx (1)

1-3: Consider React 19's ref-as-prop pattern.

While forwardRef still works in React 19, ref is now a regular prop. For modern React 19 code, you can simplify by accepting ref directly in props and removing forwardRef.

🔎 Example refactor using ref as prop
-import { forwardRef, useImperativeHandle, useCallback } from "react";
+import { useImperativeHandle, useCallback } from "react";
 import { AnimatedIconHandle, AnimatedIconProps } from "./types";
 import { motion, useAnimate } from "motion/react";

-const CloudIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
-  (
-    { size = 24, color = "currentColor", strokeWidth = 2, className = "" },
-    ref,
-  ) => {
+const CloudIcon = (
+  { size = 24, color = "currentColor", strokeWidth = 2, className = "", ref }: AnimatedIconProps & { ref?: React.Ref<AnimatedIconHandle> }
+) => {
     const [scope, animate] = useAnimate();
     // ... rest of implementation
-  },
-);
+};

Note: Verify this pattern is consistent across all icon components in the repository before applying.

icons/download-cloud-icon.tsx (1)

12-18: Remove unnecessary async keyword.

The start callback is declared as async but doesn't use await or return the promise from animate(). Since the animation repeats infinitely and you don't need to wait for it to complete, the async keyword should be removed for clarity.

🔎 Proposed fix
-    const start = useCallback(async () => {
+    const start = useCallback(() => {
       animate(
         ".arrow",
         { y: [0, 4, 0] },
         { duration: 0.8, ease: "easeInOut", repeat: Infinity },
       );
     }, [animate]);
icons/react-icon.tsx (1)

25-28: Consider the rotation reset behavior.

When stopAnimation is called mid-rotation, animating from the current rotation back to 0 may cause a jarring backward spin. For smoother UX, consider stopping at the current position or snapping to the nearest multiple of 360°.

Alternative: Stop without resetting rotation
     const stop = useCallback(() => {
-      animate(".orbit", { rotate: 0 }, { duration: 0.5 });
+      // Stop animation at current position by not animating rotate
       animate(".core", { scale: 1 }, { duration: 0.5 });
     }, [animate]);

Alternatively, you could track the animation controls and call .stop() on them instead of animating to a fixed value.

icons/left-chevron.tsx (3)

13-15: Consider ref-based targeting instead of CSS class selector.

Using a string selector ".chevron-left" is fragile because:

  • It relies on the className remaining unchanged
  • Multiple instances on the same page could interfere with each other (scope mitigates this, but ref is cleaner)
  • Typos in the class name won't be caught by TypeScript
🔎 Alternative approach using ref
 const LeftChevron = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
   (
     { size = 24, color = "currentColor", strokeWidth = 2, className = "" },
     ref,
   ) => {
     const [scope, animate] = useAnimate();
+    const svgRef = useRef<SVGSVGElement>(null);
 
     const start = useCallback(async () => {
+      if (!svgRef.current) return;
       await animate(
-        ".chevron-left",
+        svgRef.current,
         { x: [0, -6, 0] },
         {
           duration: 0.8,
           ease: "easeInOut",
         },
       );
     }, [animate]);
 
     const stop = useCallback(() => {
+      if (!svgRef.current) return;
-      animate(".chevron-left", { x: 0 }, { duration: 0.2, ease: "easeInOut" });
+      animate(svgRef.current, { x: 0 }, { duration: 0.2, ease: "easeInOut" });
     }, [animate]);

     // ... rest of code

     return (
       <motion.div
         ref={scope}
         className={`inline-flex cursor-pointer items-center justify-center ${className}`}
         onHoverStart={start}
         onHoverEnd={stop}
       >
         <svg
+          ref={svgRef}
           xmlns="http://www.w3.org/2000/svg"
           width={size}
           height={size}
           viewBox="0 0 24 24"
           fill="none"
           stroke={color}
           strokeWidth={strokeWidth}
           strokeLinecap="round"
           strokeLinejoin="round"
-          className="chevron-left"
         >

32-55: Render logic looks good with minor style suggestion.

The component correctly:

  • Wires hover handlers to start/stop animations
  • Renders a properly configured SVG with responsive size/color/strokeWidth
  • Supports custom className extension

Optional: The className concatenation could use a template literal for consistency with modern practices, though the current approach works fine:

-        className={`inline-flex cursor-pointer items-center justify-center ${className}`}
+        className={`inline-flex cursor-pointer items-center justify-center ${className}`.trim()}

or use a library like clsx if already available in the project.


23-25: Consider cancelling ongoing animations when stop is called.

If a user hovers out (triggering stop) while the start animation is still running, both animations will execute simultaneously on the same element, potentially causing visual glitches. The motion library supports both cancel() (resets animation effect) and stop() (halts but keeps current visual state) for handling this.

Implement animation control to prevent conflicts:

🔎 Example using animation ref tracking
 const LeftChevron = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
   (
     { size = 24, color = "currentColor", strokeWidth = 2, className = "" },
     ref,
   ) => {
     const [scope, animate] = useAnimate();
+    const animationRef = useRef<ReturnType<typeof animate> | null>(null);
 
     const start = useCallback(async () => {
+      animationRef.current?.cancel?.();
-      await animate(
+      animationRef.current = animate(
         ".chevron-left",
         { x: [0, -6, 0] },
         {
           duration: 0.8,
           ease: "easeInOut",
         },
       );
+      await animationRef.current;
     }, [animate]);
 
     const stop = useCallback(() => {
+      animationRef.current?.cancel?.();
-      animate(".chevron-left", { x: 0 }, { duration: 0.2, ease: "easeInOut" });
+      animationRef.current = animate(".chevron-left", { x: 0 }, { duration: 0.2, ease: "easeInOut" });
     }, [animate]);
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b34814d and 8219f82.

📒 Files selected for processing (15)
  • icons/calendar-icon.tsx
  • icons/cloud-icon.tsx
  • icons/download-cloud-icon.tsx
  • icons/index.ts
  • icons/left-chevron.tsx
  • icons/map-pin-icon.tsx
  • icons/menu-icon.tsx
  • icons/react-icon.tsx
  • icons/svelte-icon.tsx
  • icons/tailwind-icon.tsx
  • icons/top-chevron.tsx
  • icons/vue-icon.tsx
  • public/r/left-chevron.json
  • public/r/top-chevron.json
  • registry.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • icons/index.ts
  • registry.json
🧰 Additional context used
🧬 Code graph analysis (11)
icons/menu-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/map-pin-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/left-chevron.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/vue-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/react-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/top-chevron.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/download-cloud-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/svelte-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/cloud-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/calendar-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/tailwind-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
🔇 Additional comments (23)
icons/map-pin-icon.tsx (5)

5-10: Component setup is correct.

The component uses forwardRef with proper TypeScript generics, and prop defaults are sensible and consistent with other icons in the repository.


24-27: Imperative API correctly implemented.

The useImperativeHandle correctly exposes the AnimatedIconHandle interface, allowing parent components to control the animation imperatively.


29-51: Rendering implementation is correct.

The JSX structure correctly wires up hover events to trigger animations, and all props are properly bound to the SVG element. The map pin SVG markup follows standard conventions.


55-56: Export setup is correct.

Setting displayName aids debugging in React DevTools, and the default export follows the established pattern.


1-3: Imports are correct and consistent with the established icon pattern.

All three imports are properly used and follow the consistent pattern across the entire codebase. The motion/react import path is valid for the installed Motion library version (^12.23.26).

icons/cloud-icon.tsx (3)

20-22: LGTM!

The stop callback correctly resets the animation with a smooth transition and matches the interface signature.


24-50: LGTM! Clean implementation of the animated cloud icon.

The component correctly:

  • Exposes the imperative API via useImperativeHandle
  • Wires hover interactions to start/stop animation
  • Renders a scalable SVG with proper props forwarding
  • Uses motion.div for animation scope

The hover-driven animation pattern (onHoverStart/onHoverEnd) is intuitive and aligns with the other icons in this PR.


54-55: LGTM!

Setting displayName improves debugging experience in React DevTools, and the default export follows the established pattern for icon components.

icons/download-cloud-icon.tsx (6)

1-3: LGTM!

Imports are clean and all are utilized. The component correctly imports from the established types module and motion library.


5-10: LGTM!

Component definition follows the established pattern with correct types and sensible defaults. The animation setup using useAnimate is properly initialized.


24-27: LGTM!

The imperative handle correctly exposes startAnimation and stopAnimation methods as defined by the AnimatedIconHandle interface.


29-52: LGTM!

The component structure is well-implemented with proper hover handlers and SVG composition. The animation targets both the polyline and line elements with the .arrow class, creating a cohesive download arrow animation that moves together—this is the correct behavior for a download icon.


56-57: LGTM!

The display name and export follow the established conventions. Display name aids debugging in React DevTools.


20-22: No changes needed. The implementation correctly leverages motion/react's built-in behavior: when animate() is called on the same element/property, it automatically cancels any existing animation, including infinite animations. This pattern is properly documented in the motion/react useAnimate API and is consistently used across other animated icons in the codebase.

icons/tailwind-icon.tsx (1)

1-70: LGTM!

The component correctly implements the animated icon pattern with:

  • Proper use of useAnimate for parallel infinite wave animations
  • Correct imperative handle exposure for external control
  • Consistent hover-driven animation triggers
icons/menu-icon.tsx (1)

1-59: LGTM!

The hamburger-to-X animation is well implemented:

  • Promise.all correctly parallelizes the finite-duration transform animations
  • The stop function appropriately fires reverse animations without blocking
  • SVG line elements with proper class targeting for animation
icons/calendar-icon.tsx (1)

1-66: LGTM!

The 3D flip animation is well implemented:

  • Correctly applies perspective on the parent container for the rotateX effect
  • Finite animation appropriately awaited
  • Clean SVG calendar structure with proper animation targeting
icons/svelte-icon.tsx (1)

47-50: Verify pathLength animation setup for the drawing effect.

The pathLength animation typically requires initial SVG setup (e.g., pathLength="1" attribute and strokeDasharray/strokeDashoffset styles) for the "drawing" effect to render correctly. Without this, the animation may not produce the expected visual.

Recommended setup for path drawing animation
           <path
             d="M15 6C15 6 16.0001 5.00003 17.5 5.00003C19.5 5.00003 21 6.50003 21 8.50003C21 10.5 19.5 12 17.5 12H6.5C4.5 12 3 13.5 3 15.5C3 17.5 4.5 19 6.5 19C8 19 9 18 9 18M15 6L9 18M15 6C14 6 13 7 13 8.5C13 10 14 11 15 11H17.5M9 18C10 18 11 17 11 15.5C11 14 10 13 9 13H6.5"
             className="svelte-path"
+            pathLength="1"
+            style={{ strokeDasharray: 1, strokeDashoffset: 1 }}
           />

Then animate strokeDashoffset from 1 to 0 instead of pathLength, or verify motion's pathLength animation works as expected with the current setup.

icons/top-chevron.tsx (1)

1-62: LGTM!

The TopChevron correctly implements the vertical bounce animation:

  • Finite y-axis translation appropriately awaited
  • Consistent with the established icon pattern
  • Clean SVG structure with proper animation targeting
icons/left-chevron.tsx (2)

27-30: LGTM: Imperative handle correctly exposes animation controls.

The useImperativeHandle properly exposes the animation methods via the forwarded ref, allowing parent components to trigger animations programmatically.


59-60: LGTM: Display name and export.

The display name aids in React DevTools debugging, and the default export is appropriate for this icon component.

public/r/top-chevron.json (1)

1-6: LGTM: Registry metadata correctly configured.

The registry item metadata properly declares the motion dependency and follows the expected schema format.

public/r/left-chevron.json (1)

1-6: LGTM: Registry metadata correctly configured.

The registry item metadata properly declares the motion dependency and follows the expected schema format, consistent with the top-chevron registry entry.

Comment thread icons/cloud-icon.tsx Outdated
Comment thread icons/left-chevron.tsx Outdated
Comment on lines +12 to +21
const start = useCallback(async () => {
await animate(
".chevron-left",
{ x: [0, -6, 0] },
{
duration: 0.8,
ease: "easeInOut",
},
);
}, [animate]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing error handling for async animation.

The animate call could potentially reject if the target element isn't found or motion throws an error. Without try-catch, this would result in an unhandled promise rejection.

🔎 Recommended fix to add error handling
 const start = useCallback(async () => {
+  try {
     await animate(
       ".chevron-left",
       { x: [0, -6, 0] },
       {
         duration: 0.8,
         ease: "easeInOut",
       },
     );
+  } catch (error) {
+    console.error("Animation error:", error);
+  }
 }, [animate]);
📝 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
const start = useCallback(async () => {
await animate(
".chevron-left",
{ x: [0, -6, 0] },
{
duration: 0.8,
ease: "easeInOut",
},
);
}, [animate]);
const start = useCallback(async () => {
try {
await animate(
".chevron-left",
{ x: [0, -6, 0] },
{
duration: 0.8,
ease: "easeInOut",
},
);
} catch (error) {
console.error("Animation error:", error);
}
}, [animate]);
🤖 Prompt for AI Agents
In @icons/left-chevron.tsx around lines 12-21, The async animation in the start
function can reject and cause an unhandled promise; wrap the await animate(...)
call inside a try/catch in the start function to catch and log or handle errors
(use processLogger/console.error or a passed logger) and optionally bail or noop
on failure; target symbols: start function, the animate(...) invocation, and the
".chevron-left" selector.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, verify the AnimatedIconHandle interface definition
cat icons/types.ts

Repository: itshover/itshover

Length of output: 437


🏁 Script executed:

# Also check the full left-chevron.tsx file to see all exports
cat icons/left-chevron.tsx

Repository: itshover/itshover

Length of output: 1675


🏁 Script executed:

# Search for registry JSON files mentioned in the comment
fd -t f "registry" -o -name "*registry*" -o -name "*.registry.json"

Repository: itshover/itshover

Length of output: 291


🏁 Script executed:

# Find other icon files to check for same pattern
fd -t f "\.tsx$" icons/

Repository: itshover/itshover

Length of output: 5112


🏁 Script executed:

# Check one other icon file if it exists
fd -t f -name "*chevron*" icons/

Repository: itshover/itshover

Length of output: 291


🏁 Script executed:

# Check another animated chevron icon to see if it has the same pattern
cat icons/top-chevron.tsx

Repository: itshover/itshover

Length of output: 1687


Critical type mismatch: startAnimation returns Promise<void> but interface declares () => void.

The start callback in left-chevron.tsx is an async function that returns Promise<void>, but AnimatedIconHandle in icons/types.ts declares startAnimation: () => void. This violates the type contract—TypeScript will flag this as an assignment error.

This pattern is repeated across multiple animated icon files (e.g., icons/top-chevron.tsx). The mismatch means consumers have no type signal to await the animation completion, risking race conditions where the next operation runs before animation finishes.

Additionally, the await animate() call lacks error handling for animation failures.

🤖 Prompt for AI Agents
In @icons/left-chevron.tsx around lines 12-21, The start callback is async
(returns Promise<void>) but the interface AnimatedIconHandle declares
startAnimation: () => void; update the interface in icons/types.ts to declare
startAnimation: () => Promise<void> (and any other related methods that should
be async), update implementations like start in left-chevron.tsx and other
animated icon files (e.g., top-chevron.tsx) to match that signature, and wrap
the await animate(...) call inside start with a try/catch that logs or surfaces
animation errors (use the existing animate and process/logger utilities) so
failures don’t silently reject.

Comment thread icons/vue-icon.tsx Outdated
Comment thread public/r/top-chevron.json
Comment on lines +7 to +12
"files": [
{
"path": "icons/top-chevron.tsx",
"content": "import { forwardRef, useImperativeHandle, useCallback } from \"react\";\nimport { AnimatedIconHandle, AnimatedIconProps } from \"./types\";\nimport { motion, useAnimate } from \"motion/react\";\n\nconst TopChevron = forwardRef<AnimatedIconHandle, AnimatedIconProps>(\n (\n { size = 24, color = \"currentColor\", strokeWidth = 2, className = \"\" },\n ref,\n ) => {\n const [scope, animate] = useAnimate();\n\n const start = useCallback(async () => {\n await animate(\n \".chevron-top\",\n {\n y: [0, -6, 0],\n },\n {\n duration: 0.8,\n ease: \"easeInOut\",\n },\n );\n }, [animate]);\n\n const stop = useCallback(() => {\n animate(\".chevron-top\", { y: 0 }, { duration: 0.2, ease: \"easeInOut\" });\n }, [animate]);\n\n useImperativeHandle(ref, () => ({\n startAnimation: start,\n stopAnimation: stop,\n }));\n\n return (\n <motion.div\n ref={scope}\n className={`inline-flex cursor-pointer items-center justify-center ${className}`}\n onHoverStart={start}\n onHoverEnd={stop}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"chevron-top\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M6 15l6 -6l6 6\" />\n </svg>\n </motion.div>\n );\n },\n);\n\nTopChevron.displayName = \"TopChevron\";\nexport default TopChevron;\n",
"type": "registry:ui"
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

TopChevron implementation has same issues as LeftChevron.

The inline TopChevron component follows an identical pattern to LeftChevron and inherits the same concerns:

  1. Missing error handling in the async start callback
  2. CSS selector brittleness using ".chevron-top"
  3. No animation cancellation mechanism when stop is called during start

The same fixes suggested for LeftChevron apply here (error handling, ref-based targeting, animation cancellation tracking).

🤖 Prompt for AI Agents
In @public/r/top-chevron.json around lines 7-12, TopChevron's start/stop
animation lacks error handling, uses a brittle ".chevron-top" selector, and
doesn't cancel in-flight animations; update the component to (1) use a ref-based
scoped target from useAnimate (the existing scope variable) instead of the
string selector so animations target the svg element reliably, (2) wrap the
async start callback body in try/catch and log or swallow errors appropriately,
and (3) track the returned animation controls or a cancellation token from
animate so stop can cancel or seek to end (use the animate return value inside
start and call its cancel/stop in stop); modify the start and stop functions and
the useImperativeHandle export accordingly (functions: start, stop; component:
TopChevron; hook: useAnimate/scope).

Comment thread public/r/top-chevron.json
Comment on lines +13 to +18
{
"path": "icons/types.ts",
"content": "export interface AnimatedIconProps {\n /** Icon size in pixels or CSS string */\n size?: number | string;\n /** Icon color (defaults to currentColor) */\n color?: string;\n /** SVG stroke width */\n strokeWidth?: number;\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface AnimatedIconHandle {\n startAnimation: () => Promise<void>;\n stopAnimation: () => void;\n}\n",
"type": "registry:ui"
}
],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Type definition inconsistency between registry and source.

The inline types.ts content declares:

startAnimation: () => Promise<void>;

However, the relevant_code_snippets for icons/types.ts shows:

startAnimation: () => void;

This inconsistency means the registry documentation doesn't match the actual source types, which could confuse users and cause type errors. Since both LeftChevron and TopChevron implementations are async (returning Promise<void>), the registry is correct but the source icons/types.ts needs updating.

🔎 Recommended fix for icons/types.ts

Update the source icons/types.ts to match the actual async implementation:

 export interface AnimatedIconHandle {
-  startAnimation: () => void;
+  startAnimation: () => Promise<void>;
   stopAnimation: () => void;
 }
🤖 Prompt for AI Agents
In @public/r/top-chevron.json around lines 13-18, The source type for
AnimatedIconHandle is inconsistent with implementations: update the signature of
startAnimation in AnimatedIconHandle to return Promise<void> (matching async
implementations like LeftChevron and TopChevron); locate the AnimatedIconHandle
interface (symbols: AnimatedIconHandle, startAnimation, stopAnimation) and
change startAnimation: () => void to startAnimation: () => Promise<void>,
leaving stopAnimation as-is so the registry and source types are consistent.

…n, and Download Cloud icons to the icon list.
Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (1)
icons/index.ts (1)

189-197: Consider organizing imports alphabetically.

The new icon imports (ReactIcon through DownloadCloudIcon) are grouped at the end, while LeftChevron and TopChevron were inserted in alphabetical order. For consistency and easier navigation, consider placing all imports in alphabetical order.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8219f82 and 0a78261.

📒 Files selected for processing (1)
  • icons/index.ts
🔇 Additional comments (1)
icons/index.ts (1)

1366-1420: LGTM! Well-structured icon entries.

All 11 new icon entries are correctly structured with appropriate names, component references, and descriptive keywords. The automatic sorting at line 1421 ensures proper alphabetical ordering at runtime.

Comment thread icons/index.ts
Comment on lines +1366 to +1375
{
name: "left-chevron",
icon: LeftChevron,
keywords: ["arrow", "left", "back", "previous"],
},
{
name: "top-chevron",
icon: TopChevron,
keywords: ["arrow", "top", "up", "collapse"],
},
Copy link
Copy Markdown
Contributor

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:

#!/bin/bash

# First, let's read the icons/index.ts file to see the actual content around the mentioned line numbers
echo "=== Reading icons/index.ts around line 672 (down-cheveron) ==="
sed -n '665,680p' icons/index.ts

echo ""
echo "=== Reading icons/index.ts around line 872 (right-cheveron) ==="
sed -n '865,880p' icons/index.ts

echo ""
echo "=== Reading icons/index.ts around lines 1366-1375 (new icons) ==="
sed -n '1360,1380p' icons/index.ts

Repository: itshover/itshover

Length of output: 1382


🏁 Script executed:

#!/bin/bash

# Search for all occurrences of both spellings across the codebase
echo "=== All occurrences of 'cheveron' (with 'e') ==="
rg "cheveron" --type ts --type tsx -C 1 2>/dev/null | head -100

echo ""
echo "=== All occurrences of 'chevron' (with 'o') ==="
rg "chevron" --type ts --type tsx -C 1 2>/dev/null | head -100

Repository: itshover/itshover

Length of output: 158


🏁 Script executed:

#!/bin/bash

# Check the total count of each spelling variant in the file
echo "=== Count of 'cheveron' occurrences ==="
grep -c "cheveron" icons/index.ts 2>/dev/null || echo "Not found"

echo ""
echo "=== Count of 'chevron' occurrences ==="
grep -c "chevron" icons/index.ts 2>/dev/null || echo "Not found"

echo ""
echo "=== Check if icons are exported in a public API/index ==="
fd -e ts -e tsx | xargs grep -l "export.*cheveron\|export.*chevron" 2>/dev/null | head -20

Repository: itshover/itshover

Length of output: 203


🏁 Script executed:

#!/bin/bash

# Check file size to understand scope
echo "=== icons/index.ts file stats ==="
wc -l icons/index.ts

echo ""
echo "=== Check for any documentation about icon naming ==="
fd -e md -e txt | xargs grep -i "chevron\|cheveron" 2>/dev/null | head -20

Repository: itshover/itshover

Length of output: 169


Address spelling inconsistency between icon names: "chevron" vs "cheveron".

Existing directional chevron icons use the misspelled "cheveron" (down-cheveron, right-cheveron), while the new additions use the correct spelling "chevron" (left-chevron, top-chevron). This inconsistency fragments the API surface and degrades searchability for users looking for chevron icons.

Consider:

  1. Adding aliases for backward compatibility if renaming existing icons
  2. Documenting the spelling variation
  3. Planning a future migration to standardize on "chevron"

Copy link
Copy Markdown
Contributor

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

Fix all issues with AI Agents 🤖
In @icons/download-cloud-icon.tsx:
- Around line 5-54: The infinite animation started in DownloadCloudIcon using
animate(...) inside start should capture and stop the returned controls on
unmount; change the logic so animate(...) is called inside a useEffect (or store
the controls when start runs) and in the cleanup call controls.stop() to
explicitly stop the repeating animation (reference: DownloadCloudIcon, start,
animate, useEffect, controls.stop).

In @icons/tailwind-icon.tsx:
- Around line 5-28: The TailwindIcon component currently renders custom wave
paths; replace the SVG children inside the TailwindIcon render with the official
Tailwind CSS mark (the three smooth, stacked swooshes forming the T monogram)
while preserving the component API (props size, color, strokeWidth, className)
and the existing animation hooks (useAnimate, start, stop). Ensure the official
path elements include the same class names used by the animations (e.g.,
"wave-1", "wave-2", and add a third class if needed) so the start and stop
callbacks still target animated elements, keep the SVG viewBox and fill/stroke
attributes consistent with the logo (use color prop for fill/stroke), and do not
change the start/stop function names or their animate calls.
🧹 Nitpick comments (2)
icons/cloud-icon.tsx (1)

29-50: Consider adding accessibility attributes for better screen reader support.

The icon currently has no aria-label or role attribute. If this icon conveys meaningful information (e.g., "cloud storage" or "weather"), consider adding aria-label to the SVG or the wrapper. If it's purely decorative, aria-hidden="true" would be appropriate.

Example:

<svg
  xmlns="http://www.w3.org/2000/svg"
  aria-label="Cloud"
  role="img"
  // ... other props
>

Or for decorative icons:

<svg
  xmlns="http://www.w3.org/2000/svg"
  aria-hidden="true"
  // ... other props
>
icons/tailwind-icon.tsx (1)

57-62: Verify the fill + stroke combination for wave-2.

The inner wave path (wave-2) uses both stroke and fill with 50% opacity, creating a layered semi-transparent effect. Ensure this visual treatment aligns with the intended design, as it differs from typical outlined-only icon styling.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a78261 and 73ef53f.

📒 Files selected for processing (10)
  • icons/calendar-icon.tsx
  • icons/cloud-icon.tsx
  • icons/download-cloud-icon.tsx
  • icons/left-chevron.tsx
  • icons/map-pin-icon.tsx
  • icons/menu-icon.tsx
  • icons/svelte-icon.tsx
  • icons/tailwind-icon.tsx
  • icons/top-chevron.tsx
  • icons/vue-icon.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
  • icons/calendar-icon.tsx
  • icons/top-chevron.tsx
  • icons/menu-icon.tsx
  • icons/map-pin-icon.tsx
  • icons/left-chevron.tsx
  • icons/svelte-icon.tsx
🧰 Additional context used
🧬 Code graph analysis (4)
icons/vue-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/download-cloud-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/tailwind-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
icons/cloud-icon.tsx (1)
icons/types.ts (2)
  • AnimatedIconHandle (12-15)
  • AnimatedIconProps (1-10)
🔇 Additional comments (12)
icons/cloud-icon.tsx (2)

12-18: Previous type mismatch resolved—start callback now correctly returns void.

The previously flagged issue where the start callback was declared as async (returning Promise<void> instead of void) has been fixed. The callback now correctly matches the AnimatedIconHandle.startAnimation: () => void interface, and the animate call uses the fire-and-forget pattern as intended.


1-55: Implementation follows the established icon pattern correctly.

The component structure, animation logic, imperative handle exposure, and hover behavior all align with the icon library conventions. The bobbing animation (y-axis translation) is smooth and appropriate for a cloud icon.

icons/vue-icon.tsx (2)

12-18: Previous issue resolved - animation callback no longer awaits infinite promise.

The start callback correctly removes async/await keywords, allowing the infinite animation to run without blocking callers. This resolves the issue flagged in the previous review.


5-61: Clean implementation following established icon pattern.

The component correctly implements the AnimatedIconHandle interface, uses proper type signatures, and the animation logic is sound. The SVG structure with a filled inner-v path and stroked outer path creates appropriate visual feedback. Hover handlers are properly wired to the animation controls.

icons/download-cloud-icon.tsx (5)

1-3: LGTM!

The imports are appropriate and all are utilized in the component implementation.


12-22: LGTM! Animation callbacks correctly implemented.

The animation logic is clean and appropriate:

  • Downward bounce effect (y: [0, 4, 0]) fits the download icon semantics
  • Proper use of useCallback for memoization
  • Correctly implemented without async/await as noted in the commit message, since the animate function is synchronous

24-27: LGTM!

The imperative handle correctly exposes the animation API and follows the AnimatedIconHandle interface.


56-57: LGTM!

The displayName is properly set for debugging, and the export follows the established pattern.


29-35: This component's design pattern is correct as-is.

The cursor-pointer with hover-triggered animation is the intended design across all animated icons in the codebase (150+ components follow this pattern). These are presentation components with imperative animation control via refs (startAnimation/stopAnimation), not standalone interactive elements. The hover cursor appropriately signals that hovering triggers animation behavior. Consumer code is responsible for wrapping icons in interactive containers (buttons, links, etc.) if click handling is needed. No accessibility attributes are required at this layer.

Likely an incorrect or invalid review comment.

icons/tailwind-icon.tsx (3)

1-3: LGTM!

The imports are appropriate and follow the established pattern for animated icon components in this codebase.


30-33: LGTM!

The imperative handle correctly exposes the animation controls, consistent with the AnimatedIconHandle interface and the pattern used across other icon components.


69-70: LGTM!

The displayName is properly set for debugging, and the default export follows the established pattern.

Comment on lines +5 to +54
const DownloadCloudIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
(
{ size = 24, color = "currentColor", strokeWidth = 2, className = "" },
ref,
) => {
const [scope, animate] = useAnimate();

const start = useCallback(() => {
animate(
".arrow",
{ y: [0, 4, 0] },
{ duration: 0.8, ease: "easeInOut", repeat: Infinity },
);
}, [animate]);

const stop = useCallback(() => {
animate(".arrow", { y: 0 }, { duration: 0.3 });
}, [animate]);

useImperativeHandle(ref, () => ({
startAnimation: start,
stopAnimation: stop,
}));

return (
<motion.div
ref={scope}
className={`inline-flex cursor-pointer items-center justify-center ${className}`}
onHoverStart={start}
onHoverEnd={stop}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke={color}
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M17.5 19c.7 0 1.3-.1 1.9-.4 1.6-.7 2.6-2.2 2.6-4 0-2.4-1.9-4.4-4.2-4.1C17.2 7.2 14.8 5 12 5c-2.4 0-4.5 1.5-5.3 3.7C4.4 9 2.5 11 2.5 13.5c0 3 2.5 5.5 5.5 5.5h9.5z" />
<polyline points="8 12 12 16 16 12" className="arrow" />
<line x1="12" y1="8" x2="12" y2="16" className="arrow" />
</svg>
</motion.div>
);
},
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

motion/react useAnimate cleanup infinite animations unmount

💡 Result:

Short answer: either rely on useAnimate’s automatic cleanup or stop the animation manually in a useEffect cleanup. Manual stop is safer for explicit infinite loops.

Recommended patterns

  • Automatic cleanup (built-in): useAnimate cleans up animations started with its animate function when the component is removed. [1]

  • Manual cleanup (explicit, recommended for infinite/repeating animations):

    1. Capture the controls returned by animate().
    2. Call controls.stop() (or controls.cancel/controls.finish if applicable) in the effect cleanup or on unmount.

Example (React + motion/react):
const [scope, animate] = useAnimate();
useEffect(() => {
const controls = animate(scope.current, { opacity: [0,1] }, { duration: 1, repeat: Infinity });
return () => controls?.stop(); // explicit stop on unmount
}, []);

References

  • useAnimate docs (React) — automatic cleanup & examples. [1]
  • useAnimate example showing controls.stop() in cleanup. [2]

Implement manual animation cleanup for infinite animations.

While motion/react automatically cleans up animations on unmount, the recommended pattern for infinite animations (like repeat: Infinity on line 16) is to manually stop them using controls. Capture the controls returned by animate() and call controls.stop() in a useEffect cleanup function. This provides more explicit control and follows motion/react best practices.

For example:

useEffect(() => {
  const controls = animate(
    ".arrow",
    { y: [0, 4, 0] },
    { duration: 0.8, ease: "easeInOut", repeat: Infinity },
  );
  return () => controls?.stop();
}, [animate]);
🤖 Prompt for AI Agents
In @icons/download-cloud-icon.tsx around lines 5-54, The infinite animation
started in DownloadCloudIcon using animate(...) inside start should capture and
stop the returned controls on unmount; change the logic so animate(...) is
called inside a useEffect (or store the controls when start runs) and in the
cleanup call controls.stop() to explicitly stop the repeating animation
(reference: DownloadCloudIcon, start, animate, useEffect, controls.stop).

Comment thread icons/tailwind-icon.tsx
Comment on lines +5 to +28
const TailwindIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
(
{ size = 24, color = "currentColor", strokeWidth = 2, className = "" },
ref,
) => {
const [scope, animate] = useAnimate();

const start = useCallback(() => {
animate(
".wave-1",
{ x: [-2, 2, -2] },
{ duration: 1.5, ease: "easeInOut", repeat: Infinity },
);
animate(
".wave-2",
{ x: [2, -2, 2] },
{ duration: 1.5, ease: "easeInOut", repeat: Infinity },
);
}, [animate]);

const stop = useCallback(() => {
animate(".wave-1", { x: 0 }, { duration: 0.5 });
animate(".wave-2", { x: 0 }, { duration: 0.5 });
}, [animate]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n icons/tailwind-icon.tsx

Repository: itshover/itshover

Length of output: 2589


🌐 Web query:

official Tailwind CSS logo SVG design

💡 Result:

The official Tailwind CSS logo SVGs (mark and logotype) are available from the Tailwind brand assets page — download the SVGs from the "Mark" and "Logotype" links on the Tailwind brand page. [1]

A mirrored copy (including the tailwindcss-logotype.zip referenced on the brand page) is also available on Wikimedia Commons. [2]

Sources:

  • Tailwind CSS — Brand (assets & usage guidelines). [1]
  • Tailwind CSS logo files on Wikimedia Commons (includes link to tailwindcss-logotype.zip). [2]

🌐 Web query:

Tailwind CSS logo design description T-shape official mark

💡 Result:

Tailwind’s official mark is a simplified, standalone symbol (available from the Tailwind “Brand” page) that reads as an abstract, T‑shaped monogram made of three smooth, stacked swooshes/curved strokes — a compact geometric “T”/wave mark used as the logo icon separate from the wordmark. Use and downloads are governed by Tailwind’s brand/trademark guidelines. [1][2]

Sources:

  • Tailwind CSS — Brand / Assets (mark & logotype). [Tailwind Labs]. [1]
  • “Tailwind Logo SVG” description (stylized letter “T”, three‑stroke/wave mark). [2]

Replace SVG paths with the official Tailwind CSS logo.

The current SVG paths create abstract bubble/wave shapes that don't match the official Tailwind CSS logo. The official mark is a T-shaped monogram composed of three smooth, stacked swooshes—a distinctive geometric design that users expect for the Tailwind branding.

While the animation logic (opposing horizontal wave movements) is correctly implemented, the underlying icon must match the official Tailwind CSS mark.

🤖 Prompt for AI Agents
In @icons/tailwind-icon.tsx around lines 5-28, The TailwindIcon component
currently renders custom wave paths; replace the SVG children inside the
TailwindIcon render with the official Tailwind CSS mark (the three smooth,
stacked swooshes forming the T monogram) while preserving the component API
(props size, color, strokeWidth, className) and the existing animation hooks
(useAnimate, start, stop). Ensure the official path elements include the same
class names used by the animations (e.g., "wave-1", "wave-2", and add a third
class if needed) so the start and stop callbacks still target animated elements,
keep the SVG viewBox and fill/stroke attributes consistent with the logo (use
color prop for fill/stroke), and do not change the start/stop function names or
their animate calls.

@KompallyAkhil
Copy link
Copy Markdown
Contributor

Preview of the PR and components that are added
https://github.com/user-attachments/assets/2eea8150-f39c-4649-945e-3cee512f5d76

@Abhijit-Jha
Copy link
Copy Markdown
Member

Check CONTRIBUTION.md

@Abhijit-Jha
Copy link
Copy Markdown
Member

Closing this issue.

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.

4 participants