mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 12:25:05 +03:00
chore: add microinteractions to hero
This commit is contained in:
1 parent
ae05af36f8
commit
45e2d3772a
6 files changed
+224
-175
No files matched your search
@@ -1,42 +0,0 @@
|
||||
import { Button, Badge, IconArrowRight } from 'ui'
|
||||
import SectionHeader from 'components/UI/SectionHeader'
|
||||
import Solutions from 'data/Solutions.json'
|
||||
import Link from 'next/link'
|
||||
import SectionContainer from '../Layouts/SectionContainer'
|
||||
import ProductIcon from '../ProductIcon'
|
||||
import TextLink from '../TextLink'
|
||||
|
||||
const FeaturesOld = () => {
|
||||
const IconSections = Object.values(Solutions).map((solution: any) => {
|
||||
const { name, description, icon, label, url } = solution
|
||||
if (solution.name === 'Realtime') return null
|
||||
return (
|
||||
<div key={name} className="mb-10 space-y-4 md:mb-0">
|
||||
<div className="flex items-center">
|
||||
<ProductIcon icon={icon} />
|
||||
<dt className="text-scale-1200 ml-4 flex flex-row xl:flex-col">{name}</dt>
|
||||
</div>
|
||||
|
||||
<p className="p">{description}</p>
|
||||
|
||||
{label && (
|
||||
<div>
|
||||
<Badge dot>{label}</Badge>
|
||||
</div>
|
||||
)}
|
||||
{url && <TextLink label={label ? 'Get notified' : 'Learn more'} url={url} />}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<SectionContainer className="space-y-16 pb-0">
|
||||
<h3 className="h3">Build faster and focus on your products</h3>
|
||||
<dl className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 md:grid-cols-2 md:gap-16 lg:grid-cols-4 lg:gap-x-8 xl:gap-x-24">
|
||||
{IconSections}
|
||||
</dl>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default FeaturesOld
|
||||
@@ -8,6 +8,7 @@ import Image from 'next/image'
|
||||
import SectionContainer from '../Layouts/SectionContainer'
|
||||
import { motion } from 'framer-motion'
|
||||
import BackedBy from '../BackedBy'
|
||||
import { useBreakpoint } from 'common'
|
||||
|
||||
const Card = ({
|
||||
classname,
|
||||
@@ -27,7 +28,7 @@ const Card = ({
|
||||
<Link href={url}>
|
||||
<a
|
||||
className={[
|
||||
'relative col-span-1 lg:col-span-4 h-[460px] flex flex-col gap-5 lg:flex-row',
|
||||
'relative col-span-1 lg:col-span-4 h-[360px] md:h-[460px] flex flex-col gap-5 lg:flex-row',
|
||||
classname,
|
||||
].join(' ')}
|
||||
onClick={onClick}
|
||||
@@ -95,13 +96,14 @@ const Features = () => {
|
||||
)
|
||||
}
|
||||
}
|
||||
const isSm = useBreakpoint(640)
|
||||
|
||||
return (
|
||||
<SectionContainer className="space-y-8 max-w-7xl pt-4 md:!pt-0">
|
||||
<dl className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-6 lg:grid-cols-6">
|
||||
<Card
|
||||
url={Solutions['database'].url}
|
||||
title={'PostgreSQL Database'}
|
||||
title={Solutions['database'].name}
|
||||
subtitle="Every project is a full Postgres database, the world's most trusted relational database."
|
||||
onClick={() => sendTelemetryEvent(name)}
|
||||
image={
|
||||
@@ -111,8 +113,8 @@ const Features = () => {
|
||||
src="/images/index/database-dark-hover.jpg"
|
||||
alt="Supabase Postgres Database, hover image with glow"
|
||||
layout="fill"
|
||||
objectPosition="50% 50%"
|
||||
objectFit="cover"
|
||||
objectPosition={isSm ? 'bottom' : '50% 50%'}
|
||||
objectFit={isSm ? 'contain' : 'cover'}
|
||||
quality={100}
|
||||
/>
|
||||
</motion.div>
|
||||
@@ -120,8 +122,8 @@ const Features = () => {
|
||||
src="/images/index/database-dark.jpg"
|
||||
alt="Supabase Postgres Database"
|
||||
layout="fill"
|
||||
objectPosition="50% 50%"
|
||||
objectFit="cover"
|
||||
objectPosition={isSm ? 'bottom' : '50% 50%'}
|
||||
objectFit={isSm ? 'contain' : 'cover'}
|
||||
quality={100}
|
||||
/>
|
||||
</div>
|
||||
@@ -196,8 +198,8 @@ const Features = () => {
|
||||
src="/images/index/edge-dark-hover.jpg"
|
||||
alt="Supabase Edge Functions feature, hover image with glow"
|
||||
layout="fill"
|
||||
objectPosition="50% 50%"
|
||||
objectFit="cover"
|
||||
objectPosition={isSm ? 'bottom' : '50% 50%'}
|
||||
objectFit={isSm ? 'contain' : 'cover'}
|
||||
quality={100}
|
||||
/>
|
||||
</motion.div>
|
||||
@@ -205,8 +207,8 @@ const Features = () => {
|
||||
src="/images/index/edge-dark.jpg"
|
||||
alt="Supabase Edge Functions feature"
|
||||
layout="fill"
|
||||
objectPosition="50% 50%"
|
||||
objectFit="cover"
|
||||
objectPosition={isSm ? 'bottom' : '50% 50%'}
|
||||
objectFit={isSm ? 'contain' : 'cover'}
|
||||
quality={100}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,139 +1,62 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { Button, IconBookOpen } from 'ui'
|
||||
import SectionContainer from '../Layouts/SectionContainer'
|
||||
import styles from './hero.module.css'
|
||||
import { useWindowSize } from 'react-use'
|
||||
import BackedBy from '../BackedBy'
|
||||
import { useTheme } from 'common'
|
||||
import { motion } from 'framer-motion'
|
||||
import HeroBackground from './HeroBackground'
|
||||
|
||||
const Hero = () => {
|
||||
const divRef = useRef(null)
|
||||
const { isDarkMode } = useTheme()
|
||||
const { width } = useWindowSize()
|
||||
|
||||
useEffect(() => {
|
||||
const newHeight =
|
||||
width < 768 ? 100000 / width : width > 1800 ? 30000 / width : (70000 / width) * 2
|
||||
|
||||
if (divRef?.current) {
|
||||
;(divRef.current as HTMLDivElement).style.height = `${Math.round(newHeight)}px`
|
||||
console.log(width, newHeight)
|
||||
}
|
||||
}, [width])
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<SectionContainer className="md:py-16 lg:py-20">
|
||||
<div className="relative">
|
||||
<div className="mx-auto">
|
||||
<div className="mx-auto max-w-2xl lg:col-span-6 lg:flex lg:items-center justify-center text-center">
|
||||
<div className="lg:h-[50vh] lg:min-h-[300px] lg:max-h-[450px] flex flex-col items-center justify-center sm:mx-auto md:w-3/4 lg:mx-0 lg:w-full gap-4 lg:gap-8">
|
||||
<div>
|
||||
<motion.h1
|
||||
initial={{ y: 10, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 1, ease: [0, 0.13, 0.28, 1], delay: 0.2 }}
|
||||
className="
|
||||
const Hero = () => (
|
||||
<div className="relative">
|
||||
<SectionContainer className="md:py-16 lg:py-20">
|
||||
<div className="relative">
|
||||
<div className="mx-auto">
|
||||
<div className="mx-auto max-w-2xl lg:col-span-6 lg:flex lg:items-center justify-center text-center">
|
||||
<div className="lg:h-[50vh] lg:min-h-[300px] lg:max-h-[450px] flex flex-col items-center justify-center sm:mx-auto md:w-3/4 lg:mx-0 lg:w-full gap-4 lg:gap-8">
|
||||
<div>
|
||||
<motion.h1
|
||||
initial={{ y: 10, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 1, ease: [0.5, 0.01, 0.28, 1], delay: 0.2 }}
|
||||
className="
|
||||
text-scale-1200
|
||||
text-4xl sm:text-5xl sm:leading-none lg:text-6xl
|
||||
text-3xl sm:text-5xl sm:leading-none lg:text-6xl
|
||||
xl:text-7xl
|
||||
"
|
||||
>
|
||||
<span className="block">Build in a weekend.</span>
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-br from-[#3ECF8E] via-[#3ECF8E] to-[#3E9BCF] block md:ml-0">
|
||||
Scale to millions.
|
||||
</span>
|
||||
</motion.h1>
|
||||
<p className="py-2 text-scale-1200 !mb-0 mt-1.5 text-sm sm:mt-5 sm:text-base lg:text-lg">
|
||||
Supabase is an open source Firebase alternative for building secure and
|
||||
performant Postgres backends with minimal configuration.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Link href="https://app.supabase.com" as="https://app.supabase.com" passHref>
|
||||
<a>
|
||||
<Button size="small" className="text-white">
|
||||
Start your project
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href="/docs" as="/docs" passHref>
|
||||
<a>
|
||||
<Button size="small" type="default" icon={<IconBookOpen />}>
|
||||
Documentation
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<BackedBy className="hidden md:block" />
|
||||
>
|
||||
<span className="block">Build in a weekend.</span>
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-br from-[#3ECF8E] via-[#3ECF8E] to-[#3E9BCF] block md:ml-0">
|
||||
Scale to millions.
|
||||
</span>
|
||||
</motion.h1>
|
||||
<p className="py-2 text-scale-1200 !mb-0 mt-1.5 text-sm sm:mt-5 sm:text-base lg:text-lg">
|
||||
Supabase is an open source Firebase alternative for building secure and performant
|
||||
Postgres backends with minimal configuration.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Link href="https://app.supabase.com" as="https://app.supabase.com" passHref>
|
||||
<a>
|
||||
<Button size="small" className="text-white">
|
||||
Start your project
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href="/docs" as="/docs" passHref>
|
||||
<a>
|
||||
<Button size="small" type="default" icon={<IconBookOpen />}>
|
||||
Documentation
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<BackedBy className="hidden md:block" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SectionContainer>
|
||||
<div
|
||||
className={[
|
||||
'absolute z-[-4] flex flex-col top-0 left-0 w-screen h-screen overflow-hidden pointer-events-none',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="absolute w-screen h-screen">
|
||||
<div className={['w-full top-0', styles['shape-gradient']].join(' ')} />
|
||||
<div ref={divRef} className="w-full bg-scale-100" />
|
||||
<div className={['2xl:-mt-52'].join(' ')}>
|
||||
<svg
|
||||
className={['', styles['triangle-svg']].join(' ')}
|
||||
preserveAspectRatio="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 1680 915"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.16005e-05 -0.000998163L1680 0.129286V234L834.197 1068.2L-0.000248139 234.001L2.16005e-05 -0.000998163Z"
|
||||
fill={isDarkMode ? '#171717' : '#fbfcfd'}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={[
|
||||
'absolute top-0 left-0 w-full h-[600px] lg:h-[800px] overflow-hidden pointer-events-none',
|
||||
styles['hero-container'],
|
||||
].join(' ')}
|
||||
>
|
||||
<div className={['w-full h-full', styles['gradient']].join(' ')} />
|
||||
<div className="absolute bottom-0 z-[-2] w-full h-full bg-gradient-to-t from-scale-100 to-transparent" />
|
||||
<div
|
||||
className={[
|
||||
'relative -z-10 ![perspective:1200px] sm:![perspective:1200px] md:![perspective:1200px] lg:![perspective:1200px]',
|
||||
].join(' ')}
|
||||
>
|
||||
<div
|
||||
className="z-[100] absolute inset-0 [--gradient-stop-1:0px] [--gradient-stop-2:50%]"
|
||||
style={{
|
||||
background: 'linear-gradient(to top, rgba(0,0,0,0) 0px, #000000 50%)',
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
style={{
|
||||
transform: 'rotateX(85deg)',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
}}
|
||||
>
|
||||
<div className={[styles['hero-grid-lines']].join(' ')}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</SectionContainer>
|
||||
<HeroBackground />
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Hero
|
||||
@@ -0,0 +1,166 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import styles from './hero.module.css'
|
||||
import { useWindowSize } from 'react-use'
|
||||
import { useTheme } from 'common'
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
const HeroBackground = () => {
|
||||
const divRef = useRef(null)
|
||||
const { isDarkMode } = useTheme()
|
||||
const { width } = useWindowSize()
|
||||
|
||||
useEffect(() => {
|
||||
const newHeight =
|
||||
width < 768 ? 100000 / width : width > 1800 ? 30000 / width : (70000 / width) * 2
|
||||
|
||||
if (divRef?.current) {
|
||||
;(divRef.current as HTMLDivElement).style.height = `${Math.round(newHeight)}px`
|
||||
console.log(width, newHeight)
|
||||
}
|
||||
}, [width])
|
||||
|
||||
const svgTraceWidth = 1680
|
||||
const svgTraceHeight = 915
|
||||
const svgPath =
|
||||
// 'M2.16005e-05 -0.000998163L1680 0.129286V234L834.197 1068.2L-0.000248139 234.001L2.16005e-05 -0.000998163Z'
|
||||
'M2.16005e-05 -0.000998163L1680 0.129286V234L834.197 1068.2L-0.000248139 234.001L2.16005e-05 -0.000998163Z'
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={[
|
||||
'absolute z-[-4] flex flex-col top-0 left-0 w-screen h-screen overflow-hidden pointer-events-none',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="absolute w-screen h-screen">
|
||||
<div className={['w-full top-0 relative z-0', styles['shape-gradient']].join(' ')} />
|
||||
<div ref={divRef} className="relative w-full bg-scale-100" />
|
||||
<div className={['2xl:-mt-52'].join(' ')}>
|
||||
<svg
|
||||
className={['-mt-1 relative z-[-2]', styles['triangle-svg']].join(' ')}
|
||||
preserveAspectRatio="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 1680 1069"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d={svgPath}
|
||||
stroke="url(#electric-trace)"
|
||||
strokeLinecap="round"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<path
|
||||
d={svgPath}
|
||||
stroke="url(#electric-trace-right)"
|
||||
strokeLinecap="round"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<path d={svgPath} fill="var(--colors-scale1)" />
|
||||
<defs>
|
||||
{/*
|
||||
// Animated trace
|
||||
<motion.linearGradient
|
||||
animate={{
|
||||
x2: [
|
||||
-svgTraceWidth / 2,
|
||||
svgTraceWidth / 3,
|
||||
// (svgTraceWidth / 5) * 2,
|
||||
svgTraceWidth,
|
||||
svgTraceWidth,
|
||||
],
|
||||
x1: [0, svgTraceWidth / 2, svgTraceWidth * 1.5, svgTraceWidth * 1.5],
|
||||
y1: [
|
||||
svgTraceHeight / 2,
|
||||
svgTraceHeight / 2,
|
||||
svgTraceHeight / 2,
|
||||
svgTraceHeight / 2,
|
||||
],
|
||||
y2: [
|
||||
svgTraceHeight / 2,
|
||||
svgTraceHeight / 2,
|
||||
svgTraceHeight / 2,
|
||||
svgTraceHeight / 2,
|
||||
],
|
||||
}}
|
||||
transition={{
|
||||
duration: 6,
|
||||
repeat: Infinity,
|
||||
ease: 'linear',
|
||||
delay: 4,
|
||||
}}
|
||||
id="electric-trace"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#3ecf8e" stopOpacity="0" />
|
||||
<stop stopColor="#3ecf8e" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="#3E9BCF" stopOpacity="0" />
|
||||
</motion.linearGradient> */}
|
||||
<linearGradient
|
||||
x1={svgTraceWidth / 2 - 100}
|
||||
y1={svgTraceHeight / 2}
|
||||
x2={0}
|
||||
y2={svgTraceHeight / 2}
|
||||
id="electric-trace"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#3ecf8e" stopOpacity="0" />
|
||||
<stop stopColor="#3ecf8e" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="#3E9BCF" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1={svgTraceWidth + 100}
|
||||
y1={svgTraceHeight / 2}
|
||||
x2={svgTraceWidth / 2}
|
||||
y2={svgTraceHeight / 2}
|
||||
id="electric-trace-right"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#3ecf8e" stopOpacity="0" />
|
||||
<stop stopColor="#3ecf8e" stopOpacity="0.8" />
|
||||
<stop offset="1" stopColor="#3E9BCF" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={[
|
||||
'absolute top-0 left-0 w-full h-[600px] lg:h-[800px] overflow-hidden pointer-events-none',
|
||||
styles['hero-container'],
|
||||
].join(' ')}
|
||||
>
|
||||
<div className={['w-full h-full', styles['gradient']].join(' ')} />
|
||||
<div className="absolute bottom-0 z-[-2] w-full h-full bg-gradient-to-t from-scale-100 to-transparent" />
|
||||
<div
|
||||
className={[
|
||||
'relative -z-10 ![perspective:1200px] sm:![perspective:1200px] md:![perspective:1200px] lg:![perspective:1200px]',
|
||||
].join(' ')}
|
||||
>
|
||||
<div
|
||||
className="z-[100] absolute inset-0 [--gradient-stop-1:0px] [--gradient-stop-2:50%]"
|
||||
style={{
|
||||
background: 'linear-gradient(to top, rgba(0,0,0,0) 0px, #000000 50%)',
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
style={{
|
||||
transform: 'rotateX(85deg)',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
}}
|
||||
>
|
||||
<div className={[styles['hero-grid-lines']].join(' ')}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default HeroBackground
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 134 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 102 KiB |
Reference in new issue
Block a user