Skip to content

Commit aa8b8f8

Browse files
authored
new conf design — testimonials (#2003)
* Add location section * Move PixelArtIcons to _design-system dir * Move Accordion and icons to _design-system * Improve new location section * Get rid of dark blob, style dark mode * Add WebkitMaskImage and redundant remove console.log * Format * Add Testimonials section * Tint the pics pink * Fix the width * Add the separator back * Switch numbers in [What to Expect] section * Switch testimonials * Format
1 parent 0b2d07c commit aa8b8f8

File tree

6 files changed

+167
-24
lines changed

6 files changed

+167
-24
lines changed

src/app/conf/2025/components/cta-card-section/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export function CtaCardSection({
5555
}}
5656
>
5757
<StripesDecoration
58-
thin
58+
stripeWidth="5.2px"
5959
oddClassName="bg-[linear-gradient(180deg,var(--start)_0%,var(--end)_100%)]"
6060
/>
6161
</div>
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { StripesDecoration } from "@/app/conf/_design-system/stripes-decoration"
2+
import { clsx } from "clsx"
3+
import Image from "next-image-export-optimizer"
4+
5+
import maskBlur from "./mask.webp"
6+
7+
export interface TestimonialsProps extends React.HTMLAttributes<HTMLElement> {}
8+
9+
interface Testimonial {
10+
quote: string
11+
author: {
12+
name: string
13+
role: string
14+
avatar: string
15+
}
16+
}
17+
18+
const testimonials: Testimonial[] = [
19+
{
20+
quote:
21+
"GraphQL is evolving to new use cases every day and it's really a competitive advantage to experience them first hand with everyone that matters. I look forward the next edition!",
22+
author: {
23+
name: "Vincent Desmares",
24+
role: "Teamstarter, CTO",
25+
avatar:
26+
"https://avatars.sched.co/d/cc/21066875/avatar.jpg.320x320px.jpg?f80",
27+
},
28+
},
29+
{
30+
quote:
31+
"As a beginner in GraphQL, it was very helpful to see real use cases and honest accounts of the challenges along the way. I learned a lot about performance and security and had a great opportunity to network with other participants and potential vendors.",
32+
author: {
33+
name: "Nicolai Draslov",
34+
role: "Danish Agency for Digital Government",
35+
avatar:
36+
"https://media.licdn.com/dms/image/v2/C4E03AQGlrdt3GpJI9w/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1528203207471?e=1753920000&v=beta&t=H6CMhDZFoXJxGUu4XYwC_rEX9Jjwh7OdPIDm8JaeXAU",
37+
},
38+
},
39+
{
40+
quote:
41+
"GraphQLConf 24 was well organized event which empowers new and existing organizations to adopt GraphQL and help navigate how to rollout within their organizations by building understanding of ecosystem.",
42+
author: {
43+
name: "Satish Chitnis",
44+
role: "Paramount, Principal Architect",
45+
avatar:
46+
"https://avatars.sched.co/1/c3/21496512/avatar.jpg.320x320px.jpg?0c2",
47+
},
48+
},
49+
]
50+
51+
export function Testimonials({ className, ...rest }: TestimonialsProps) {
52+
return (
53+
<section
54+
className={clsx(
55+
"gql-conf-container py-8 max-md:px-4 md:pb-16 md:pt-24 md:[mask-image:linear-gradient(to_right,transparent,black_5%,black_95%,transparent)]",
56+
className,
57+
)}
58+
{...rest}
59+
>
60+
<h2 className="text-center text-neu-800 typography-h2">
61+
How was the previous edition?
62+
</h2>
63+
<div className="flex w-full snap-x snap-mandatory flex-row gap-10 overflow-x-auto px-4 py-6 lg:mt-16 lg:py-16">
64+
{testimonials.map((testimonial, i) => (
65+
<div
66+
key={i}
67+
className="flex shrink-0 snap-start flex-row-reverse items-center gap-6 max-md:flex-col md:px-10"
68+
>
69+
<div>
70+
<p className="max-w-[calc(100vw-32px)] !leading-[1.1] typography-body-lg max-md:text-center md:max-w-[544px]">
71+
{testimonial.quote}
72+
</p>
73+
<AuthorNameAndRole
74+
author={testimonial.author}
75+
className="mt-4 max-md:hidden"
76+
/>
77+
</div>
78+
<TestimonialAuthor author={testimonial.author} />
79+
</div>
80+
))}
81+
</div>
82+
</section>
83+
)
84+
}
85+
86+
function TestimonialAuthor({ author }: { author: Testimonial["author"] }) {
87+
return (
88+
<div className="relative flex shrink-0 flex-col items-center justify-center whitespace-pre md:px-6 lg:h-full lg:px-8">
89+
<div className="relative bg-neu-500 dark:bg-neu-200 dark:opacity-90">
90+
<Image
91+
src={author.avatar}
92+
alt={author.name}
93+
width={128}
94+
height={128}
95+
className="size-16 saturate-0 xl:size-32"
96+
/>
97+
<div className="absolute inset-0 z-[1] bg-pri-darker mix-blend-plus-lighter" />
98+
<Stripes />
99+
</div>
100+
<AuthorNameAndRole author={author} className="contents md:hidden" />
101+
<div
102+
// the separator
103+
className="absolute inset-y-0 right-0 w-px bg-gradient-to-b from-transparent via-pri-lighter to-transparent max-md:hidden"
104+
/>
105+
</div>
106+
)
107+
}
108+
109+
function AuthorNameAndRole({
110+
author,
111+
className,
112+
}: {
113+
author: Testimonial["author"]
114+
className?: string
115+
}) {
116+
return (
117+
<div className={className}>
118+
<div className="mt-3 typography-body-sm">{author.name}</div>
119+
<div className="text-neu-700 typography-body-xs">{author.role}</div>
120+
</div>
121+
)
122+
}
123+
124+
function Stripes() {
125+
const mask = `url(${maskBlur.src})`
126+
return (
127+
<div
128+
role="presentation"
129+
className="pointer-events-none absolute inset-0"
130+
style={{
131+
maskImage: mask,
132+
WebkitMaskImage: mask,
133+
maskSize: "cover",
134+
WebkitMaskSize: "cover",
135+
maskPosition: "left",
136+
WebkitMaskPosition: "left",
137+
}}
138+
>
139+
<StripesDecoration
140+
evenClassName="bg-gradient-to-b from-pri-light/0 to-pri-lighter/25"
141+
stripeWidth="8px"
142+
/>
143+
</div>
144+
)
145+
}
Binary file not shown.

src/app/conf/2025/components/what-to-expect.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ export default function WhatToExpectSection({
1414
>
1515
<h3 className="typography-h2 md:flex-1">What to expect</h3>
1616
<ul className="flex flex-col gap-6 uppercase md:flex-1">
17-
<ListItem number="3" text="days" />
18-
<ListItem number="23" text="speakers" />
19-
<ListItem number="36" text="panels & workshops" />
17+
<ListItem number="75+" text="talks" />
18+
<ListItem number="42" text="sessions" />
19+
<ListItem number="7" text="workshops" />
2020
<ListItem number="1" text="unique venue" />
2121
</ul>
2222
</section>

src/app/conf/2025/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { CtaCardSection } from "./components/cta-card-section"
1616
import { Button } from "../_design-system/button"
1717
import { GET_TICKETS_LINK } from "./links"
1818
import { GalleryStrip } from "./components/gallery-strip"
19+
import { Testimonials } from "./components/testimonials"
1920

2021
export const metadata: Metadata = {
2122
title: "GraphQLConf 2025 — Sept 08-10",
@@ -78,6 +79,7 @@ export default function Page() {
7879
/>
7980
<Venue />
8081
<GalleryStrip />
82+
<Testimonials />
8183
<GraphQLFoundationCard />
8284
<FAQ />
8385
<CtaCardSection

src/app/conf/_design-system/stripes-decoration.tsx

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,39 @@
11
import clsx from "clsx"
22

3-
const maskEvenWide =
4-
"repeating-linear-gradient(to right, transparent, transparent 12px, black 12px, black 24px)"
3+
const maskEven =
4+
"repeating-linear-gradient(to right, transparent, transparent var(--stripe-width), black var(--stripe-width), black calc(var(--stripe-width) * 2))"
55

6-
const maskOddWide =
7-
"repeating-linear-gradient(to right, black, black 12px, transparent 12px, transparent 24px)"
8-
9-
const maskEvenThin =
10-
"repeating-linear-gradient(to right, transparent, transparent 5.2px, black 5.2px, black 10.4px)"
11-
12-
const maskOddThin =
13-
"repeating-linear-gradient(to right, black, black 5.2px, transparent 5.2px, transparent 10.4px)"
6+
const maskOdd =
7+
"repeating-linear-gradient(to right, black, black var(--stripe-width), transparent var(--stripe-width), transparent calc(var(--stripe-width) * 2))"
148

159
export interface StripesDecorationProps {
1610
evenClassName?: string
1711
oddClassName?: string
18-
thin?: boolean
12+
stripeWidth?: string
1913
}
2014

21-
export function StripesDecoration(props: StripesDecorationProps) {
22-
const [maskEven, maskOdd] = props.thin
23-
? [maskEvenThin, maskOddThin]
24-
: [maskEvenWide, maskOddWide]
25-
15+
export function StripesDecoration({
16+
stripeWidth = "12px",
17+
evenClassName,
18+
oddClassName,
19+
}: StripesDecorationProps) {
2620
return (
2721
<>
28-
{props.evenClassName && (
22+
{evenClassName && (
2923
<div
30-
className={clsx("absolute inset-0", props.evenClassName)}
24+
className={clsx("absolute inset-0", evenClassName)}
3125
style={{
26+
...({ "--stripe-width": stripeWidth } as React.CSSProperties),
3227
maskImage: maskEven,
3328
WebkitMaskImage: maskEven,
3429
}}
3530
/>
3631
)}
37-
{props.oddClassName && (
32+
{oddClassName && (
3833
<div
39-
className={clsx("absolute inset-0", props.oddClassName)}
34+
className={clsx("absolute inset-0", oddClassName)}
4035
style={{
36+
...({ "--stripe-width": stripeWidth } as React.CSSProperties),
4137
maskImage: maskOdd,
4238
WebkitMaskImage: maskOdd,
4339
}}

0 commit comments

Comments
 (0)