Skip to content

Commit eee85f2

Browse files
authored
add Read more / Show less to Amsterdam agenda (#2402)
## Description Also fixed the bug where we didn't display the side speaker card when there were less than 2 paragraphs and got rid of some redundant white space on smaller screens. <img width="1323" height="421" alt="image" src="https://github.com/user-attachments/assets/74db3fa7-cb00-40f7-8c68-d8db49738004" />
1 parent e43016e commit eee85f2

2 files changed

Lines changed: 38 additions & 12 deletions

File tree

src/app/day/2026/amsterdam/schedule-data.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export const amsterdamSessions: AmsterdamSession[] = [
5555
end: "2026-06-10T09:55:00+02:00",
5656
tags: ["GraphQL", "REST"],
5757
description:
58-
'<p>Living in the GraphQL bubble for the last few years, I\'ve watched the ecosystem grow up in a way that\'s hard to appreciate from the outside. The spec, the tooling, the vendors, the federation story, all visibly stronger than just two years ago. GraphQL was never bad, It was misunderstood, overhyped and overused. Fast forward to today, the dust has settled. Enterprises are on the slope of enlightenment, yet the people who pick the query language still have to handle pushback: ""GraphQL breaks caching!,"" ""it has the N+1 problem,"" ""OpenAPI is much simpler."" Almost all of that pushback is grounded in views that were already outdated when first written down. So I traced 18 of the most repeated GraphQL vs REST claims back to their primary sources: papers, RFCs, doc pages, security reports. Only three survived cleanly. This talk sends you home able to articulate GraphQL\'s real strengths, and its honest trade-offs, with receipts. We\'ll close on where GraphQL\'s value is growing fastest: as an abstraction layer for LLMs and agents, where a single typed graph is a far simpler surface to integrate against than hundreds of REST API endpoints.</p>\n',
58+
'<p>Living in the GraphQL bubble for the last few years, I\'ve watched the ecosystem grow up in a way that\'s hard to appreciate from the outside. The spec, the tooling, the vendors, the federation story, all visibly stronger than just two years ago. GraphQL was never bad, It was misunderstood, overhyped and overused. Fast forward to today, the dust has settled. Enterprises are on the slope of enlightenment, yet the people who pick the query language still have to handle pushback: ""GraphQL breaks caching!,"" ""it has the N+1 problem,"" ""OpenAPI is much simpler."" Almost all of that pushback is grounded in views that were already outdated when first written down.</p>\n<p>I traced 18 of the most repeated GraphQL vs REST claims back to their primary sources: papers, RFCs, doc pages, security reports. Only three survived cleanly. This talk sends you home able to articulate GraphQL\'s real strengths, and its honest trade-offs, with receipts. We\'ll close on where GraphQL\'s value is growing fastest: as an abstraction layer for LLMs and agents, where a single typed graph is a far simpler surface to integrate against than hundreds of REST API endpoints.</p>\n',
5959
venue: "TBA",
6060
speakers: [
6161
{
@@ -107,7 +107,7 @@ export const amsterdamSessions: AmsterdamSession[] = [
107107
end: "2026-06-10T10:20:00+02:00",
108108
tags: ["GraphQL", "MCP", "AI Agents"],
109109
description:
110-
"<p>As AI assistants and MCP-style tools increasingly sit in front of GraphQL APIs, embeddings have become critical for fuzzy schema search, field retrieval, and natural-language-to-query systems. Yet most teams rely on general-purpose embedding models that were not specifically designed to understand GraphQL type systems, relationships, or naming patterns. This talk shares practical experience building schema-aware embedding pipelines with off-the-shelf and fine-tuned models while exploring how far preprocessing, chunking, and schema structuring can take you before custom training is needed. We’ll discuss evaluation methods, common failure modes like field confusion and hallucinated types, and the tradeoffs between large hosted models and compact, GraphQL-focused embeddings that can run with lightweight CPU inference. The goal is to give GraphQL platform teams concrete, production-ready guidelines for choosing, adapting, and shipping embeddings that actually understand their schemas.</p>\n",
110+
"<p>As AI assistants and MCP-style tools increasingly sit in front of GraphQL APIs, embeddings have become critical for fuzzy schema search, field retrieval, and natural-language-to-query systems. Yet most teams rely on general-purpose embedding models that were not specifically designed to understand GraphQL type systems, relationships, or naming patterns.</p>\n<p>This talk shares practical experience building schema-aware embedding pipelines with off-the-shelf and fine-tuned models while exploring how far preprocessing, chunking, and schema structuring can take you before custom training is needed. We’ll discuss evaluation methods, common failure modes like field confusion and hallucinated types, and the tradeoffs between large hosted models and compact, GraphQL-focused embeddings that can run with lightweight CPU inference. The goal is to give GraphQL platform teams concrete, production-ready guidelines for choosing, adapting, and shipping embeddings that actually understand their schemas.</p>\n",
111111
venue: "TBA",
112112
speakers: [
113113
{

src/app/day/2026/amsterdam/schedule-section.tsx

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
"use client"
2+
3+
import { useState } from "react"
14
import Image from "next/image"
25
import clsx from "clsx"
36

@@ -10,6 +13,7 @@ import {
1013
SocialIconType,
1114
} from "@/app/conf/_design-system/social-icon"
1215
import { formatDescription } from "@/app/conf/2026/schedule/[id]/format-description"
16+
import ArrowDownIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr"
1317

1418
import {
1519
AmsterdamSession,
@@ -81,7 +85,7 @@ function SessionBlock({
8185
<SessionHeader session={session} className="px-2 py-8 sm:px-3 lg:py-12" />
8286
{session.description && (
8387
<>
84-
<Hr className="mt-10 xl:mt-0 2xl:mt-16" />
88+
<Hr className="mt-0 lg:mt-10 xl:mt-0 2xl:mt-16" />
8589
<SessionDescription
8690
description={session.description}
8791
sideSpeaker={sideSpeaker}
@@ -108,24 +112,46 @@ function SessionDescription({
108112
description: string
109113
sideSpeaker: AmsterdamSpeaker | null
110114
}) {
115+
const [expanded, setExpanded] = useState(false)
111116
const paragraphs = parseParagraphs(description)
112-
const splitAt =
113-
sideSpeaker && paragraphs.length >= 2
114-
? paragraphs.length - 2
115-
: paragraphs.length
116-
const lead = paragraphs.slice(0, splitAt)
117-
const tail = paragraphs.slice(splitAt)
117+
const hasMore = paragraphs.length > 1
118+
const visible = expanded ? paragraphs : paragraphs.slice(0, 1)
119+
const splitAt = sideSpeaker ? Math.max(0, visible.length - 2) : visible.length
120+
const lead = visible.slice(0, splitAt)
121+
const tail = visible.slice(splitAt)
122+
const lastInLead = tail.length === 0 ? lead.length - 1 : -1
123+
const lastInTail = tail.length - 1
124+
125+
const toggle = hasMore && (
126+
<>
127+
{" "}
128+
<button
129+
type="button"
130+
onClick={() => setExpanded(e => !e)}
131+
aria-expanded={expanded}
132+
className="typography-link"
133+
>
134+
{expanded ? "Show less." : "Read more…"}
135+
</button>
136+
</>
137+
)
118138

119139
return (
120140
<div className="typography-body-lg mt-8 px-2 pb-8 sm:px-3 lg:mt-12 xl:pb-12 [&>p+p]:mt-4 [&_a]:break-words">
121141
{lead.map((html, i) => (
122-
<p key={`lead-${i}`} dangerouslySetInnerHTML={{ __html: html }} />
142+
<p key={`lead-${i}`}>
143+
<span dangerouslySetInnerHTML={{ __html: html }} />
144+
{i === lastInLead && toggle}
145+
</p>
123146
))}
124147
{tail.length > 0 && (
125-
<div className="mt-4 xl:flex xl:items-end xl:gap-6">
148+
<div className="mt-4 first:mt-0 xl:flex xl:items-end xl:gap-6">
126149
<div className="xl:flex-1 [&>p+p]:mt-4">
127150
{tail.map((html, i) => (
128-
<p key={`tail-${i}`} dangerouslySetInnerHTML={{ __html: html }} />
151+
<p key={`tail-${i}`}>
152+
<span dangerouslySetInnerHTML={{ __html: html }} />
153+
{i === lastInTail && toggle}
154+
</p>
129155
))}
130156
</div>
131157
{sideSpeaker && (

0 commit comments

Comments
 (0)