Skip to content

Commit 89590e7

Browse files
committed
Improve styles
1 parent cce8bf4 commit 89590e7

File tree

10 files changed

+159
-78
lines changed

10 files changed

+159
-78
lines changed

src/app/conf/2025/components/speaker-card.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function SpeakerCard({
2929
return (
3030
<article
3131
className={clsx(
32-
"relative flex flex-col overflow-hidden border border-neu-300 [container-type:inline-size]",
32+
"relative flex flex-col overflow-hidden border border-neu-200 [container-type:inline-size] dark:border-neu-100",
3333
className,
3434
)}
3535
{...props}
@@ -100,7 +100,7 @@ function SpeakerLinks({
100100
return (
101101
<div
102102
className={clsx(
103-
"z-[3] flex divide-x divide-neu-200 border border-neu-200",
103+
"z-[3] flex divide-x divide-neu-200 border border-neu-200 dark:border-neu-100",
104104
className,
105105
)}
106106
>

src/app/conf/2025/page.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,12 @@ import { Button } from "../_design-system/button"
1717
import { GET_TICKETS_LINK } from "./links"
1818
import { GalleryStrip } from "./components/gallery-strip"
1919
import { HeroImage } from "./components/hero/hero-image"
20+
import { HERO_MARQUEE_ITEMS } from "./utils"
2021

2122
export const metadata: Metadata = {
2223
title: "GraphQLConf 2025 — Sept 08-10",
2324
}
2425

25-
const HERO_MARQUEE_ITEMS = [
26-
["COMMUNITY", "DEVELOPER EXPERIENCE", "APIs", "TOOLS & LIBRARIES"],
27-
["OPEN SOURCE", "FEDERATION", "ECOSYSTEMS", "TRACING & OBSERVABILITY"],
28-
["BEST PRACTICES", "WORKSHOPS", "SCHEMAS", "SECURITY"],
29-
]
30-
3126
export default function Page() {
3227
return (
3328
<main className="gql-all-anchors-focusable">
Lines changed: 6 additions & 0 deletions
Loading

src/app/conf/2025/schedule/[id]/page.tsx

Lines changed: 107 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,21 @@ import { metadata as layoutMetadata } from "@/app/conf/2023/layout"
88
import { speakers, schedule } from "../../_data"
99
import { ScheduleSession } from "../../../2023/types"
1010

11-
import { SessionVideo } from "./session-video"
11+
import ExternalLinkIcon from "@/app/conf/2025/pixelarticons/external-link.svg?svgr"
12+
13+
import { findVideo, SessionVideo } from "./session-video"
1214
import { NavbarPlaceholder } from "../../components/navbar"
1315
import { BackLink } from "../_components/back-link"
1416
import { Tag } from "@/app/conf/_design-system/tag"
15-
import { eventsColors, getEventTitle } from "../../utils"
17+
import { eventsColors, getEventTitle, HERO_MARQUEE_ITEMS } from "../../utils"
1618
import { PinIcon } from "../../pixelarticons/pin-icon"
1719
import { CalendarIcon } from "../../pixelarticons/calendar-icon"
1820
import { SpeakerCard } from "../../components/speaker-card"
1921
import { Anchor } from "@/app/conf/_design-system/anchor"
22+
import { MarqueeRows } from "../../components/marquee-rows"
23+
import { GET_TICKETS_LINK } from "../../links"
24+
import { CtaCardSection } from "../../components/cta-card-section"
25+
import { Button } from "@/app/conf/_design-system/button"
2026

2127
type SessionProps = { params: { id: string } }
2228

@@ -60,48 +66,87 @@ export default function SessionPage({ params }: SessionProps) {
6066
event.speakers!.map(s => s.name),
6167
)
6268

69+
const video = findVideo(event, eventTitle)
70+
6371
return (
64-
<main className="gql-all-anchors-focusable">
65-
<NavbarPlaceholder className="top-0 bg-neu-0 before:bg-white/40 dark:bg-neu-0 dark:before:bg-blk/30" />
66-
<div className="gql-conf-container gql-conf-navbar-strip text-neu-900 before:bg-white/40 before:dark:bg-blk/30">
67-
<div className="gql-conf-section">
68-
<div className="mx-auto max-w-[1088px] py-10">
69-
<section className="mx-auto min-h-[80vh] flex-col justify-center px-2 sm:px-0 lg:justify-between">
70-
<SessionHeader
71-
event={event}
72-
eventTitle={eventTitle}
73-
year="2025"
74-
/>
75-
<SessionVideo event={event} eventTitle={eventTitle} />
76-
77-
<div className="mt-8 flex gap-4 max-lg:flex-col lg:mt-16 lg:gap-8">
78-
<h3 className="typography-h2 min-w-[320px]">
79-
Session description
72+
<>
73+
<NavbarPlaceholder className="top-0 bg-neu-50 before:bg-white/40 dark:bg-neu-0 dark:before:bg-blk/30" />
74+
75+
<main className="gql-all-anchors-focusable gql-conf-navbar-strip text-neu-900 before:bg-white/40 before:dark:bg-blk/30">
76+
<div className="gql-conf-container">
77+
<div className="gql-conf-section !pt-0">
78+
<div className="border-x border-neu-200 pt-8 dark:border-neu-100 2xl:pt-16">
79+
<section className="mx-auto min-h-[80vh] flex-col justify-center px-2 sm:px-0 lg:justify-between">
80+
<SessionHeader
81+
event={event}
82+
eventTitle={eventTitle}
83+
year="2025"
84+
className={clsx(
85+
"px-2 sm:px-3",
86+
video && "mx-auto max-w-[1088px]",
87+
)}
88+
/>
89+
{video ? (
90+
<SessionVideo video={video} className="mt-6" />
91+
) : (
92+
<Hr className="mt-10 2xl:mt-16" />
93+
)}
94+
95+
<div className="mt-8 flex gap-4 px-2 max-lg:flex-col sm:px-3 lg:mt-16 lg:gap-8 xl:pb-16">
96+
<h3 className="typography-h2 min-w-[320px]">
97+
Session description
98+
</h3>
99+
<p className="typography-body-lg">{event.description}</p>
100+
</div>
101+
102+
<Hr />
103+
104+
<h3 className="typography-h2 my-8 max-w-[408px] px-2 sm:px-3 lg:my-16">
105+
Session speakers
80106
</h3>
81-
<p className="typography-body-lg">{event.description}</p>
82-
</div>
83-
84-
<h3 className="typography-h2 my-8 max-w-[408px] lg:mb-16">
85-
Session speakers
86-
</h3>
87-
<SessionSpeakers event={event} />
88-
89-
<div className="py-8">
90-
{event.files?.map(({ path }) => (
91-
<div key={path}>
92-
<a href={path} target="_blank" rel="noreferrer">
93-
View Full PDF{" "}
94-
<span className="font-sans text-2xl font-light"></span>
95-
</a>
96-
<iframe src={path} className="aspect-video size-full" />
97-
</div>
98-
))}
99-
</div>
100-
</section>
107+
<SessionSpeakers event={event} className="-mx-px" />
108+
109+
<div className="py-8 xl:mt-16">
110+
{event.files?.map(({ path }) => (
111+
<div key={path} className="flex flex-col">
112+
<Button
113+
variant="tertiary"
114+
href={path}
115+
target="_blank"
116+
rel="noreferrer"
117+
className={clsx(
118+
"m-1 h-fit items-center gap-x-2 self-end bg-neu-100 !p-2 text-neu-700 transition-opacity hover:bg-neu-200/80 hover:text-neu-900 disabled:opacity-0",
119+
)}
120+
>
121+
View full PDF
122+
<ExternalLinkIcon className="size-4" />
123+
</Button>
124+
<iframe src={path} className="aspect-video size-full" />
125+
</div>
126+
))}
127+
</div>
128+
</section>
129+
</div>
101130
</div>
102131
</div>
103-
</div>
104-
</main>
132+
133+
<div className="bg-neu-0 py-8 xl:py-16">
134+
<div className="gql-conf-container">
135+
<CtaCardSection
136+
title="Get your ticket"
137+
description="Join three transformative days of expert insights and innovation to shape the next decade of APIs!"
138+
>
139+
<Button variant="primary" href={GET_TICKETS_LINK}>
140+
Get tickets
141+
</Button>
142+
</CtaCardSection>
143+
<div className="py-8">
144+
<MarqueeRows variant="secondary" items={HERO_MARQUEE_ITEMS} />
145+
</div>
146+
</div>
147+
</div>
148+
</main>
149+
</>
105150
)
106151
}
107152

@@ -139,15 +184,17 @@ function SessionHeader({
139184
event,
140185
eventTitle,
141186
year,
187+
className,
142188
}: {
143189
event: ScheduleSession
144190
eventTitle: string | null
145191
year: number | `${number}`
192+
className?: string
146193
}) {
147194
const speakers = event.speakers || []
148195

149196
return (
150-
<header>
197+
<header className={className}>
151198
<BackLink year="2025" kind="schedule" />
152199
<p
153200
className={clsx(
@@ -187,12 +234,29 @@ function SessionHeader({
187234
)
188235
}
189236

190-
function SessionSpeakers({ event }: { event: ScheduleSession }) {
237+
function SessionSpeakers({
238+
event,
239+
className,
240+
}: {
241+
event: ScheduleSession
242+
className?: string
243+
}) {
191244
return (
192-
<div className="grid gap-5 lg:grid-cols-2">
245+
<div className={clsx("grid gap-5 lg:grid-cols-2", className)}>
193246
{event.speakers?.map(speaker => (
194247
<SpeakerCard key={speaker.username} speaker={speaker} year="2025" />
195248
))}
196249
</div>
197250
)
198251
}
252+
253+
function Hr({ className }: { className?: string }) {
254+
return (
255+
<hr
256+
className={clsx(
257+
"ml-[-50vw] w-[200vw] border-neu-200 dark:border-neu-100",
258+
className,
259+
)}
260+
/>
261+
)
262+
}
Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
1+
import clsx from "clsx"
12
import { findBestMatch } from "string-similarity"
2-
import { videos } from "../../_videos"
3+
34
import { ScheduleSession } from "@/app/conf/2023/types"
45

6+
import { videos } from "../../_videos"
7+
58
export interface SessionVideoProps {
6-
eventTitle: string
7-
event: ScheduleSession
9+
video: {
10+
id: string
11+
title: string
12+
}
13+
className?: string
814
}
915

10-
export function SessionVideo({ eventTitle, event }: SessionVideoProps) {
16+
export function SessionVideo({ video, className }: SessionVideoProps) {
17+
return (
18+
<iframe
19+
className={clsx("mx-auto aspect-video w-full", className)}
20+
src={`https://youtube.com/embed/${video.id}`}
21+
title={video.title}
22+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
23+
allowFullScreen
24+
/>
25+
)
26+
}
27+
28+
export function findVideo(event: ScheduleSession, eventTitle: string) {
1129
const result = findBestMatch(
1230
`${eventTitle} ${event.speakers!.map(e => e.name).join(" ")}`,
1331
videos.map(e => e.title),
@@ -19,19 +37,11 @@ export function SessionVideo({ eventTitle, event }: SessionVideoProps) {
1937

2038
const recordingTitle = result.bestMatch
2139

22-
const videoId = videos.find(e => e.title === recordingTitle.target)?.id
40+
const video = videos.find(e => e.title === recordingTitle.target)
2341

24-
if (!videoId) {
42+
if (!video) {
2543
throw new Error(`Video "${recordingTitle.target}" not found`)
2644
}
2745

28-
return (
29-
<iframe
30-
className="mx-auto mt-6 aspect-video w-full"
31-
src={`https://youtube.com/embed/${videoId}`}
32-
title={recordingTitle.target}
33-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
34-
allowFullScreen
35-
/>
36-
)
46+
return video
3747
}

src/app/conf/2025/schedule/_components/filters.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function FiltersCombobox({
9999
{label}
100100
</Combobox.Label>
101101
)}
102-
<label className="relative w-full border border-neu-500 bg-neu-0 p-2 focus-within:outline-none focus-within:ring focus-within:ring-neu-300 dark:focus-within:ring-neu-200">
102+
<label className="relative w-full border border-neu-500 bg-neu-0 p-2 focus-within:outline-none focus-within:ring focus-within:ring-neu-300 dark:border-neu-200 dark:focus-within:ring-neu-200">
103103
<Combobox.Input
104104
value={query}
105105
onChange={e => setQuery(e.target.value)}

src/app/conf/2025/schedule/_components/schedule-list.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,13 @@ export function ScheduleList({
171171
{Object.entries(concurrentSessionsGroup).map(
172172
([sessionDate, sessions]) => (
173173
<div key={`concurrent sessions on ${sessionDate}`}>
174-
<div className="mb-px mr-px flex flex-col max-lg:ml-px lg:flex-row">
174+
<div className="mr-px flex flex-col max-lg:ml-px lg:mb-px lg:flex-row">
175175
<div className="relative border-neu-50 bg-neu-50 dark:bg-neu-0 max-lg:-mx-px max-lg:mt-px max-lg:border-x lg:mr-px">
176176
<span className="typography-body-sm mt-3 inline-block w-20 whitespace-nowrap pb-0.5 pl-4 lg:mr-6 lg:w-28 lg:pb-4 lg:pl-0">
177177
{format(parseISO(sessionDate), "hh:mmaaaa 'PDT'")}
178178
</span>
179179
</div>
180-
<div className="relative flex w-full flex-col items-end lg:flex-row lg:items-start lg:gap-px">
180+
<div className="relative flex w-full flex-col items-end gap-px lg:flex-row lg:items-start">
181181
{sessions.map(session => (
182182
<ScheduleSessionCard
183183
key={session.id}

src/app/conf/2025/schedule/_components/schedule-session-card.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,13 @@ export function ScheduleSessionCard({
4949
{eventTitle}
5050
</div>
5151
) : (
52-
<a
53-
id={`session-${session.id}`}
54-
data-tooltip-id="my-tooltip"
55-
href={`/conf/${year}/schedule/${session.id}?name=${session.name}`}
56-
className="group relative size-full bg-neu-0 p-4 font-normal no-underline ring-neu-400 hover:bg-neu-0/90 hover:ring-1 focus-visible:z-[1] dark:ring-neu-100 dark:hover:bg-neu-0/80 max-lg:mt-px"
57-
>
58-
{/* todo: fix link nesting */}
52+
<div className="group relative size-full bg-neu-0 p-4 font-normal no-underline ring-neu-400 hover:bg-neu-0/90 hover:ring-1 focus-visible:z-[1] dark:ring-neu-100 dark:hover:bg-neu-0/80">
53+
<Anchor
54+
id={`session-${session.id}`}
55+
href={`/conf/${year}/schedule/${session.id}?name=${session.name}`}
56+
className="absolute inset-0 z-[1] ring-inset ring-neu-400 hover:ring-1 dark:ring-neu-100"
57+
aria-label={`Read more about "${eventTitle}" by ${speakers.map(s => s.name).join(", ")}`}
58+
/>
5959
<span className="flex h-full flex-col justify-start">
6060
{eventColor && (
6161
<Tag className="mb-3" color={eventColor}>
@@ -92,6 +92,6 @@ export function ScheduleSessionCard({
9292
</span>
9393
</span>
9494
</span>
95-
</a>
95+
</div>
9696
)
9797
}

src/app/conf/2025/schedule/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default function SchedulePage() {
2525
</Button>
2626
</div>
2727
</Hero>
28-
<div className="gql-conf-container gql-conf-section">
28+
<div className="gql-conf-container gql-conf-section 2xl:!px-24">
2929
<ScheduleList
3030
filterCategories={filterCategories2024}
3131
eventsColors={eventsColors}

src/app/conf/2025/utils.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,9 @@ export function getEventTitle(
3939

4040
return name
4141
}
42+
43+
export const HERO_MARQUEE_ITEMS = [
44+
["COMMUNITY", "DEVELOPER EXPERIENCE", "APIs", "TOOLS & LIBRARIES"],
45+
["OPEN SOURCE", "FEDERATION", "ECOSYSTEMS", "TRACING & OBSERVABILITY"],
46+
["BEST PRACTICES", "WORKSHOPS", "SCHEMAS", "SECURITY"],
47+
]

0 commit comments

Comments
 (0)