kevinryan.io
The portfolio site at kevinryan.io is the primary web property for Kevin Ryan. It is a statically exported Next.js application served by nginx.
| Technology | Version | Role |
|---|---|---|
| Next.js | 16.1.4 | React framework (App Router, static export) |
| React | 19.2.3 | UI library |
| TypeScript | ^5 | Type safety (strict mode) |
| Tailwind CSS | ^4 | Utility-first styling |
| DaisyUI | ^5.5.14 | Tailwind component library |
| PostCSS | ^8.5.6 | CSS processing |
| Fitty | ^2.4.2 | Responsive text fitting |
| ESLint | ^9 | Linting (with eslint-config-next) |
Architecture
Section titled “Architecture”graph TD
subgraph app["Next.js App Router"]
layout["app/layout.tsx<br/>(root layout)"]
home["app/page.tsx<br/>(home)"]
speaking["app/speaking/<br/>(talk pages + decks)"]
end
subgraph components["Components"]
header["SiteHeader"]
footer["SiteFooter"]
sections["Section components ×12"]
end
subgraph styling["Styling"]
tw["Tailwind CSS 4"]
daisy["DaisyUI"]
globals["globals.css<br/>(CSS variables)"]
end
layout --> header & footer
home --> sections
sections --> tw & daisy
Configuration
Section titled “Configuration”Next.js (next.config.ts)
Section titled “Next.js (next.config.ts)”| Setting | Value | Purpose |
|---|---|---|
output | 'export' | Static HTML export (no Node.js runtime) |
images.unoptimized | true | Required for static export |
trailingSlash | true | Consistent URL paths |
reactStrictMode | true | React development checks |
NEXT_PUBLIC_COMMIT_SHA | From env or 'dev' | Version display in footer |
TypeScript (tsconfig.json)
Section titled “TypeScript (tsconfig.json)”- Strict mode enabled
- Target: ES2017
- Module resolution: bundler
- Path alias:
@/*maps to project root
Application Structure
Section titled “Application Structure”sites/kevinryan-io/├── app/│ ├── layout.tsx # Root layout (fonts, analytics, header)│ ├── page.tsx # Home page (section composition)│ ├── globals.css # Tailwind imports + CSS variables│ └── speaking/│ ├── layout.tsx # Speaking section metadata│ ├── page.tsx # Speaking index│ ├── spec-driven-development/│ │ ├── page.tsx # Talk detail page│ │ └── deck/page.tsx # Slide deck│ └── dark-factory/│ ├── page.tsx # Talk detail page│ └── deck/page.tsx # Slide deck├── components/│ ├── SiteHeader.tsx # Fixed navigation with mobile menu│ ├── SiteFooter.tsx # Footer with commit SHA│ ├── BookCover.tsx # Book cover component│ └── sections/│ ├── HeroSection.tsx│ ├── TickerBar.tsx│ ├── DocsBanner.tsx│ ├── AboutSection.tsx│ ├── CapabilitiesSection.tsx│ ├── DeliverySection.tsx│ ├── ClientsSection.tsx│ ├── TimelineSection.tsx│ ├── SpecMcpSection.tsx│ ├── WritingSection.tsx│ ├── CertificationsSection.tsx│ └── ContactSection.tsx├── hooks/│ └── useRevealOnScroll.ts # IntersectionObserver for scroll animations├── lib/│ └── constants.ts # Layout constants (container config)├── public/ # Static assets (images, favicons)├── Dockerfile├── nginx.conf├── next.config.ts├── tsconfig.json├── postcss.config.mjs├── eslint.config.mjs└── package.jsonPage Architecture
Section titled “Page Architecture”Home Page
Section titled “Home Page”The home page is a composition of section components rendered in sequence. Each section is a standalone component in components/sections/:
- HeroSection — full-viewport hero with name, title, and animated text (Fitty)
- TickerBar — scrolling ticker with key phrases
- DocsBanner — link to documentation site
- AboutSection — professional summary
- CapabilitiesSection — skill areas
- DeliverySection — delivery methodology
- ClientsSection — client logos and names
- TimelineSection — career timeline
- SpecMcpSection — SpecMCP project highlight
- WritingSection — published works
- CertificationsSection — professional certifications
- ContactSection — contact details
Speaking Pages
Section titled “Speaking Pages”The /speaking route contains talk detail pages and slide decks. Each talk has its own directory with a detail page and a /deck sub-route for the presentation slides.
Styling
Section titled “Styling”Tailwind CSS 4
Section titled “Tailwind CSS 4”Tailwind is imported via the new v4 CSS-first configuration:
@import "tailwindcss";PostCSS processes Tailwind via @tailwindcss/postcss. Custom CSS variables define the brand colour palette and typography scale in globals.css.
Five Google Fonts are loaded in the root layout:
| Font | Weight(s) | Usage |
|---|---|---|
| Archivo | 400–900 | Primary body text |
| Bebas Neue | 400 | Display headings |
| Work Sans | 300, 900 | Accent text |
| DM Sans | 300–700 | Secondary body text |
| JetBrains Mono | 400–700 | Code and monospace |
DaisyUI
Section titled “DaisyUI”DaisyUI provides pre-built Tailwind components (buttons, cards, navigation). It is installed as a dev dependency and integrated via the Tailwind plugin system.
Client-Side Patterns
Section titled “Client-Side Patterns”Scroll Reveal
Section titled “Scroll Reveal”The useRevealOnScroll hook uses the IntersectionObserver API to add a .revealed class to elements with the .reveal class when they enter the viewport. This drives CSS-based entrance animations without a third-party animation library.
Responsive Text (Fitty)
Section titled “Responsive Text (Fitty)”The fitty library dynamically scales text to fill its container width. It is used in the hero section for responsive headline sizing that adapts to any viewport width.
Static Export
Section titled “Static Export”The site uses output: 'export' in Next.js, which generates a fully static site at build time. There are no API routes, no server components with runtime data fetching, and no middleware. The output directory (out/) contains only HTML, CSS, JavaScript, and static assets.
Build and Serve
Section titled “Build and Serve”The site uses a multi-stage Docker build:
- Build stage — Node.js 22 Alpine with pnpm runs
next build, producing static files inout/ - Serve stage — nginx 1.28.2 Alpine serves the static output on port 8080
The COMMIT_SHA build argument is exposed as NEXT_PUBLIC_COMMIT_SHA, making the git commit hash available in the client bundle for version identification in the footer.
nginx Configuration
Section titled “nginx Configuration”- JSON structured access logs (consumed by Promtail/Loki)
- Gzip compression for HTML, CSS, JS, JSON, and SVG
- Long-lived cache headers for
/_next/static/(immutable hashed assets) - No-cache headers for HTML files (ensures fresh content)
/healthzendpoint for Kubernetes health probes- Security headers:
X-Content-Type-Options,X-Frame-Options,Referrer-Policy try_filesfallback:$uri→$uri.html→$uri/index.html→/404.html