Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d7decd4
Initial commit
Pabl0cks Jan 9, 2026
8f55d72
Add all the slides with provisional content
Pabl0cks Jan 9, 2026
1c7b8f7
Remove unused image
Pabl0cks Jan 9, 2026
3434da7
Refactor and change scrolling-menu behavior
Pabl0cks Jan 9, 2026
18c019d
Tweak card corner
Pabl0cks Jan 9, 2026
6e79599
Terminal with accordion for small devices
Pabl0cks Jan 9, 2026
08947f6
Delete automated test sshots
Pabl0cks Jan 9, 2026
51de810
Merge branch 'main' into 2025-recap
carletex Jan 9, 2026
80ba160
(copy) viem => vocs
technophile-04 Jan 10, 2026
e49cf8a
use `getLayout` pattern to handle conditional rendering of Header and…
technophile-04 Jan 10, 2026
f22ddc0
setup og title, description
technophile-04 Jan 11, 2026
e6a317a
remove duplicate tag for twitter and general og
technophile-04 Jan 11, 2026
b7ec7ed
Add links
Pabl0cks Jan 11, 2026
282038f
Increase noise to be noticeable
Pabl0cks Jan 11, 2026
1611094
Fix tab switch behavior when clicking tabs like Arbitrum
Pabl0cks Jan 11, 2026
71d128e
Tweak descriptions
Pabl0cks Jan 11, 2026
592b5a2
Tweak section titles
Pabl0cks Jan 11, 2026
7727a47
Initial version of hero (WIP)
Pabl0cks Jan 11, 2026
b9cd1fa
Hero content tweaks
Pabl0cks Jan 11, 2026
f4a360b
Tweak SRE content
Pabl0cks Jan 11, 2026
05aad36
Wording and style content tweaks
Pabl0cks Jan 11, 2026
e5c758b
SE-2 content tweaks
Pabl0cks Jan 11, 2026
6a80222
Add link to aigentsbcn
Pabl0cks Jan 12, 2026
bedb4e0
Ecosystem content tweaks
Pabl0cks Jan 12, 2026
20502d0
Tweak misc content and terminal subtitles color
Pabl0cks Jan 12, 2026
a211de3
Hero text
carletex Jan 12, 2026
c68daa7
SRE
carletex Jan 12, 2026
97973e8
SE-2 section + Link at the end
carletex Jan 12, 2026
37a7a5e
mobile
carletex Jan 12, 2026
5417832
Education
carletex Jan 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/nextjs/components/2025/SideNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { SLIDES, SlideId } from "./constants";

interface SideNavProps {
activeSlide: string;
onNavigate: (slideId: SlideId) => void;
}

export const SideNav = ({ activeSlide, onNavigate }: SideNavProps) => (
<nav className="fixed right-8 top-1/2 -translate-y-1/2 z-50 hidden lg:flex flex-col gap-3 bg-black/90 backdrop-blur-sm px-4 py-3 rounded-lg border border-neutral-content/10">
{SLIDES.map(slide => (
<button
key={slide.id}
onClick={() => onNavigate(slide.id)}
className={`flex items-center gap-3 transition-all duration-200 text-sm ${
activeSlide === slide.id ? "text-primary-content" : "text-neutral-content/40 hover:text-neutral-content/70"
}`}
>
<span
className={`block w-1.5 h-1.5 transition-all duration-200 ${
activeSlide === slide.id ? "bg-primary scale-125" : "bg-neutral-content/30"
}`}
/>
<span className={`transition-all duration-200 ${activeSlide === slide.id ? "font-medium" : ""}`}>
{slide.title}
</span>
</button>
))}
</nav>
);
35 changes: 35 additions & 0 deletions packages/nextjs/components/2025/SlideSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ReactNode } from "react";
import { NOISE_TEXTURE_BG } from "./constants";

interface SlideSectionProps {
id: string;
projectNumber: string;
children: ReactNode;
}

export const SlideSection = ({ id, projectNumber, children }: SlideSectionProps) => (
<section
id={id}
className="min-h-[80vh] py-24 flex items-center relative overflow-hidden border-t border-neutral-content/10 first:border-t-0"
>
{/* Background texture */}
<div className="absolute inset-0 opacity-[0.02]">
<div
className="absolute inset-0"
style={{
backgroundImage: NOISE_TEXTURE_BG,
}}
/>
</div>

<div className="container mx-auto px-6 lg:px-12 relative z-10">
{/* Section indicator */}
<div className="flex items-center gap-4 mb-8">
<span className="text-xs text-white/80 uppercase tracking-[0.3em]">Project {projectNumber}</span>
<span className="w-48 lg:w-64 h-px bg-neutral-content/20" />
</div>

{children}
</div>
</section>
);
32 changes: 32 additions & 0 deletions packages/nextjs/components/2025/StatCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
interface StatCardProps {
value: string;
label: string;
growth?: string;
}

export const StatCard = ({ value, label, growth }: StatCardProps) => (
<div className="group">
<div className="border border-neutral-content/20 px-4 py-2 transition-colors duration-200 hover:border-neutral-content/40">
{/* Top border decoration */}
<div className="flex items-center gap-2 text-neutral-content/30 text-xs">
<span>┌</span>
<span className="flex-1 border-t border-neutral-content/20" />
<span>┐</span>
</div>

{/* Content */}
<div className="flex flex-col py-4 items-center text-center">
<span className="text-3xl md:text-4xl font-bold text-primary-content tracking-tight">{value}</span>
{growth && <span className="text-secondary text-sm mt-1">{growth}</span>}
<span className="text-neutral-content/50 text-sm mt-2">{label}</span>
</div>

{/* Bottom border decoration */}
<div className="flex items-center gap-2 text-neutral-content/30 text-xs">
<span>└</span>
<span className="flex-1 border-t border-neutral-content/20" />
<span>┘</span>
</div>
</div>
</div>
);
14 changes: 14 additions & 0 deletions packages/nextjs/components/2025/TaskList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
interface TaskListProps {
tasks: string[];
}

export const TaskList = ({ tasks }: TaskListProps) => (
<div className="grid gap-2">
{tasks.map((task, index) => (
<div key={index} className="flex items-start gap-3 text-neutral-content/70">
<span className="text-secondary text-xs">◆</span>
<span className="text-sm leading-relaxed">{task}</span>
</div>
))}
</div>
);
153 changes: 153 additions & 0 deletions packages/nextjs/components/2025/TerminalWindow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { ReactNode, useState } from "react";
import { ChevronDownIcon } from "@heroicons/react/24/outline";

interface Tab {
id: string;
title: string;
content: ReactNode;
}

interface TerminalWindowProps {
tabs: Tab[];
defaultTab?: string;
}

// Desktop version with tabs
const DesktopTerminal = ({
tabs,
activeTab,
setActiveTab,
}: {
tabs: Tab[];
activeTab: string;
setActiveTab: (id: string) => void;
}) => {
const activeContent = tabs.find(t => t.id === activeTab);

return (
<div className="hidden md:block border border-neutral-600 rounded-lg overflow-hidden">
{/* Title bar */}
<div className="flex items-stretch bg-neutral-700 border-b border-neutral-600">
{/* Window controls (decorative) */}
<div className="flex items-center gap-2 px-3 py-2">
<span className="w-3 h-3 rounded-full bg-red-500" />
<span className="w-3 h-3 rounded-full bg-yellow-500" />
<span className="w-3 h-3 rounded-full bg-green-500" />
</div>

{/* Tab headers */}
{tabs.map(tab => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`flex-1 min-w-0 flex items-center justify-center px-2 text-xs whitespace-nowrap border-l border-neutral-600 transition-all duration-200 ${
activeTab === tab.id ? "text-white font-medium" : "text-neutral-400 hover:text-neutral-200"
}`}
style={{
backgroundColor: activeTab === tab.id ? "#6b7280" : "transparent",
}}
title={tab.title}
>
{tab.title}
</button>
))}
</div>

{/* Content area */}
<div className="bg-black p-6">
<div className="animate-fadeIn">
{activeContent && (
<div>
<div className="flex items-center gap-3 mb-4">
<div className="flex items-center gap-2 text-primary text-sm">
<span>$</span>
<span className="text-neutral-content/70">cat</span>
<span className="text-secondary">{activeContent.title.toLowerCase().replace(/\s+/g, "-")}.md</span>
</div>
</div>
{activeContent.content}
</div>
)}
</div>
</div>
</div>
);
};

// Mobile version with accordion inside single terminal frame
const MobileTerminal = ({
tabs,
expandedTab,
toggleTab,
}: {
tabs: Tab[];
expandedTab: string | null;
toggleTab: (id: string) => void;
}) => {
return (
<div className="md:hidden border border-neutral-600 rounded-lg overflow-hidden">
{/* Title bar with window controls - only once */}
<div className="flex items-center gap-2 px-3 py-2 bg-neutral-700 border-b border-neutral-600">
<span className="w-3 h-3 rounded-full bg-red-500" />
<span className="w-3 h-3 rounded-full bg-yellow-500" />
<span className="w-3 h-3 rounded-full bg-green-500" />
</div>

{/* Accordion items */}
<div className="bg-black">
{tabs.map((tab, index) => {
const isExpanded = expandedTab === tab.id;
const isLast = index === tabs.length - 1;
return (
<div key={tab.id} className={!isLast ? "border-b border-neutral-700" : ""}>
{/* Accordion header */}
<button
onClick={() => toggleTab(tab.id)}
className="w-full flex items-center justify-between px-4 py-3 hover:bg-neutral-800/50 transition-colors"
>
<span className="text-sm text-white font-medium">{tab.title}</span>
<ChevronDownIcon
className={`w-5 h-5 text-neutral-400 transition-transform duration-200 ${
isExpanded ? "rotate-180" : ""
}`}
/>
</button>

{/* Accordion content */}
<div
className={`overflow-hidden transition-all duration-300 ease-in-out ${
isExpanded ? "max-h-[600px] opacity-100" : "max-h-0 opacity-0"
}`}
>
<div className="px-4 pb-4">
<div className="flex items-center gap-2 text-primary text-xs mb-3">
<span>$</span>
<span className="text-neutral-content/70">cat</span>
<span className="text-secondary">{tab.title.toLowerCase().replace(/\s+/g, "-")}.md</span>
</div>
{tab.content}
</div>
</div>
</div>
);
})}
</div>
</div>
);
};

export const TerminalWindow = ({ tabs, defaultTab }: TerminalWindowProps) => {
const [activeTab, setActiveTab] = useState<string>(defaultTab || tabs[0]?.id);
const [expandedTab, setExpandedTab] = useState<string | null>(defaultTab || tabs[0]?.id);

const toggleTab = (id: string) => {
setExpandedTab(expandedTab === id ? null : id);
};

return (
<>
<DesktopTerminal tabs={tabs} activeTab={activeTab} setActiveTab={setActiveTab} />
<MobileTerminal tabs={tabs} expandedTab={expandedTab} toggleTab={toggleTab} />
</>
);
};
33 changes: 33 additions & 0 deletions packages/nextjs/components/2025/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Slide definitions for navigation
export const SLIDES = [
{
id: "speedrun",
title: "Speedrun Ethereum",
shortTitle: "SRE",
},
{
id: "scaffold-eth",
title: "Scaffold-ETH 2",
shortTitle: "SE2",
},
{
id: "educational",
title: "Educational Initiatives",
shortTitle: "EDU",
},
{
id: "ecosystem",
title: "Ecosystem Collabs",
shortTitle: "ECO",
},
{
id: "misc",
title: "Misc",
shortTitle: "MISC",
},
] as const;

export type SlideId = (typeof SLIDES)[number]["id"];

// Shared noise texture background - extracted to avoid repetition
export const NOISE_TEXTURE_BG = `url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E")`;
10 changes: 10 additions & 0 deletions packages/nextjs/components/2025/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export { StatCard } from "./StatCard";
export { SideNav } from "./SideNav";
export { TerminalWindow } from "./TerminalWindow";
export { SlideSection } from "./SlideSection";
export { TaskList } from "./TaskList";
export { SLIDES, NOISE_TEXTURE_BG } from "./constants";
export type { SlideId } from "./constants";

// Re-export slides
export * from "./slides";
Loading