feat: add missing top and left chevron icons#40
feat: add missing top and left chevron icons#40krockxz wants to merge 5 commits intoitshover:masterfrom
Conversation
|
@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. |
📝 WalkthroughWalkthroughAdds 11 new forwardRef animated icon components (each exposing start/stop imperative methods), updates Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
Comment |
30b7438 to
b34814d
Compare
There was a problem hiding this comment.
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
LeftCheveronandTopCheveronshare 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
AnimatedIconHandleinterface definesstartAnimation: () => void, but the actual implementation in both components isasync(lines 12-23 in the component files), which returnsPromise<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
📒 Files selected for processing (11)
app/request/page.tsxcomponents/container.tsxcomponents/navbar.tsxicons/index.tsicons/left-cheveron.tsxicons/top-cheveron.tsxpublic/r/layout-sidebar-right-collapse-icon.jsonpublic/r/left-cheveron.jsonpublic/r/registry.jsonpublic/r/top-cheveron.jsonregistry.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 updateThe 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 addedThe new "/request" navigation link is properly implemented and consistent with existing desktop navigation items.
149-155: LGTM - Mobile navigation link addedThe 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 pageThe 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 implementationThe Container component now accepts an optional className prop and properly merges it with base classes using the
cnutility. 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
forwardRefwith proper type annotations, and default props are sensible. The imports are clean and necessary.
29-32: LGTM! Imperative handle correctly exposes animation controls.The
useImperativeHandleproperly exposesstartAnimationandstopAnimationmethods, allowing parent components to programmatically control the animation.
34-57: LGTM! JSX structure is well-implemented.The component correctly:
- Uses
motion.divfor hover-triggered animations- Sets appropriate cursor styling for interactive elements
- Applies the
chevron-topclass 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 6correctly 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-cheveronandtop-cheveronregistry entries correctly:
- Follow the established schema
- Declare the
motiondependency- 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
AnimatedIconPropsinterface is well-structured with:
- Clear JSDoc documentation
- Flexible
sizetype (number | string)- Sensible optional properties with defaults
- Proper typing for SVG attributes
The
AnimatedIconHandleinterface issue (async return type) was already flagged in the previous comment.
4-4: The motion package is correctly installed and properly imported. The component aticons/left-cheveron.tsxcorrectly importsmotionanduseAnimatefrommotion/reactand uses them appropriately withmotion.divand theuseAnimate()hook. The dependency declaration in the registry file is accurate.
There was a problem hiding this comment.
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
📒 Files selected for processing (8)
icons/index.tsicons/left-cheveron.tsxicons/top-cheveron.tsxpublic/r/layout-sidebar-right-collapse-icon.jsonpublic/r/left-cheveron.jsonpublic/r/registry.jsonpublic/r/top-cheveron.jsonregistry.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-cheveronandtop-cheveronfollow 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
|
Hey @krockxz , can you please share a recording of the animations you’ve created? You can refer to Lucide, Tabler Icons, or other icon libraries to find similar icons and animate them. Thank you. |
|
@Abhijit-Jha To make this PR more substantial, I propose adding the following missing icons: 1. Tech Stack
2. UI Components
3. Utility
Let me know if you'd like me to proceed with these updates |
|
Go ahead @krockxz |
…ud, Download Cloud, Map Pin, React, Svelte)
|
@Abhijit-Jha updated the PR with the icons |
There was a problem hiding this comment.
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
LeftChevroncomponent content matches the source fileicons/left-chevron.tsxreviewed earlier. It inherits the same issues already flagged:
- Missing error handling in async
startcallback- CSS selector brittleness
- No animation cancellation mechanism
Refer to the detailed comments on
icons/left-chevron.tsxfor recommended fixes.
13-18: Type definition mismatch between registry and source types.ts.The inline
types.tscontent declaresstartAnimation: () => Promise<void>, but therelevant_code_snippetsfor the sourceicons/types.tsshows() => 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 sourceicons/types.tsneeds to be updated toPromise<void>.
🧹 Nitpick comments (7)
icons/map-pin-icon.tsx (1)
12-18: Remove unnecessaryasynckeyword.The
startcallback is marked asasyncbut doesn't useawait. 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
forwardRefstill works in React 19, ref is now a regular prop. For modern React 19 code, you can simplify by acceptingrefdirectly in props and removingforwardRef.🔎 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 unnecessaryasynckeyword.The
startcallback is declared asasyncbut doesn't useawaitor return the promise fromanimate(). Since the animation repeats infinitely and you don't need to wait for it to complete, theasynckeyword 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
stopAnimationis called mid-rotation, animating from the current rotation back to0may 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
clsxif already available in the project.
23-25: Consider cancelling ongoing animations whenstopis called.If a user hovers out (triggering
stop) while thestartanimation is still running, both animations will execute simultaneously on the same element, potentially causing visual glitches. The motion library supports bothcancel()(resets animation effect) andstop()(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
📒 Files selected for processing (15)
icons/calendar-icon.tsxicons/cloud-icon.tsxicons/download-cloud-icon.tsxicons/index.tsicons/left-chevron.tsxicons/map-pin-icon.tsxicons/menu-icon.tsxicons/react-icon.tsxicons/svelte-icon.tsxicons/tailwind-icon.tsxicons/top-chevron.tsxicons/vue-icon.tsxpublic/r/left-chevron.jsonpublic/r/top-chevron.jsonregistry.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
forwardRefwith proper TypeScript generics, and prop defaults are sensible and consistent with other icons in the repository.
24-27: Imperative API correctly implemented.The
useImperativeHandlecorrectly exposes theAnimatedIconHandleinterface, 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
displayNameaids 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/reactimport path is valid for the installed Motion library version (^12.23.26).icons/cloud-icon.tsx (3)
20-22: LGTM!The
stopcallback 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
displayNameimproves 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
useAnimateis properly initialized.
24-27: LGTM!The imperative handle correctly exposes
startAnimationandstopAnimationmethods as defined by theAnimatedIconHandleinterface.
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
.arrowclass, 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: whenanimate()is called on the same element/property, it automatically cancels any existing animation, including infinite animations. This pattern is properly documented in the motion/reactuseAnimateAPI 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
useAnimatefor 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.allcorrectly 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
perspectiveon the parent container for therotateXeffect- 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
pathLengthanimation typically requires initial SVG setup (e.g.,pathLength="1"attribute andstrokeDasharray/strokeDashoffsetstyles) 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
strokeDashoffsetfrom 1 to 0 instead ofpathLength, or verify motion'spathLengthanimation 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
useImperativeHandleproperly 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
motiondependency 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
motiondependency and follows the expected schema format, consistent with thetop-chevronregistry entry.
| const start = useCallback(async () => { | ||
| await animate( | ||
| ".chevron-left", | ||
| { x: [0, -6, 0] }, | ||
| { | ||
| duration: 0.8, | ||
| ease: "easeInOut", | ||
| }, | ||
| ); | ||
| }, [animate]); |
There was a problem hiding this comment.
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.
| 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.
🧩 Analysis chain
🏁 Script executed:
# First, verify the AnimatedIconHandle interface definition
cat icons/types.tsRepository: itshover/itshover
Length of output: 437
🏁 Script executed:
# Also check the full left-chevron.tsx file to see all exports
cat icons/left-chevron.tsxRepository: 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.tsxRepository: 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.
| "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" | ||
| }, |
There was a problem hiding this comment.
TopChevron implementation has same issues as LeftChevron.
The inline TopChevron component follows an identical pattern to LeftChevron and inherits the same concerns:
- Missing error handling in the async
startcallback - CSS selector brittleness using
".chevron-top" - No animation cancellation mechanism when
stopis called duringstart
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).
| { | ||
| "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" | ||
| } | ||
| ], |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
📒 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.
| { | ||
| name: "left-chevron", | ||
| icon: LeftChevron, | ||
| keywords: ["arrow", "left", "back", "previous"], | ||
| }, | ||
| { | ||
| name: "top-chevron", | ||
| icon: TopChevron, | ||
| keywords: ["arrow", "top", "up", "collapse"], | ||
| }, |
There was a problem hiding this comment.
🧩 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.tsRepository: 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 -100Repository: 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 -20Repository: 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 -20Repository: 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:
- Adding aliases for backward compatibility if renaming existing icons
- Documenting the spelling variation
- Planning a future migration to standardize on "chevron"
… in icon components.
There was a problem hiding this comment.
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-labelorroleattribute. If this icon conveys meaningful information (e.g., "cloud storage" or "weather"), consider addingaria-labelto 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
strokeandfillwith 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
📒 Files selected for processing (10)
icons/calendar-icon.tsxicons/cloud-icon.tsxicons/download-cloud-icon.tsxicons/left-chevron.tsxicons/map-pin-icon.tsxicons/menu-icon.tsxicons/svelte-icon.tsxicons/tailwind-icon.tsxicons/top-chevron.tsxicons/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—startcallback now correctly returnsvoid.The previously flagged issue where the
startcallback was declared asasync(returningPromise<void>instead ofvoid) has been fixed. The callback now correctly matches theAnimatedIconHandle.startAnimation: () => voidinterface, 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
startcallback correctly removesasync/awaitkeywords, 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
AnimatedIconHandleinterface, 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
useCallbackfor memoization- Correctly implemented without
async/awaitas noted in the commit message, since theanimatefunction is synchronous
24-27: LGTM!The imperative handle correctly exposes the animation API and follows the
AnimatedIconHandleinterface.
56-57: LGTM!The
displayNameis properly set for debugging, and the export follows the established pattern.
29-35: This component's design pattern is correct as-is.The
cursor-pointerwith 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.
| 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> | ||
| ); | ||
| }, | ||
| ); |
There was a problem hiding this comment.
🧩 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):
- Capture the controls returned by animate().
- 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).
| 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]); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n icons/tailwind-icon.tsxRepository: 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.
|
Preview of the PR and components that are added |
|
Check CONTRIBUTION.md |
|
Closing this issue. |
Added TopCheveron and LeftCheveron to complete the set. Verified consistency with DownCheveron.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.