From 782e9f66b9a02500da3d382726c3d98cd5b48267 Mon Sep 17 00:00:00 2001
From: Adithya Krishna
Date: Sun, 8 Mar 2026 15:02:27 +0530
Subject: [PATCH 1/3] feat: smoother animation for icons
---
.../app/(landing)/components/hero/hero.tsx | 78 ++++++++++++++++---
1 file changed, 68 insertions(+), 10 deletions(-)
diff --git a/apps/sim/app/(landing)/components/hero/hero.tsx b/apps/sim/app/(landing)/components/hero/hero.tsx
index 546dc47627f..21c3feba22b 100644
--- a/apps/sim/app/(landing)/components/hero/hero.tsx
+++ b/apps/sim/app/(landing)/components/hero/hero.tsx
@@ -1,6 +1,7 @@
'use client'
import React from 'react'
+import { motion } from 'framer-motion'
import { ArrowUp, CodeIcon } from 'lucide-react'
import { useRouter } from 'next/navigation'
import { type Edge, type Node, Position } from 'reactflow'
@@ -152,6 +153,16 @@ export default function Hero() {
const [lastHoveredIndex, setLastHoveredIndex] = React.useState(null)
const intervalRef = React.useRef(null)
+ const iconRowRef = React.useRef(null)
+ const buttonRefs = React.useRef<(HTMLDivElement | null)[]>([])
+ const [pillLayout, setPillLayout] = React.useState<{
+ left: number
+ top: number
+ width: number
+ height: number
+ } | null>(null)
+ const [layoutVersion, setLayoutVersion] = React.useState(0)
+
/**
* Handle service icon click to populate textarea with template
*/
@@ -246,6 +257,29 @@ export default function Hero() {
}
}
+ const activeIconIndex =
+ isUserHovering && lastHoveredIndex !== null ? lastHoveredIndex : autoHoverIndex
+
+ React.useLayoutEffect(() => {
+ const container = iconRowRef.current
+ const target = buttonRefs.current[activeIconIndex]
+ if (!container || !target) return
+ const cr = container.getBoundingClientRect()
+ const tr = target.getBoundingClientRect()
+ setPillLayout({
+ left: tr.left - cr.left,
+ top: tr.top - cr.top,
+ width: tr.width,
+ height: tr.height,
+ })
+ }, [activeIconIndex, layoutVersion, visibleIconCount])
+
+ React.useEffect(() => {
+ const onResize = () => setLayoutVersion((v) => v + 1)
+ window.addEventListener('resize', onResize)
+ return () => window.removeEventListener('resize', onResize)
+ }, [])
+
/**
* Handle form submission
*/
@@ -377,24 +411,48 @@ export default function Hero() {
Build and deploy AI agent workflows
- {/* Service integration buttons */}
+ {pillLayout !== null && (
+
+ )}
{serviceIcons.slice(0, visibleIconCount).map((service, index) => {
const Icon = service.icon
return (
- handleServiceClick(service.key as keyof typeof SERVICE_TEMPLATES)}
- onMouseEnter={() => setLastHoveredIndex(index)}
- style={service.style}
- isAutoHovered={!isUserHovering && index === autoHoverIndex}
+ ref={(el) => {
+ buttonRefs.current[index] = el
+ }}
>
-
-
+ handleServiceClick(service.key as keyof typeof SERVICE_TEMPLATES)}
+ onMouseEnter={() => setLastHoveredIndex(index)}
+ style={service.style}
+ isAutoHovered={!isUserHovering && index === autoHoverIndex}
+ >
+
+
+
)
})}
From 48782d2bd3bb391a769adcc6500046f4b15504e5 Mon Sep 17 00:00:00 2001
From: Adithya Krishna
Date: Sun, 8 Mar 2026 15:54:35 +0530
Subject: [PATCH 2/3] chore: fix state lag on hover
---
.../components/hero/components/icon-button.tsx | 11 +++++++++--
apps/sim/app/(landing)/components/hero/hero.tsx | 2 +-
2 files changed, 10 insertions(+), 3 deletions(-)
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..254853437f0 100644
--- a/apps/sim/app/(landing)/components/hero/components/icon-button.tsx
+++ b/apps/sim/app/(landing)/components/hero/components/icon-button.tsx
@@ -9,6 +9,7 @@ interface IconButtonProps {
style?: React.CSSProperties
'aria-label': string
isAutoHovered?: boolean
+ highlightFromParent?: boolean
}
export function IconButton({
@@ -18,7 +19,13 @@ export function IconButton({
style,
'aria-label': ariaLabel,
isAutoHovered = false,
+ highlightFromParent = false,
}: IconButtonProps) {
+ const showOwnHighlight = !highlightFromParent && isAutoHovered
+ const hoverHighlight = !highlightFromParent
+ ? 'hover:border-[#E5E5E5] hover:shadow-[0_2px_4px_0_rgba(0,0,0,0.08)]'
+ : ''
+
return (