A Next.js 16+ personal portfolio template built around a signature WebGL flow shader, a magnetic morphing portrait, Lenis smooth scroll, and a calm black-and-white design system. Designed for individual designers and engineers who want a brand-ready scaffold with a distinctive look on day one.
/about routeprefers-reduced-motion guardsnpm install
npm run dev
Open http://localhost:3000 in your browser.
| Command | Description |
|---|---|
npm run dev |
Start development server |
npm run build |
Build for production |
npm run start |
Start production server |
npm run lint |
Run ESLint |
npm run lint:fix |
Fix ESLint errors |
npm run format |
Format code with Prettier |
npm run format:check |
Check code formatting |
npm run typecheck |
Run TypeScript type checking |
├── app/
│ ├── about/ # About route
│ ├── projects/ # Projects route
│ ├── globals.css # Design tokens, frame, project-card styles
│ ├── layout.tsx # Root layout with providers, nav, backdrop
│ ├── page.tsx # Home page
│ ├── robots.ts # Dynamic robots.txt
│ ├── sitemap.ts # Dynamic sitemap
│ ├── icon.svg # Favicon
│ └── apple-icon.svg # Apple touch icon
├── components/
│ ├── about/
│ │ ├── education.tsx # Education list with bordered logo squares
│ │ ├── experience.tsx # Expandable timeline with fade-mask collapse
│ │ ├── polaroid-strip.tsx # Tilted polaroid photos with dotted backs
│ │ ├── skills.tsx # Skills grid
│ │ └── stack.tsx # Matter.js physics-driven tech chips
│ ├── contact/
│ │ ├── contact-button.tsx # Click-to-copy email button
│ │ ├── contact-card.tsx # Shader-backed contact card
│ │ └── contact-card-ctas.tsx # Social CTAs
│ ├── hero/
│ │ ├── hero.tsx # Hero layout and copy
│ │ ├── hero-ctas.tsx # Magnetic primary/secondary CTAs
│ │ └── portrait-morph.tsx # Hover-swap portrait with magnetic follow
│ ├── layout/
│ │ ├── nav.tsx # Pill nav with theme toggle
│ │ ├── page-backdrop.tsx # Site-wide shader backdrop
│ │ ├── providers.tsx # Theme + smooth-scroll providers
│ │ ├── skip-to-content.tsx # Skip link for a11y
│ │ └── smooth-scroll.tsx # Lenis smooth-scroll wrapper
│ ├── projects/
│ │ └── projects.tsx # Projects grid
│ ├── shaders/
│ │ └── shader-flow.tsx # WebGL flow shader (raw OGL)
│ └── ui/
│ ├── dotted-pattern.tsx # Shared dotted texture
│ └── motion-primitives.tsx # FadeIn, ScaleUnblur entrance helpers
├── lib/
│ ├── config.ts # Site config
│ ├── metadata.ts # SEO metadata utilities
│ └── motion.tsx # Motion components & hooks
└── public/
├── josh.webp # Default portrait
├── josh_wave.webp # Hover portrait
├── linkedin.svg # Social icon
├── x.svg # Social icon
└── site.webmanifest # PWA manifest
Edit lib/metadata.ts to update:
- Site name, description, and URL
- Social media handles
- Keywords and authors
The default siteConfig.url is https://example.com — replace it with your production URL before deploying so OpenGraph and the sitemap emit correct absolute URLs.
public/josh.webp and public/josh_wave.webp with your own default + hover portraits. Keep both files the same dimensions and aspect ratio for a clean morph.components/hero/hero.tsx.components/contact/contact-card.tsx and components/contact/contact-card-ctas.tsx.All about-page content is co-located in its component file — there is no separate content directory.
components/about/polaroid-strip.tsx — image paths and captionscomponents/about/skills.tsx — skill listcomponents/about/stack.tsx — tech logos and physics chipscomponents/about/experience.tsx — roles, companies, dates, descriptionscomponents/about/education.tsx — schools, programs, datesEdit the project array in components/projects/projects.tsx. Each entry includes a title, description, image (dribbble mockup or your own), and external link.
Replace the following files with your brand assets:
- app/icon.svg — Favicon (32x32)
- app/apple-icon.svg — Apple touch icon (180x180)
- public/og-image.png — Open Graph image (1200x630)
The flow shader (components/shaders/shader-flow.tsx) is used by both the page backdrop and the contact card. Key knobs (all exposed as props with sensible defaults):
colorLowA, colorHighA — palette stops in linear RGBflowSpeed — [x, y] flow vectoriterations — domain-warp iteration count (up to 24)scale — domain scalebrightness — output multiplierfadeCx, fadeCy, fadeRx, fadeRy — aspect-correct circular fade center and radii. The fade is baked into the fragment shader and reads --background from CSS so theme changes are picked up automatically via a MutationObserver on <html>.The shader:
- Caps DPR at min(devicePixelRatio, 1.25) for ShaderFlow
- Sizes to host container via ResizeObserver
- Pauses via IntersectionObserver when offscreen and on visibilitychange
- Uses highp precision; renders a single opaque draw (no CSS mask layers)
// app/contact/page.tsx
import { createMetadata } from "@/lib/metadata";
import type { Metadata } from "next";
export const metadata: Metadata = createMetadata({
title: "Contact",
description: "Get in touch.",
path: "/contact",
});
export default function ContactPage() {
return <main id="main-content">...</main>;
}
--background / --foreground — Page background and text--muted / --muted-foreground — Subtle surfaces and secondary text--border — Hairline rails and dividers--ring — Focus rings--frame — Site-frame color (matches --background)The palette is strict black and white. No accent or semantic color hues are used.
backdrop-blur (except the experience collapsed-fade)pointer on all clickable nav and CTA itemsThe template includes:
- Skip-to-content link
- Visible focus rings on all interactive elements
- ARIA labels on toggles, social links, and the contact button
- prefers-reduced-motion guards on the theme toggle view-transition
- Shaders pause when offscreen and on tab hide
- Proper heading hierarchy (single <h1> per page)
- WCAG 2.1 AA contrast compliance in both themes
WEBGL_lose_contextIntersectionObserver) and on tab hide (visibilitychange)next.config.ts images.remotePatterns allows images.unsplash.com and cdn.dribbble.com for the polaroid and project mockups respectively.@/ path alias.components/projects/projects.tsx noting that dribbble mockups are placeholders to be replaced with your own work.This template is free to use in personal and commercial projects. You may not resell or redistribute the template itself.
$ claude mcp add rbp-portfolio \
-- python -m otcore.mcp_server <graph>