Skip to content

Commit 98b12c3

Browse files
committed
Improve schedule grid styles
1 parent 0202cd2 commit 98b12c3

File tree

2 files changed

+111
-89
lines changed

2 files changed

+111
-89
lines changed

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

+86-89
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import { SchedSpeaker } from "@/app/conf/2023/types"
88

99
import { Filters } from "./filters"
1010
import {
11-
ScheduleSession,
11+
type ScheduleSession,
1212
CategoryName,
1313
ConcurrentSessions,
1414
ScheduleSessionsByDay,
1515
} from "./session-list"
16+
import { PinIcon } from "../../pixelarticons/pin-icon"
17+
import { Tag } from "@/app/conf/_design-system/tag"
1618

1719
function isString(x: any) {
1820
return Object.prototype.toString.call(x) === "[object String]"
@@ -164,106 +166,33 @@ export function ScheduleList({
164166
([date, concurrentSessionsGroup], index) => (
165167
<div
166168
key={date}
167-
className="bg-neu-200 typography-body-sm dark:bg-neu-50"
169+
className="bg-neu-200 pt-px typography-body-sm dark:bg-neu-50"
168170
>
169171
<h3
170-
className="bg-neu-50 py-4 dark:bg-neu-0"
172+
className="bg-neu-50 py-4 dark:bg-neu-0 lg:mb-px"
171173
id={`day-${index + 1}`}
172174
>
173175
{format(parseISO(date), "EEEE, MMMM d")}
174176
</h3>
175177
{Object.entries(concurrentSessionsGroup).map(
176178
([sessionDate, sessions]) => (
177179
<div key={`concurrent sessions on ${sessionDate}`}>
178-
<div className="mb-px flex flex-col lg:mr-px lg:flex-row">
179-
<div className="relative bg-neu-50 dark:border-neu-50 dark:bg-neu-0 lg:border-r">
180-
<span className="inline-block w-20 whitespace-nowrap pb-4 typography-body-sm lg:mr-6 lg:mt-3 lg:w-28">
180+
<div className="mb-px mr-px flex flex-col max-lg:ml-px lg:flex-row">
181+
<div className="relative border-neu-50 bg-neu-50 dark:bg-neu-0 max-lg:-mx-px max-lg:border-x lg:mr-px">
182+
<span className="mt-3 inline-block w-20 whitespace-nowrap pb-0.5 pl-4 typography-body-sm lg:mr-6 lg:w-28 lg:pb-4 lg:pl-0">
181183
{format(parseISO(sessionDate), "hh:mmaaaa 'PDT'")}
182184
</span>
183185
</div>
184-
<div className="relative flex w-full flex-col items-end gap-px pl-[28px] lg:flex-row lg:items-start lg:pl-0">
185-
{sessions.map(session => {
186-
const eventType = session.event_type.endsWith("s")
187-
? session.event_type.slice(0, -1)
188-
: session.event_type
189-
190-
const speakers = session.speakers
191-
const formattedSpeakers = isString(speakers || [])
192-
? (speakers as string)?.split(",")
193-
: (speakers as SchedSpeaker[])?.map(e => e.name)
194-
const eventTitle = getEventTitle(
195-
// @ts-expect-error fixme
196-
session,
197-
formattedSpeakers,
198-
)
199-
200-
const eventColor = eventsColors[session.event_type]
201-
202-
return session.event_type === "Breaks" ? (
203-
<div
204-
key={session.id}
205-
className="flex size-full items-center bg-neu-0 px-4 py-2 font-normal"
206-
>
207-
{showEventType ? eventType + " / " : ""}
208-
{eventTitle}
209-
</div>
210-
) : (
211-
<a
212-
id={`session-${session.id}`}
213-
data-tooltip-id="my-tooltip"
214-
href={`/conf/${year}/schedule/${session.id}?name=${session.name}`}
215-
key={session.id}
216-
className="group relative size-full bg-neu-0 px-4 py-2 font-normal no-underline [&:hover_*]:!no-underline"
217-
>
218-
<span className="flex h-full flex-col justify-start gap-y-2 py-3">
219-
{eventColor && (
220-
<span
221-
className="relative mb-3 flex items-center justify-center self-start border px-2 py-1 font-mono text-xs/none uppercase"
222-
style={{
223-
borderColor: eventColor,
224-
}}
225-
>
226-
<span
227-
className="absolute inset-0 opacity-20"
228-
style={{
229-
backgroundColor: eventColor,
230-
}}
231-
/>
232-
<span className="relative">
233-
{eventType}
234-
</span>
235-
</span>
236-
)}
237-
<div className="flex h-full flex-col justify-between gap-y-2 group-hover:underline">
238-
{showEventType ? eventType + " / " : ""}
239-
{eventTitle}
240-
<div className="flex flex-col">
241-
{(speakers?.length || 0) > 0 && (
242-
<span className="font-light">
243-
{formattedSpeakers.join(", ")}
244-
</span>
245-
)}
246-
<span className="mt-2 flex items-center font-bold text-gray-700">
247-
<svg
248-
className="mb-0.5 mr-1"
249-
width="16px"
250-
height="16px"
251-
xmlns="http://www.w3.org/2000/svg"
252-
viewBox="0 0 384 512"
253-
>
254-
<path
255-
fill="rgb(55, 65, 81)"
256-
d="M215.7 499.2C267 435 384 279.4 384 192C384 86 298 0 192 0S0 86 0 192c0 87.4 117 243 168.3 307.2c12.3 15.3 35.1 15.3 47.4 0zM192 128a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"
257-
/>
258-
</svg>
259-
{session.venue}
260-
</span>
261-
</div>
262-
</div>
263-
</span>
264-
</a>
265-
)
266-
})}
186+
<div className="relative flex w-full flex-col items-end lg:flex-row lg:items-start lg:gap-px">
187+
{sessions.map(session => (
188+
<ScheduleSession
189+
key={session.id}
190+
session={session}
191+
showEventType={showEventType}
192+
year={year}
193+
eventsColors={eventsColors}
194+
/>
195+
))}
267196
</div>
268197
</div>
269198
</div>
@@ -277,3 +206,71 @@ export function ScheduleList({
277206
</>
278207
)
279208
}
209+
210+
function ScheduleSession({
211+
session,
212+
showEventType,
213+
year,
214+
eventsColors,
215+
}: {
216+
session: ScheduleSession
217+
showEventType: boolean | undefined
218+
year: "2025" | "2024"
219+
eventsColors: Record<string, string>
220+
}) {
221+
const eventType = session.event_type.endsWith("s")
222+
? session.event_type.slice(0, -1)
223+
: session.event_type
224+
225+
const speakers = session.speakers
226+
const formattedSpeakers = isString(speakers || [])
227+
? (speakers as string)?.split(",")
228+
: (speakers as SchedSpeaker[])?.map(e => e.name)
229+
230+
const eventTitle = getEventTitle(
231+
// @ts-expect-error fixme
232+
session,
233+
formattedSpeakers,
234+
)
235+
236+
const eventColor = eventsColors[session.event_type]
237+
238+
return session.event_type === "Breaks" ? (
239+
<div className="flex size-full items-center bg-neu-0 px-4 py-2 font-normal">
240+
{showEventType ? eventType + " / " : ""}
241+
{eventTitle}
242+
</div>
243+
) : (
244+
<a
245+
id={`session-${session.id}`}
246+
data-tooltip-id="my-tooltip"
247+
href={`/conf/${year}/schedule/${session.id}?name=${session.name}`}
248+
className="group relative size-full bg-neu-0 p-4 font-normal no-underline focus-visible:z-[1] max-lg:mt-px"
249+
>
250+
<span className="flex h-full flex-col justify-start">
251+
{eventColor && (
252+
<Tag className="mb-3" color={eventColor}>
253+
{eventType}
254+
</Tag>
255+
)}
256+
<span className="flex h-full flex-col justify-between gap-y-2">
257+
{showEventType ? eventType + " / " : ""}
258+
<span className="typography-body-md group-hover:underline">
259+
{eventTitle}
260+
</span>
261+
<span className="flex flex-col">
262+
{(speakers?.length || 0) > 0 && (
263+
<span className="typography-body-sm">
264+
{formattedSpeakers.join(", ")}
265+
</span>
266+
)}
267+
<span className="mt-2 flex items-center gap-0.5 typography-body-xs">
268+
<PinIcon className="size-4 text-pri-base" />
269+
{session.venue}
270+
</span>
271+
</span>
272+
</span>
273+
</span>
274+
</a>
275+
)
276+
}

src/app/conf/_design-system/tag.tsx

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import clsx from "clsx"
2+
3+
export interface TagProps extends React.HTMLAttributes<HTMLSpanElement> {
4+
color: string
5+
}
6+
export function Tag({ color, children, style, className, ...rest }: TagProps) {
7+
return (
8+
<span
9+
className={clsx(
10+
"relative flex items-center justify-center self-start border px-2 py-1 font-mono text-xs/none uppercase dark:opacity-[96.5%]",
11+
className,
12+
)}
13+
style={{ borderColor: color, ...style }}
14+
{...rest}
15+
>
16+
<span
17+
className="absolute inset-0 opacity-20"
18+
style={{
19+
backgroundColor: color,
20+
}}
21+
/>
22+
<span className="relative">{children}</span>
23+
</span>
24+
)
25+
}

0 commit comments

Comments
 (0)