Skip to content

Commit c80e8e7

Browse files
authored
New VibeCoders page (supabase#39373)
* set up * more sections * new section * ai logos * nits * format * nits * limit * rm postgrid date
1 parent e48e7fc commit c80e8e7

File tree

10 files changed

+952
-70
lines changed

10 files changed

+952
-70
lines changed

apps/www/components/Solutions/AIBuildersLogos.tsx

Lines changed: 169 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,187 @@
11
import Link from 'next/link'
2-
import React from 'react'
2+
import React, { useEffect } from 'react'
33
import { cn } from 'ui'
44

5+
import styles from './ai-builders-logos.module.css'
6+
57
const logos = [
6-
{
7-
image: `/images/logos/publicity/lovable.svg`,
8-
alt: 'lovable',
9-
name: 'lovable',
10-
href: 'https://lovable.dev/',
11-
},
12-
{
13-
image: `/images/logos/publicity/bolt.svg`,
14-
alt: 'bolt',
15-
name: 'bolt',
16-
href: 'https://bolt.new',
17-
},
18-
{
19-
image: `/images/logos/publicity/v0.svg`,
20-
alt: 'v0',
21-
name: 'v0',
22-
href: 'https://v0.dev',
23-
},
24-
{
25-
image: `/images/logos/publicity/figma.svg`,
26-
alt: 'figma',
27-
name: 'figma',
28-
href: 'https://www.figma.com/make/',
29-
},
30-
{
31-
image: `/images/logos/publicity/tempo.svg`,
32-
alt: 'tempo',
33-
name: 'tempo',
34-
href: 'https://tempo.new',
35-
},
36-
{
37-
image: `/images/logos/publicity/gumloop.svg`,
38-
alt: 'gumloop',
39-
name: 'gumloop',
40-
href: 'https://gumloop.com',
41-
},
42-
{
43-
image: `/images/logos/publicity/co-com.svg`,
44-
alt: 'co.com',
45-
name: 'co-com',
46-
href: 'https://co.dev',
47-
},
8+
[
9+
{
10+
image: `/images/logos/publicity/v0.svg`,
11+
alt: 'v0',
12+
name: 'v0',
13+
href: 'https://v0.dev',
14+
},
15+
{
16+
image: `/images/logos/publicity/lovable.svg`,
17+
alt: 'lovable',
18+
name: 'lovable',
19+
href: 'https://lovable.dev/',
20+
},
21+
{
22+
image: `/images/logos/publicity/bolt.svg`,
23+
alt: 'bolt',
24+
name: 'bolt',
25+
href: 'https://bolt.new',
26+
},
27+
],
28+
[
29+
{
30+
image: `/images/logos/publicity/figma.svg`,
31+
alt: 'figma',
32+
name: 'figma',
33+
href: 'https://www.figma.com/make/',
34+
},
35+
{
36+
image: `/images/logos/publicity/tempo.svg`,
37+
alt: 'tempo',
38+
name: 'tempo',
39+
href: 'https://tempo.new',
40+
},
41+
{
42+
image: `/images/logos/publicity/gumloop.svg`,
43+
alt: 'gumloop',
44+
name: 'gumloop',
45+
href: 'https://gumloop.com',
46+
},
47+
],
48+
// {
49+
// image: `/images/logos/publicity/co-com.svg`,
50+
// alt: 'co.com',
51+
// name: 'co-com',
52+
// href: 'https://co.dev',
53+
// },
4854
]
4955

5056
interface Props {
5157
className?: string
5258
}
5359

60+
const stagger = 0.1
61+
62+
// duration in ms
63+
const duration = 5000
64+
5465
const EnterpriseLogos: React.FC<Props> = ({ className }) => {
66+
const [index, setIndex] = React.useState(0)
67+
const [animate, setAnimate] = React.useState(false)
68+
69+
const currentLogos = logos[index].slice(0, 3)
70+
const logosNext = logos[(index + 1) % logos.length].slice(0, 3)
71+
72+
useEffect(() => {
73+
const id = setTimeout(() => {
74+
setAnimate(true)
75+
}, 500)
76+
77+
return () => {
78+
clearTimeout(id)
79+
}
80+
}, [])
81+
82+
useEffect(() => {
83+
if (!animate) {
84+
return
85+
}
86+
87+
function loop() {
88+
setIndex((index) => (index + 1) % logos.length)
89+
}
90+
91+
const interval = setInterval(loop, duration)
92+
93+
return () => {
94+
clearInterval(interval)
95+
}
96+
}, [animate])
97+
5598
return (
56-
<div
99+
<div className={cn('grid place-items-center w-full', className)}>
100+
<div
101+
key={`${index}-exit`}
102+
className="grid grid-cols-3 items-center text-center justify-center sm:flex gap-4 lg:gap-8"
103+
style={{
104+
gridArea: '1 / 1',
105+
}}
106+
>
107+
{currentLogos.map((logo, idx) => (
108+
<Logo
109+
key={`ent-logo-${logo.name}-${idx}`}
110+
logo={logo}
111+
state="exit"
112+
animate={animate}
113+
index={idx}
114+
stagger={stagger}
115+
/>
116+
))}
117+
</div>
118+
119+
{animate && (
120+
<div
121+
key={`${index}-enter`}
122+
className="items-center text-center justify-center flex-wrap flex gap-4 lg:gap-8"
123+
style={{
124+
gridArea: '1 / 1',
125+
}}
126+
>
127+
{logosNext.map((logo, idx) => (
128+
<Logo
129+
key={`ent-logo-${logo.name}-${idx}`}
130+
logo={logo}
131+
state="enter"
132+
animate={animate}
133+
index={idx}
134+
stagger={stagger}
135+
/>
136+
))}
137+
</div>
138+
)}
139+
</div>
140+
)
141+
}
142+
143+
const Logo = ({
144+
logo,
145+
state,
146+
animate,
147+
index,
148+
stagger,
149+
}: {
150+
logo: (typeof logos)[0][0]
151+
state: 'enter' | 'exit'
152+
animate: boolean
153+
index: number
154+
stagger: number
155+
}) => {
156+
return (
157+
<Link
158+
href={logo.href}
159+
target="_blank"
57160
className={cn(
58-
'flex lg:grid grid-cols-2 xl:flex flex-nowrap gap-4 md:gap-8 lg:gap-4 2xl:gap-4',
59-
className
161+
'h-8 lg:h-12 w-max mx-auto hover:opacity-100 opacity-80 transition-opacity',
162+
styles.logo
60163
)}
164+
data-state={state}
165+
data-animate={animate}
166+
style={
167+
{
168+
'--delay': `${index * stagger}s`,
169+
} as React.CSSProperties
170+
}
61171
>
62-
{logos.map((logo) => (
63-
<Link
64-
href={logo.href}
65-
target="_blank"
66-
key={`ent-logo-${logo.name}`}
67-
className="h-12 lg:h-12 w-max hover:opacity-100 opacity-80 transition-opacity"
68-
>
69-
<img
70-
src={logo.image}
71-
alt={logo.alt}
72-
className="
73-
w-auto block
74-
h-10 !min-h-10
75-
md:h-10 md:!min-h-10
76-
lg:h-7 lg:!min-h-7
77-
2xl:h-10 2xl:!min-h-10
78-
"
79-
draggable={false}
80-
/>
81-
</Link>
82-
))}
83-
</div>
172+
<img
173+
src={logo.image}
174+
alt={logo.alt}
175+
className="
176+
w-auto block
177+
h-10 !min-h-10
178+
md:h-10 md:!min-h-10
179+
lg:h-7 lg:!min-h-7
180+
2xl:h-10 2xl:!min-h-10
181+
"
182+
draggable={false}
183+
/>
184+
</Link>
84185
)
85186
}
86187

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { cn } from 'ui'
2+
import SectionContainer from '../Layouts/SectionContainer'
3+
import { getSortedPosts } from '~/lib/posts'
4+
import Image from 'next/image'
5+
import Link from 'next/link'
6+
7+
interface PostGridProps {
8+
id?: string
9+
className?: string
10+
11+
header: React.ReactNode
12+
subheader: React.ReactNode
13+
posts: ReturnType<typeof getSortedPosts>
14+
}
15+
16+
function PostGrid({ id, className, header, subheader, posts }: PostGridProps) {
17+
const hasPosts = posts.length > 0
18+
19+
return (
20+
<SectionContainer id={id} className={cn('flex flex-col gap-12 py-16 md:py-24', className)}>
21+
<div className="flex flex-col lg:max-w-[50%]">
22+
<h2 className="h2">{header}</h2>
23+
<p className="p">{subheader}</p>
24+
</div>
25+
26+
{hasPosts ? (
27+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
28+
{posts.map((post) => (
29+
<Link
30+
href={post.path}
31+
className="border rounded-md flex flex-col relative overflow-hidden"
32+
key={post.slug}
33+
>
34+
{post.thumb && (
35+
<div className="w-full aspect-video relative rounded-t-md dark:[mask-image:linear-gradient(to_bottom,_#000_0%,_#000_60%,_transparent_100%)]">
36+
<Image
37+
src={`/images/blog/${post.thumb}`}
38+
alt={post.title || ''}
39+
className="object-cover"
40+
fill
41+
/>
42+
</div>
43+
)}
44+
45+
<div className="p-3 mt-auto flex flex-col gap-1">
46+
<h3 className="p !mb-0 !text-foreground line-clamp-1">{post.title}</h3>
47+
<p className="text-sm !mb-0 !text-foreground-light inline-flex items-center gap-2">
48+
<span>{post.readingTime}</span>
49+
</p>
50+
</div>
51+
</Link>
52+
))}
53+
</div>
54+
) : (
55+
<p className="p">No posts found</p>
56+
)}
57+
</SectionContainer>
58+
)
59+
}
60+
61+
export default PostGrid
62+
63+
function formatDate(date: string) {
64+
return new Date(date).toLocaleDateString('en-US', {
65+
month: 'long',
66+
day: 'numeric',
67+
year: 'numeric',
68+
})
69+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
.logo {
2+
--duration: 500ms;
3+
--curve: ease;
4+
5+
&[data-animate='false'] {
6+
&[data-state='enter'] {
7+
opacity: 0;
8+
animation: none;
9+
}
10+
&[data-state='exit'] {
11+
opacity: 1;
12+
animation: none;
13+
}
14+
}
15+
16+
&[data-state='enter'][data-animate='true'] {
17+
animation: enter var(--duration) var(--curve) var(--delay) both;
18+
}
19+
20+
&[data-state='exit'][data-animate='true'] {
21+
animation: exit var(--duration) var(--curve) var(--delay) both;
22+
}
23+
}
24+
25+
@keyframes enter {
26+
0% {
27+
translate: 0 32px;
28+
filter: blur(4px);
29+
opacity: 0;
30+
}
31+
100% {
32+
translate: 0 0;
33+
filter: blur(0px);
34+
opacity: 1;
35+
}
36+
}
37+
38+
@keyframes exit {
39+
0% {
40+
translate: 0 0;
41+
filter: blur(0px);
42+
opacity: 1;
43+
}
44+
100% {
45+
translate: 0 -32px;
46+
filter: blur(4px);
47+
opacity: 0;
48+
}
49+
}

0 commit comments

Comments
 (0)