Imported from ticklestk/personal_portfolio (
AGENTS.md). Install upstream withnpx skills add ticklestk/personal_portfolio. Copyright stays with the author.
Current Build Status
See HANDOFF.md in the project root for the full status of what's done, what's missing, and what to do next. Always read it before starting work.
HANDOFF.md Update Rule
After completing any of the following, immediately update HANDOFF.md to reflect the new state:
- A component is created or meaningfully improved
- A section is completed
npm run buildpasses or a new error is introduced- User-provided assets (images, content) are added
- Any "Next Steps" item is finished
Keep the "What's Done" table and "Next Steps" list accurate at all times. Update incrementally as you go — not just at the end of a session.
This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ before writing any code. Heed deprecation notices.
Portfolio Agent Rules
Stack Versions
| Package | Version |
|---|---|
| next | 16.2.6 (Next.js 15 series) |
| react | 19.2.4 |
| @react-three/fiber | 9.x |
| @react-three/drei | 10.x |
| framer-motion | 12.x |
| tailwindcss | 4.x |
| shadcn/ui style | radix-nova |
3D Rule
R3F <Canvas> must always be in a 'use client' file that is dynamically imported with { ssr: false }.
Never render a Canvas in a server component. The chain is:
ServerComponent → ClientWrapper (dynamic ssr:false) → LogoCanvas (Canvas host) → LogoMesh
Font Rule
Clash Display is a local font in app/fonts/ClashDisplay-Variable.woff2 loaded via next/font/local.
Never import it from Google Fonts or a CDN. Inter uses next/font/google.
Color Rule
All colors come from CSS custom properties in app/globals.css.
Palette (pastel light — do not revert to dark):
--aurora-violet:oklch(0.48 0.15 258)— deeper periwinkle-indigo (contrast on light bg)--aurora-rose:oklch(0.52 0.12 5)— muted rose (contrast on light bg)--background:oklch(0.97 0.012 270)— soft lavender-white--foreground:oklch(0.18 0.015 265)— deep cool charcoal--muted-foreground:oklch(0.46 0.015 265)— solid slate-600 equivalent (NOT semi-transparent)
Light mode only — no dark class on <html>. The .dark block in globals.css is preserved as a reference but is not active.
Never hardcode hex or oklch literals in component JSX/TSX.
Use Tailwind utility classes (bg-aurora-violet/10, text-aurora-violet, border-aurora-rose/20)
or CSS vars (var(--aurora-violet)) for all color references.
Exception — Framer Motion inline style props (borderColor, boxShadow, filter, background gradients):
These bypass Tailwind, so use the raw oklch value that matches the CSS var.
When the palette changes, these must be updated manually — they are listed in HeroSection.tsx, NavTabs.tsx, SectionPanel.tsx, ExperienceCard.tsx.
Animation Rule
All Framer Motion animated components must carry 'use client' and should check useReducedMotion().
Import from "framer-motion" directly — no separate client sub-path needed in Next.js 15.
Data Rule
All portfolio content (education, experience, projects) lives in lib/data/*.ts as typed arrays.
Components receive typed props — no inline content strings in JSX.
21st Dev Magic MCP
The "magic" MCP server is registered and available. When building or improving UI components, proactively query it for animated/interactive component inspiration — especially for: buttons, cards, hover effects, loaders, navigation, and timeline components. Use it without waiting to be asked when you think a component would benefit from it.
Tailwind v4 Notes
No tailwind.config.js — all custom tokens go in @theme inline { } in globals.css.
Custom colors declared there become utility classes: bg-aurora-violet, text-aurora-rose, etc.
Arbitrary font family: font-[family-name:var(--font-clash)] or register --font-heading in @theme inline.