The honest question after a talk. The story you needed to hear. The person who’s been figuring out the same thing as you.
We’re shaping DevCon26 around useful stories and enough time to talk to the people in the room.
-
- {speakerSlots.map((slot) => (
-
-
-
{slot}
-
-
Next voice, coming soon.
-
- Speaker announcement
- To be announced
-
- ))}
+
+
+
+
@@ -534,11 +507,16 @@ function packageEnquiryLink(sponsorship: (typeof sponsorshipPackages)[number]) {
@@ -900,37 +878,72 @@ function packageEnquiryLink(sponsorship: (typeof sponsorshipPackages)[number]) {
margin-top: 16px;
}
- .programme-note {
- position: relative;
- max-width: 380px;
- margin: 38px 0 0 34px;
- padding: 30px;
- border-radius: 6px;
- background: var(--dc-yellow);
- box-shadow: 0 3px 3px rgb(0 0 0 / 0.03), 0 13px 24px rgb(0 0 0 / 0.06);
- transform: rotate(3deg);
+ .programme {
+ margin-top: 38px;
}
- .note-fold {
- position: absolute;
- top: 12px;
- right: 20px;
+ .programme h3 {
font-size: 32px;
}
- .programme-note .section-caption {
- padding-right: 25px;
- font-size: var(--dc-text-meta);
+ .programme-status {
+ margin-top: 16px;
+ color: var(--dc-muted);
+ line-height: 1.6;
}
- .programme-note h3 {
- font-size: 32px;
+ .programme-list {
+ margin: 20px 0 0;
+ padding: 0;
+ list-style: none;
}
- .programme-note > p:last-child {
- margin-top: 18px;
+ .programme-list :global(li) {
+ display: grid;
+ grid-template-columns: minmax(76px, 110px) minmax(0, 1fr);
+ gap: 16px;
+ padding: 20px 0;
+ border-top: 1px solid currentColor;
+ }
+
+ .programme-list :global(.programme-time) {
font-size: 14px;
- line-height: 1.6;
+ font-weight: 700;
+ }
+
+ .programme-list :global(h4) {
+ font-size: 20px;
+ line-height: 1.25;
+ }
+
+ .programme-list :global(p) {
+ margin-top: 8px;
+ color: var(--dc-muted);
+ line-height: 1.55;
+ white-space: pre-line;
+ overflow-wrap: anywhere;
+ }
+
+ :global(.programme-speakers) {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px 16px;
+ margin-top: 14px;
+ }
+
+ :global(.programme-speaker) {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
+ font-weight: 700;
+ }
+
+ :global(.programme-speaker img) {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ object-fit: cover;
}
.speakers-section {
@@ -957,82 +970,38 @@ function packageEnquiryLink(sponsorship: (typeof sponsorshipPackages)[number]) {
gap: 20px;
}
- .speaker-grid {
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 36px;
- margin: 50px 35px 0;
- align-items: start;
- }
-
- .speaker-card:nth-child(2) {
- margin-top: 62px;
+ .speakers-status {
+ margin-top: 32px;
+ color: var(--dc-muted);
+ line-height: 1.6;
}
- .speaker-card:nth-child(3) {
- margin-top: 20px;
+ .speaker-list {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
+ gap: 16px;
+ margin: 40px 0 0;
+ padding: 0;
+ list-style: none;
}
- .speaker-placeholder {
- position: relative;
+ .speaker-list :global(li) {
display: flex;
- aspect-ratio: 1.08;
- flex-direction: column;
- justify-content: space-between;
- overflow: hidden;
- padding: 23px;
- border-radius: 8px;
- background: var(--dc-ink);
- color: var(--dc-paper-light);
- }
-
- .speaker-number {
- font-size: var(--dc-text-meta);
- font-weight: 600;
- }
-
- .speaker-art {
- position: absolute;
- align-self: center;
- top: 18%;
- width: 58%;
- max-width: 160px;
- height: auto;
- color: var(--dc-yellow);
- pointer-events: none;
- transform: rotate(-10deg);
- }
-
- .speaker-card:nth-child(2) .speaker-placeholder {
- background: var(--dc-yellow);
- color: var(--dc-ink);
- }
-
- .speaker-card:nth-child(2) .speaker-art {
- color: var(--dc-ink);
- transform: rotate(8deg);
- }
-
- .speaker-card:nth-child(3) .speaker-art {
- color: oklch(0.76 0.14 354);
- transform: rotate(-4deg);
- }
-
- .speaker-placeholder small {
- position: relative;
- font-size: var(--dc-text-meta);
- }
-
- .speaker-status {
- margin-top: 18px;
- color: var(--dc-muted);
- font-size: var(--dc-text-meta);
+ align-items: center;
+ gap: 14px;
+ padding: 16px 0;
+ border-top: 1px solid currentColor;
+ font-size: var(--dc-text-card);
+ font-weight: 700;
+ overflow-wrap: anywhere;
}
- .speaker-card h3 {
- margin-top: 7px;
- font-size: var(--dc-text-card);
- letter-spacing: var(--tracking-heading);
+ .speaker-list :global(img) {
+ width: 56px;
+ height: 56px;
+ flex: none;
+ border-radius: 50%;
+ object-fit: cover;
}
.location-section {
@@ -2017,11 +1986,6 @@ function packageEnquiryLink(sponsorship: (typeof sponsorshipPackages)[number]) {
gap: 38px;
}
- .speaker-grid {
- gap: 25px;
- margin-inline: 0;
- }
-
.sponsors-section {
column-gap: 40px;
}
@@ -2226,10 +2190,8 @@ function packageEnquiryLink(sponsorship: (typeof sponsorshipPackages)[number]) {
margin-top: 20px;
}
- .programme-note {
- max-width: calc(100% - 20px);
- margin: 30px 0 0 10px;
- padding: 26px;
+ .programme {
+ margin-top: 30px;
}
.speakers-section {
@@ -2241,54 +2203,9 @@ function packageEnquiryLink(sponsorship: (typeof sponsorshipPackages)[number]) {
gap: 22px;
}
- .speaker-grid {
+ .speaker-list {
grid-template-columns: minmax(0, 1fr);
- gap: 24px;
- margin: 30px 0 0;
- }
-
- .speaker-card,
- .speaker-card:nth-child(2),
- .speaker-card:nth-child(3) {
- display: grid;
- grid-template-columns: 112px minmax(0, 1fr);
- grid-template-rows: 1fr 1fr;
- gap: 6px 18px;
- margin: 0;
- }
-
- .speaker-placeholder {
- grid-row: 1 / 3;
- aspect-ratio: 1;
- padding: 12px;
- }
-
- .speaker-number {
- font-size: 12px;
- }
-
- .speaker-art {
- top: 28%;
- width: 58%;
- max-width: 72px;
- }
-
- .speaker-placeholder small {
- display: none;
- }
-
- .speaker-status {
- align-self: end;
- margin: 0;
- font-size: 13px;
- line-height: 1.4;
- }
-
- .speaker-card h3 {
- align-self: start;
- margin: 0;
- font-size: 20px;
- line-height: 1.2;
+ margin-top: 30px;
}
.location-section {
diff --git a/src/worker.ts b/src/worker.ts
index 8914708..6afc438 100644
--- a/src/worker.ts
+++ b/src/worker.ts
@@ -21,6 +21,10 @@ const SOCIAL_CARD_ROUTE = /^\/devcon26\/cards\/([A-Za-z0-9_-]{22,80})\/?$/;
const SOCIAL_CARD_IMAGE_ROUTE = /^\/devcon26\/cards\/([A-Za-z0-9_-]{22,80})\/image\/?$/;
const SOCIAL_CARD_PREVIEW_ROUTE = /^\/devcon26\/cards\/([A-Za-z0-9_-]{22,80})\/preview\.svg\/?$/;
const CALENDAR_PATH = '/events/calendar.ics';
+const PROGRAMME_RELAY_PATH = '/__public/devcon26/programme';
+const PROGRAMME_UPSTREAM = 'https://em.devcongress.org/api/public/annual-conference/2026/programme';
+const WEBSITE_PREVIEW_HOST = /^[a-z0-9-]+-devcongress-website\.admins-a7d\.workers\.dev$/;
+const PROGRAMME_MAX_BYTES = 1024 * 1024;
const EVENT_SHELL_PATH = '/events/';
const SUPPORT_PATHS = new Set(['/support', '/support/']);
const SUPPORT_URL = 'https://pages.inttegro.com/buy/sale_eLiPUNAEjfeHZOX4VHQPuy0oJWQT6n0g7Bdof5rr';
@@ -34,6 +38,17 @@ export default {
async fetch(request: Request, env: Env, context: ExecutionContext): Promise
{
const url = new URL(request.url);
+ if (url.pathname === PROGRAMME_RELAY_PATH) {
+ if (!WEBSITE_PREVIEW_HOST.test(url.hostname)) return notFound();
+ if (request.method !== 'GET') {
+ return new Response('Method not allowed', {
+ status: 405,
+ headers: { Allow: 'GET', 'Cache-Control': 'no-store' },
+ });
+ }
+ return servePreviewProgramme();
+ }
+
if (SUPPORT_PATHS.has(url.pathname)) {
return Response.redirect(SUPPORT_URL, 307);
}
@@ -78,6 +93,61 @@ export default {
},
};
+async function servePreviewProgramme(): Promise {
+ try {
+ const upstream = await fetch(PROGRAMME_UPSTREAM, {
+ method: 'GET',
+ headers: { Accept: 'application/json' },
+ redirect: 'manual',
+ signal: AbortSignal.timeout(12_000),
+ });
+
+ const contentType = upstream.headers.get('Content-Type')?.split(';')[0].trim().toLowerCase();
+
+ if (!upstream.ok || !upstream.body || contentType !== 'application/json') {
+ return serviceUnavailable('Programme temporarily unavailable');
+ }
+
+ const reader = upstream.body.getReader();
+ const chunks: Uint8Array[] = [];
+ let total = 0;
+
+ try {
+ while (true) {
+ const { done, value } = await reader.read();
+
+ if (done) break;
+ total += value.byteLength;
+ if (total > PROGRAMME_MAX_BYTES) {
+ await reader.cancel();
+ return serviceUnavailable('Programme temporarily unavailable');
+ }
+ chunks.push(value);
+ }
+ } finally {
+ reader.releaseLock();
+ }
+
+ const body = new Uint8Array(total);
+ let offset = 0;
+
+ for (const chunk of chunks) {
+ body.set(chunk, offset);
+ offset += chunk.byteLength;
+ }
+
+ return new Response(body, {
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Cache-Control': 'no-store',
+ 'X-Content-Type-Options': 'nosniff',
+ },
+ });
+ } catch {
+ return serviceUnavailable('Programme temporarily unavailable');
+ }
+}
+
async function serveLiveSocialCardPage(request: Request, slug: string): Promise {
if (!isDevcon26SocialCardSlug(slug)) return notFound();
diff --git a/wrangler.jsonc b/wrangler.jsonc
index 73afe8a..8429b05 100644
--- a/wrangler.jsonc
+++ b/wrangler.jsonc
@@ -12,6 +12,7 @@
"binding": "ASSETS",
"run_worker_first": [
"/events/*",
+ "/__public/devcon26/programme",
"/devcon26/cards/*",
"/support",
"/support/",