diff --git a/apps/sim/app/(landing)/components/hero/components/icon-button.tsx b/apps/sim/app/(landing)/components/hero/components/icon-button.tsx index c8e523e2f86..722476b9653 100644 --- a/apps/sim/app/(landing)/components/hero/components/icon-button.tsx +++ b/apps/sim/app/(landing)/components/hero/components/icon-button.tsx @@ -1,5 +1,6 @@ 'use client' +import { forwardRef } from 'react' import type React from 'react' interface IconButtonProps { @@ -8,31 +9,23 @@ interface IconButtonProps { onMouseEnter?: () => void style?: React.CSSProperties 'aria-label': string - isAutoHovered?: boolean } -export function IconButton({ - children, - onClick, - onMouseEnter, - style, - 'aria-label': ariaLabel, - isAutoHovered = false, -}: IconButtonProps) { +export const IconButton = forwardRef(function IconButton( + { children, onClick, onMouseEnter, style, 'aria-label': ariaLabel }, + ref +) { return ( ) -} +}) diff --git a/apps/sim/app/(landing)/components/hero/hero.tsx b/apps/sim/app/(landing)/components/hero/hero.tsx index 546dc47627f..e793f8517bc 100644 --- a/apps/sim/app/(landing)/components/hero/hero.tsx +++ b/apps/sim/app/(landing)/components/hero/hero.tsx @@ -152,6 +152,18 @@ export default function Hero() { const [lastHoveredIndex, setLastHoveredIndex] = React.useState(null) const intervalRef = React.useRef(null) + /** + * Refs for smooth sliding pill highlight + */ + const iconContainerRef = React.useRef(null) + const iconRefs = React.useRef<(HTMLButtonElement | null)[]>([]) + const [pillStyle, setPillStyle] = React.useState({ + opacity: 0, + width: 0, + height: 0, + transform: 'translateX(0px)', + }) + /** * Handle service icon click to populate textarea with template */ @@ -225,6 +237,40 @@ export default function Hero() { } }, [isUserHovering, visibleIconCount]) + /** + * Compute the active icon index and update the pill position + */ + const activeIndex = isUserHovering ? lastHoveredIndex : autoHoverIndex + + const updatePillPosition = React.useCallback(() => { + if (activeIndex == null) { + setPillStyle((prev) => ({ ...prev, opacity: 0 })) + return + } + + const button = iconRefs.current[activeIndex] + const container = iconContainerRef.current + if (!button || !container) return + + const containerRect = container.getBoundingClientRect() + const buttonRect = button.getBoundingClientRect() + const offsetX = buttonRect.left - containerRect.left + + setPillStyle({ + width: buttonRect.width, + height: buttonRect.height, + transform: `translateX(${offsetX}px)`, + opacity: 1, + }) + }, [activeIndex]) + + React.useEffect(() => { + updatePillPosition() + + window.addEventListener('resize', updatePillPosition) + return () => window.removeEventListener('resize', updatePillPosition) + }, [updatePillPosition]) + /** * Handle mouse enter on icon container */ @@ -377,21 +423,30 @@ export default function Hero() { Build and deploy AI agent workflows

+ {/* Sliding highlight pill */} +