- Store domain shown as Store.GregKilmer.com (no uppercase transform) - Cards and panels 10px corners; buttons pill-shaped - Canvas star/circuit violet switched to brand violet #6A2FE0 - Nav, store nav and footer seal at the brand's 72px minimum (nav 80px tall) - Faint seals on contact, store cards, deck boxes, order and professional Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
Greg Kilmer — Profile & Portfolio Site
A responsive, content-driven personal site for Greg Kilmer, built as two wings of the same house: a polished professional resume/portfolio on one side, and a warm, personality-driven interests hub on the other.
Built with Astro 7 + React Islands + TypeScript, served by a Node server with API routes, ready to deploy as a container on a homelab Cosmos system.
Highlights
- Astro 7 static-first core with React Islands for interactivity (mobile menu, theme toggle, animated hero background, contact form).
- Node backend (
@astrojs/node, standalone) with a typed/api/contactendpoint (SMTP via nodemailer + webhook fallback, Zod validation, rate limit). - Section-aware theming: the Professional wing reads dark/polished, the Interests wing reads warm/inviting, plus a global light/dark toggle.
- Rich motion: animated mesh-gradient hero with an optional canvas particle
field, IntersectionObserver scroll reveals, View Transitions page swaps,
scroll-triggered nav blur. All
prefers-reduced-motionaware. - Content Collections (typed): add projects and experience by dropping markdown/MDX files.
- mdi icons everywhere (no emojis, per the spec), inlined at build time from
@iconify-json/mdifor zero runtime cost. - Self-hosted variable fonts (Inter, Plus Jakarta Sans, JetBrains Mono).
- Accessible: semantic landmarks, skip link, keyboard-operable menu, focus-visible rings, ARIA on interactive bits, WCAG-AA contrast targets.
- Deploy artifacts: multi-stage
Dockerfile+docker-compose.yml.
Quickstart
# 1. Install dependencies (Node 20+)
npm install
# 2. Start the dev server with hot reload
npm run dev
# -> http://localhost:4321
# 3. Production build (runs astro check + build)
npm run build
# 4. Run the built Node server
npm run serve
# -> http://localhost:4321
Environment is optional for local dev. To enable contact-form delivery, copy
.env.example to .env and fill in SMTP or webhook vars (see
docs/DEPLOYMENT.md).
Project structure
src/
├── components/
│ ├── hero/ AnimatedBackground.tsx (gradient + canvas particles)
│ ├── motion/ Reveal.astro (IntersectionObserver scroll reveals)
│ ├── nav/ NavBar.astro, MobileMenu.tsx, ThemeToggle.tsx
│ ├── cards/ InterestCard.astro
│ ├── professional/ Timeline, SkillGrid, ProjectCard, SectionNav, ContactForm
│ ├── layout/ Footer, SectionHeader, SplitTransition, InterestHero,
│ │ RelatedInterests, Placeholder
│ └── Icon.astro Inline mdi icon renderer (build-time)
├── content/
│ ├── projects/ Project collection entries (markdown/MDX)
│ └── experience/ Experience collection entries
├── layouts/
│ └── BaseLayout.astro <html> shell, head, fonts, theme init, nav, footer
├── pages/
│ ├── index.astro Home (hero + snapshot grid)
│ ├── about.astro About + split transition fork
│ ├── professional/index.astro Single long page: Summary, Experience,
│ │ Skills, Projects, Education, Contact
│ ├── interests/ Hub + 5 sub-pages
│ │ ├── index.astro
│ │ ├── ttrpgs / gaming / reading / making / pokemon .astro
│ ├── contact.astro Two-column connect page + contact form
│ └── api/contact.ts POST endpoint -> SMTP / webhook
├── content.config.ts Collection schemas (projects, experience)
├── lib/ site.ts (nav/socials), motion.ts, content helpers
├── styles/ global.css, themes.css, animations.css
└── scripts/theme-init.ts No-FOUC theme boot (inlined in <head>)
The two wings (theming model)
Theming has two independent axes (see docs/THEMING.md):
- Global light/dark on
<html data-theme>, set before paint by an inline script, persisted tolocalStorage, toggleable via the nav button. - Section tone via
data-surfaceon<body>:neutral(Home/About/Contact) — balanced middle groundprofessional— dark slate, polishedinterest— warm cream/amber, inviting
Components only reference semantic CSS variables (--surface, --text,
--accent...), so changing either axis restyles everything automatically.
Adding content
- Projects / Experience: drop a markdown or MDX file in
src/content/projects/orsrc/content/experience/. Schemas and field reference are in docs/CONTENT.md. - Interest pages: hand-built
.astrofiles undersrc/pages/interests/, each following the spec's template (Header, Overview, Highlights, Currently, Related). Edit copy in place. - Placeholders: every
[Greg to fill]gap from the spec is a styledPlaceholderslot with a TODO badge so you can find and replace them.
Deployment (Cosmos homelab)
The site runs as a Node server inside a container. Full walkthrough in docs/DEPLOYMENT.md:
docker compose up -d --build
# Then point your reverse proxy at port 4321 with TLS for gregkilmer.com.
Scripts
| Script | Description |
|---|---|
npm run dev |
Dev server with hot reload |
npm run build |
Type-check (astro check) then build the Node server |
npm run serve |
Run the built server (dist/server/entry.mjs) |
npm run preview |
Preview the build locally |
npm run check |
Type-check only |
Tech stack
Astro 7, React 19, TypeScript 6, Tailwind CSS v4, Framer Motion, nodemailer,
Zod, @iconify-json/mdi, Fontsource variable fonts, @astrojs/node standalone.
License
Personal site for Greg Kilmer. All rights reserved.