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';