diff --git a/packages/twenty-website/src/icons/informative/Edit.tsx b/packages/twenty-website/src/icons/informative/Edit.tsx index 65957cb75b0..e3b00810b76 100644 --- a/packages/twenty-website/src/icons/informative/Edit.tsx +++ b/packages/twenty-website/src/icons/informative/Edit.tsx @@ -1,12 +1,6 @@ -'use client'; - -import { useId } from 'react'; - type EditIconProps = { size: number; color: string; strokeWidth?: number }; export function EditIcon({ size, color, strokeWidth = 2 }: EditIconProps) { - const clipPathId = useId().replace(/:/g, ''); - return ( - + - - - - - ); } diff --git a/packages/twenty-website/src/sections/Hero/components/ProductVisual/HeroVisualScroll.tsx b/packages/twenty-website/src/sections/Hero/components/ProductVisual/HeroVisualScroll.tsx index f02e9801cb0..ee82869a2be 100644 --- a/packages/twenty-website/src/sections/Hero/components/ProductVisual/HeroVisualScroll.tsx +++ b/packages/twenty-website/src/sections/Hero/components/ProductVisual/HeroVisualScroll.tsx @@ -2,8 +2,8 @@ import { type ReactNode, + useCallback, useEffect, - useId, useLayoutEffect, useRef, useState, @@ -24,6 +24,7 @@ import { getHeroScrollMotion, } from './hero-scroll-colors'; import { useProductHeroMenuSync } from './product-hero-menu-sync'; +import { PRODUCT_HERO_AI_TABS_ID } from './product-hero-tabs-id'; import { ProductVisual } from './ProductVisual'; import { useHeroScrollProgress } from './use-hero-scroll-progress'; @@ -215,8 +216,8 @@ const PanelVisual = styled.div` } `; -const INTRO_PANEL_COLORS = getHeroScrollColors(0); -const AI_PANEL_COLORS = getHeroScrollColors(1); +const INTRO_PANEL_COLORS = getHeroScrollColors(0, 0); +const AI_PANEL_COLORS = getHeroScrollColors(1, 1); export function HeroVisualScroll({ aiBody, @@ -236,9 +237,8 @@ export function HeroVisualScroll({ useHeroScrollProgress(trackRef); const [activeTab, setActiveTab] = useState(0); const [panelStepPixels, setPanelStepPixels] = useState(0); - const idPrefix = useId(); - const heroColors = getHeroScrollColors(colorMix); + const heroColors = getHeroScrollColors(colorMix, panelMix); const heroMotion = getHeroScrollMotion(panelMix, panelStepPixels); const introPanelColors = isScrollDriven ? heroColors : INTRO_PANEL_COLORS; const aiPanelColors = isScrollDriven ? heroColors : AI_PANEL_COLORS; @@ -264,38 +264,38 @@ export function HeroVisualScroll({ return () => resizeObserver.disconnect(); }, [aiBody, aiHeading, introBody, introHeading, isScrollDriven, tabs]); - useEffect(() => { + const syncMenuColorMix = useCallback(() => { if (!menuSync) { return; } - const updateMenuColorMix = () => { - const track = trackRef.current; - const trackRect = track?.getBoundingClientRect() ?? null; - const aiPanelRect = aiPanelRef.current?.getBoundingClientRect() ?? null; + const track = trackRef.current; + const trackRect = track?.getBoundingClientRect() ?? null; + const aiPanelRect = aiPanelRef.current?.getBoundingClientRect() ?? null; - menuSync.setMenuColorMix( - getHeroMenuColorMix({ - aiPanelRect, - colorMix, - isScrollDriven, - navHeight: NAV_HEIGHT, - panelMix, - trackRect, - viewportHeight: window.innerHeight, - }), - ); - }; + menuSync.setMenuDarkOpacity( + getHeroMenuColorMix({ + aiPanelRect, + colorMix, + isScrollDriven, + navHeight: NAV_HEIGHT, + panelMix, + trackRect, + viewportHeight: window.innerHeight, + }), + ); + }, [colorMix, isScrollDriven, menuSync, panelMix]); - updateMenuColorMix(); - window.addEventListener('scroll', updateMenuColorMix, { passive: true }); - window.addEventListener('resize', updateMenuColorMix); + useEffect(() => { + syncMenuColorMix(); + window.addEventListener('scroll', syncMenuColorMix, { passive: true }); + window.addEventListener('resize', syncMenuColorMix); return () => { - window.removeEventListener('scroll', updateMenuColorMix); - window.removeEventListener('resize', updateMenuColorMix); + window.removeEventListener('scroll', syncMenuColorMix); + window.removeEventListener('resize', syncMenuColorMix); }; - }, [colorMix, isScrollDriven, menuSync, panelMix]); + }, [syncMenuColorMix]); return ( @@ -393,7 +393,7 @@ export function HeroVisualScroll({ > diff --git a/packages/twenty-website/src/sections/Hero/components/ProductVisual/hero-scroll-colors.ts b/packages/twenty-website/src/sections/Hero/components/ProductVisual/hero-scroll-colors.ts index 2743b95ad1d..2a52716bf18 100644 --- a/packages/twenty-website/src/sections/Hero/components/ProductVisual/hero-scroll-colors.ts +++ b/packages/twenty-website/src/sections/Hero/components/ProductVisual/hero-scroll-colors.ts @@ -51,7 +51,7 @@ function smoothstep(value: number): number { return value * value * (3 - 2 * value); } -// Dark reads as a layer fading in over white — ramps up slowly, then completes. +// Dark reads as a layer fading in over white — ramps up slowly on scroll. function mapColorMixToDarkOpacity(colorMix: number): number { if (colorMix <= 0) { return 0; @@ -64,6 +64,17 @@ function mapColorMixToDarkOpacity(colorMix: number): number { return Math.pow(colorMix, 1.85); } +function getDarkOverlayOpacity(colorMix: number, panelMix: number): number { + const fromScroll = mapColorMixToDarkOpacity(colorMix); + const fromPanel = mapColorMixToDarkOpacity(panelMix); + + if (panelMix >= 0.5) { + return Math.max(fromScroll, fromPanel); + } + + return fromScroll; +} + function parseHexColor(hex: string): [number, number, number] { const normalized = hex.replace('#', ''); @@ -114,9 +125,7 @@ function interpolateRgba(from: string, to: string, mix: number): string { return `rgba(${red}, ${green}, ${blue}, ${alpha})`; } -export function getHeroScrollColors(colorMix: number) { - const darkOverlayOpacity = mapColorMixToDarkOpacity(colorMix); - +function buildHeroScrollColors(darkOverlayOpacity: number) { return { backgroundColor: LIGHT_BACKGROUND, darkOverlayOpacity, @@ -135,6 +144,14 @@ export function getHeroScrollColors(colorMix: number) { }; } +export function getHeroScrollColors(colorMix: number, panelMix = colorMix) { + return buildHeroScrollColors(getDarkOverlayOpacity(colorMix, panelMix)); +} + +export function getHeroScrollColorsFromOpacity(darkOverlayOpacity: number) { + return buildHeroScrollColors(darkOverlayOpacity); +} + export function getHeroMenuScheme(darkOverlayOpacity: number) { return darkOverlayOpacity >= 0.55 ? 'secondary' : 'primary'; } @@ -187,10 +204,10 @@ export function getHeroMenuColorMix({ return Math.min(1, visibilityRatio) * smoothstep(Math.min(1, exitFade)); } - let menuColorMix = colorMix; + let menuDarkOpacity = getDarkOverlayOpacity(colorMix, panelMix); if (panelMix < 0.5) { - menuColorMix = colorMix * (panelMix / 0.5); + menuDarkOpacity = getDarkOverlayOpacity(colorMix * (panelMix / 0.5), panelMix); } const isExitingHero = @@ -202,10 +219,10 @@ export function getHeroMenuColorMix({ Math.max(viewportHeight - navHeight, 1); const exitFade = smoothstep(Math.min(1, Math.max(0, exitProgress))); - menuColorMix = menuColorMix * (1 - exitFade); + menuDarkOpacity = menuDarkOpacity * (1 - exitFade); } - return menuColorMix; + return menuDarkOpacity; } export function getHeroScrollMotion( diff --git a/packages/twenty-website/src/sections/Hero/components/ProductVisual/product-hero-menu-sync.tsx b/packages/twenty-website/src/sections/Hero/components/ProductVisual/product-hero-menu-sync.tsx index 7ad0bb73d27..f36023725e5 100644 --- a/packages/twenty-website/src/sections/Hero/components/ProductVisual/product-hero-menu-sync.tsx +++ b/packages/twenty-website/src/sections/Hero/components/ProductVisual/product-hero-menu-sync.tsx @@ -15,10 +15,13 @@ import type { MenuSocialLinkType, } from '@/sections/Menu/types'; -import { getHeroMenuScheme, getHeroScrollColors } from './hero-scroll-colors'; +import { + getHeroMenuScheme, + getHeroScrollColorsFromOpacity, +} from './hero-scroll-colors'; type ProductHeroMenuContextValue = { - setMenuColorMix: (colorMix: number) => void; + setMenuDarkOpacity: (darkOverlayOpacity: number) => void; }; const ProductHeroMenuContext = @@ -39,9 +42,9 @@ export function ProductHeroMenuSync({ navItems, socialLinks, }: ProductHeroMenuSyncProps) { - const [menuColorMix, setMenuColorMix] = useState(0); + const [menuDarkOpacity, setMenuDarkOpacity] = useState(0); - const heroMenuColors = getHeroScrollColors(menuColorMix); + const heroMenuColors = getHeroScrollColorsFromOpacity(menuDarkOpacity); const menuScheme: MenuScheme = getHeroMenuScheme( heroMenuColors.darkOverlayOpacity, ); @@ -49,7 +52,7 @@ export function ProductHeroMenuSync({ const contextValue = useMemo( () => ({ - setMenuColorMix, + setMenuDarkOpacity, }), [], ); diff --git a/packages/twenty-website/src/sections/Hero/components/ProductVisual/product-hero-tabs-id.ts b/packages/twenty-website/src/sections/Hero/components/ProductVisual/product-hero-tabs-id.ts new file mode 100644 index 00000000000..aac6c2e3d56 --- /dev/null +++ b/packages/twenty-website/src/sections/Hero/components/ProductVisual/product-hero-tabs-id.ts @@ -0,0 +1 @@ +export const PRODUCT_HERO_AI_TABS_ID = 'product-hero-ai-tabs';