mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES/NO ## What kind of change does this PR introduce? Bug fix, feature, docs update, ... ## What is the current behavior? Please link any relevant issues here. ## What is the new behavior? Feel free to include screenshots if it includes visual changes. ## Additional context Add any other context or screenshots. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Refreshed theming across the UI to use modern color expressions and shared theme variables (including OKLCH-based gradients), improving consistency for charts, code blocks, overlays, icons, and decorative backgrounds. * **Bug Fixes** * Improved light/dark color and gradient consistency across axis/grid styling, reference lines, buttons/badges, sidebar accents, loaders, and other visual components. * **Documentation** * Updated styling/theming guidance to align with the revised semantic token system and the updated theme variable usage patterns. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
168 lines
7.4 KiB
TypeScript
168 lines
7.4 KiB
TypeScript
import { isBrowser, useReducedMotion } from 'common'
|
|
import Image from 'next/image'
|
|
import React, { useRef, useState } from 'react'
|
|
import { cn } from 'ui'
|
|
|
|
interface Props {
|
|
className?: string
|
|
}
|
|
|
|
const RealtimeVisual: React.FC<Props> = ({ className }) => {
|
|
const cardRef = useRef<HTMLDivElement | null>(null)
|
|
const [svgTransformSelf, setSvgTransformSelf] = useState<string>('translate(0px, 0px)')
|
|
const [svgTransform, setSvgTransform] = useState<string>('translate(0px, 0px)')
|
|
const [svgTransform2, setSvgTransform2] = useState<string>('translate(0px, 0px)')
|
|
const reduceMotion = isBrowser && useReducedMotion()
|
|
|
|
const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
|
|
if (cardRef.current) {
|
|
const cardRect = cardRef.current.getBoundingClientRect()
|
|
const mouseX = event.clientX - cardRect.left // Mouse X relative to card
|
|
const mouseY = event.clientY - cardRect.top // Mouse Y relative to card
|
|
const cardWidth = cardRect.width
|
|
const cardHeight = cardRect.height
|
|
|
|
const svgX = (mouseX / cardRect.width) * 100 - 50 // Calculate SVG X position
|
|
const svgY = (mouseY / cardRect.height) * 100 - 50 // Calculate SVG Y position
|
|
|
|
// Set the transform to move the SVG in the opposite direction
|
|
setSvgTransform(
|
|
`translate(${mouseX > cardWidth / 4 + 40 ? svgX * 3 : svgX * 1.2}px, ${mouseY > cardHeight / 2 + 30 ? -svgY * 1.5 : -svgY * 2.2}px)`
|
|
)
|
|
setSvgTransform2(
|
|
`translate(${mouseX > cardWidth / 2 + 40 ? -svgX * 3 : svgX * 1.6}px, ${mouseY > cardHeight / 2 - 100 ? svgY * 1.2 : svgY * 2.8}px)`
|
|
)
|
|
setSvgTransformSelf(`translate(${mouseX + 12}px, ${mouseY + 4}px)`)
|
|
}
|
|
}
|
|
|
|
const handleMouseLeave = () => {
|
|
setSvgTransform('translate(0px, 0px)')
|
|
setSvgTransform2('translate(0px, 0px)')
|
|
}
|
|
|
|
return (
|
|
<figure
|
|
ref={cardRef}
|
|
className={cn(
|
|
'absolute inset-0 xl:-bottom-2 2xl:bottom-0 z-0 w-full overflow-hidden pointer-events-auto',
|
|
className
|
|
)}
|
|
role="img"
|
|
aria-label="Supabase Realtime multiplayer app demo"
|
|
onMouseMove={reduceMotion ? undefined : handleMouseMove}
|
|
onMouseLeave={handleMouseLeave} // Reset on mouse leave
|
|
>
|
|
<Image
|
|
src="/images/index/products/realtime-dark.svg"
|
|
alt="Supabase Realtime"
|
|
fill
|
|
sizes="100%"
|
|
quality={100}
|
|
className="hidden dark:block absolute object-cover xl:object-center inset-0"
|
|
/>
|
|
<Image
|
|
src="/images/index/products/realtime-light.svg"
|
|
alt="Supabase Realtime"
|
|
fill
|
|
sizes="100%"
|
|
quality={100}
|
|
className="dark:hidden absolute object-cover xl:object-center inset-0"
|
|
/>
|
|
{/* User 1 */}
|
|
<div
|
|
className="absolute will-change-transform"
|
|
style={{
|
|
position: 'absolute',
|
|
top: '60%',
|
|
left: '30%',
|
|
transform: `${svgTransform} translate(-50%, -50%)`, // Center the SVG
|
|
transition: 'transform 0.75s ease-out', // Smooth transition
|
|
}}
|
|
>
|
|
<svg
|
|
width="30"
|
|
height="38"
|
|
viewBox="0 0 30 38"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
d="M3.58385 1.69742C2.57836 0.865603 1.05859 1.58076 1.05859 2.88572V35.6296C1.05859 37.1049 2.93111 37.7381 3.8265 36.5656L12.5863 25.0943C12.6889 24.96 12.8483 24.8812 13.0173 24.8812H27.3245C28.7697 24.8812 29.4211 23.0719 28.3076 22.1507L3.58385 1.69742Z"
|
|
fill="var(--background-surface-200)"
|
|
stroke="var(--foreground-lighter)"
|
|
strokeLinejoin="round"
|
|
/>
|
|
</svg>
|
|
|
|
<div className="w-[66.70px]! h-[33.35px]! absolute left-full flex items-center justify-center gap-1 -top-6 border border-foreground-lighter/70 rounded-full bg-surface-100">
|
|
<div className="w-1.5 h-1.5 rounded-full bg-foreground-lighter animate-[pulse_600ms_cubic-bezier(0.4,0,0.6,1)_infinite] pause group-hover:run" />
|
|
<div className="w-1.5 h-1.5 rounded-full bg-foreground-lighter animate-[pulse_600ms_cubic-bezier(0.4,0,0.6,1)_200ms_infinite] pause group-hover:run" />
|
|
<div className="w-1.5 h-1.5 rounded-full bg-foreground-lighter animate-[pulse_600ms_cubic-bezier(0.4,0,0.6,1)_400ms_infinite] pause group-hover:run" />
|
|
</div>
|
|
</div>
|
|
{/* User 2 */}
|
|
<div
|
|
className="absolute will-change-transform scale-[80%]"
|
|
style={{
|
|
position: 'absolute',
|
|
top: '80%',
|
|
left: '65%',
|
|
transform: `${svgTransform2} translate(-50%, -50%)`, // Center the SVG
|
|
transition: 'transform 1s ease-out', // Smooth transition
|
|
}}
|
|
>
|
|
<svg
|
|
width="20"
|
|
height="28"
|
|
viewBox="0 0 30 38"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
d="M3.58385 1.69742C2.57836 0.865603 1.05859 1.58076 1.05859 2.88572V35.6296C1.05859 37.1049 2.93111 37.7381 3.8265 36.5656L12.5863 25.0943C12.6889 24.96 12.8483 24.8812 13.0173 24.8812H27.3245C28.7697 24.8812 29.4211 23.0719 28.3076 22.1507L3.58385 1.69742Z"
|
|
fill="var(--background-surface-200)"
|
|
stroke="var(--foreground-lighter)"
|
|
strokeLinejoin="round"
|
|
/>
|
|
</svg>
|
|
|
|
<div className="w-[55px]! h-[28px]! absolute left-full flex items-center justify-center gap-1 -top-6 border border-foreground-muted rounded-full bg-surface-100 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
<div className="w-1.5 h-1.5 rounded-full bg-foreground-lighter animate-[pulse_600ms_cubic-bezier(0.4,0,0.6,1)_infinite] pause group-hover:run" />
|
|
<div className="w-1.5 h-1.5 rounded-full bg-foreground-lighter animate-[pulse_600ms_cubic-bezier(0.4,0,0.6,1)_200ms_infinite] pause group-hover:run" />
|
|
<div className="w-1.5 h-1.5 rounded-full bg-foreground-lighter animate-[pulse_600ms_cubic-bezier(0.4,0,0.6,1)_400ms_infinite] pause group-hover:run" />
|
|
</div>
|
|
</div>
|
|
{/* Self */}
|
|
<div
|
|
className="absolute will-change-transform w-1 h-1 opacity-0 motion-safe:group-hover:opacity-100 delay-0 duration-75 group-hover:duration-300 transition-opacity"
|
|
style={{
|
|
position: 'absolute',
|
|
top: '0',
|
|
left: '0',
|
|
transform: `${svgTransformSelf} translate(-50%, -50%)`, // Center the SVG
|
|
// transition: 'transform 0.1s ease-out', // Smooth transition
|
|
}}
|
|
>
|
|
<div className="w-auto h-auto px-2.5 py-1.5 absolute left-full flex items-center justify-center gap-1 -top-6 border border-brand rounded-full bg-brand-300">
|
|
<div className="w-1.5 h-1.5 rounded-full bg-brand animate-[pulse_600ms_cubic-bezier(0.4,0,0.6,1)_infinite] pause group-hover:run" />
|
|
<div className="w-1.5 h-1.5 rounded-full bg-brand animate-[pulse_600ms_cubic-bezier(0.4,0,0.6,1)_200ms_infinite] pause group-hover:run" />
|
|
<div className="w-1.5 h-1.5 rounded-full bg-brand animate-[pulse_600ms_cubic-bezier(0.4,0,0.6,1)_400ms_infinite] pause group-hover:run" />
|
|
</div>
|
|
</div>
|
|
{/* Gradient to hide animation under text to maintain readability */}
|
|
<div
|
|
className="
|
|
visual-overlay
|
|
absolute pointer-events-none
|
|
w-full h-full max-h-[400px] lg:max-h-none
|
|
inset-0 top-auto
|
|
bg-[linear-gradient(to_top,transparent_0%,transparent_50%,var(--background-surface-75)_85%)]
|
|
"
|
|
/>
|
|
</figure>
|
|
)
|
|
}
|
|
|
|
export default RealtimeVisual
|