A modern, responsive portfolio website showcasing professional experience, projects, and blog content for Jen Rumery, a Product Manager with 15+ years of experience in healthcare technology.
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run build
# Preview production build
npm run previewThis project is built with:
- Vite - Fast build tool and development server
- TypeScript - Type-safe JavaScript (strict mode enabled)
- React - UI library (v18.3.1) with code splitting
- shadcn-ui - High-quality UI components built on Radix UI
- Tailwind CSS - Utility-first CSS framework
- React Router DOM - Client-side routing with lazy loading
- React Helmet Async - Dynamic SEO meta tags
- React Hook Form + Zod - Form handling and validation
- Lucide React - Icon library
- next-themes - Dark mode support
portfolio/
βββ src/
β βββ components/
β β βββ layout/ # Layout components (Header, Footer, Layout)
β β βββ ui/ # shadcn-ui components (48 components)
β β βββ ErrorBoundary.tsx # Error handling wrapper
β β βββ PageLoader.tsx # Loading state component
β β βββ SEO.tsx # SEO meta tags component
β β βββ ScrollToTop.tsx # Route change scroll handler
β βββ data/
β β βββ blogPosts.ts # Blog post data
β β βββ caseStudies.ts # Project data
β βββ types/ # TypeScript type definitions
β β βββ blog.ts # BlogPost interface
β β βββ caseStudy.ts # CaseStudy interface
β β βββ index.ts # Type exports
β βββ hooks/ # Custom React hooks
β β βββ use-toast.ts # Toast notifications
β β βββ use-mobile.tsx # Mobile detection
β βββ lib/
β β βββ utils.ts # Utility functions
β β βββ routes.ts # Centralized route constants
β βββ pages/ # Page components (lazy loaded)
β β βββ Index.tsx # Home page
β β βββ About.tsx # About page
β β βββ Blog.tsx # Blog listing
β β βββ BlogPost.tsx # Individual blog post
β β βββ CaseStudies.tsx # Projects showcase with image lightbox
β β βββ Contact.tsx # Contact form
β β βββ NotFound.tsx # 404 page
β βββ App.tsx # Main app with routing & error boundary
β βββ main.tsx # Entry point with HelmetProvider
βββ public/ # Static assets
βββ .env.local # Environment variables (gitignored)
- Responsive Design - Mobile-first approach with Tailwind CSS
- Type Safety - Full TypeScript implementation with strict mode
- Component Library - Extensive shadcn-ui component collection (48 components)
- Code Splitting - Route-level lazy loading for optimal performance
- SEO Optimized - Dynamic meta tags with react-helmet-async + static HTML for crawlers
- Social Media Ready - Pre-rendered Open Graph tags for LinkedIn, Facebook, Twitter
- Error Handling - Error boundaries for graceful error recovery
- Routing - React Router with
/portfoliobase path + GitHub Pages SPA support - Dark Mode Support - Theme switching with next-themes
- Professional Portfolio - Showcases work experience, projects, and blog posts
- Image Lightbox - Click project images to view enlarged in modal dialog
- Cohesive Design - Dark header/footer bookend design with bronze accent colors
- Performance - Optimized bundle sizes (main: 182KB, pages: 2-15KB each)
| Command | Description |
|---|---|
npm run dev |
Start development server |
npm run build |
Production build (runs prebuild automatically) |
npm run prebuild |
Generate static HTML for blog posts (runs before build) |
npm run build:dev |
Development build |
npm run preview |
Preview production build |
npm run lint |
Run ESLint |
Note: The prebuild script runs automatically before build to generate static HTML files for social media crawler compatibility.
- UI Components: shadcn-ui (Radix UI primitives)
- Styling: Tailwind CSS with custom theme
- Animations: Custom fade-in animations with staggered delays
- Typography: Custom display and body fonts with responsive sizing
- Color Scheme: Professional color palette with warm shadows
- Layout: Dark header (#3a3a3a) and footer bookend design
- Accent Colors: Bronze (#B8733E) for hover states and highlights
- Responsive Typography: Mobile-first with breakpoint-specific sizing
The application uses React Router with these routes (defined in src/lib/routes.ts):
/- Home page (ROUTES.HOME)/projects- Projects showcase (ROUTES.PROJECTS)/blog- Blog listing (ROUTES.BLOG)/blog/:slug- Individual blog post (ROUTES.BLOG_POST)/contact- Contact form (ROUTES.CONTACT)/about- About page (ROUTES.ABOUT)*- 404 Not Found page
All routes are lazy-loaded for optimal performance.
@/is aliased tosrc/directory- Use absolute imports:
import Layout from "@/components/layout/Layout"
- Page components β
src/pages/(lazy-loaded) - Layout components β
src/components/layout/ - UI components β
src/components/ui/(shadcn-ui) - Custom components β
src/components/ - Type definitions β
src/types/ - Data files β
src/data/
- TypeScript: Strict mode enabled - all types required
- Routing: Always use
ROUTESconstants fromsrc/lib/routes.ts - Navigation: Use
<Link>from React Router (never<a>tags) - SEO: Add
<SEO>component to all pages with unique metadata - Imports: Use TypeScript types from
src/types/ - Styling: Tailwind CSS utility classes (mobile-first)
- Icons: Lucide React icon library
- Animations: Subtle, professional fade-ins with staggered delays
- Error Handling: Pages wrapped in ErrorBoundary
- Loading States: Use Suspense with PageLoader fallback
Create a .env.local file (gitignored) with:
VITE_WEB3FORMS_ACCESS_KEY=your-api-key-here- Blog posts:
src/data/blogPosts.ts(BlogPost type fromsrc/types/blog.ts) - Projects:
src/data/caseStudies.ts(CaseStudy type fromsrc/types/caseStudy.ts) - Static data only: No backend/API (except Web3Forms for contact form)
- Type safety: All data uses centralized TypeScript interfaces
When adding a new blog post, you need to update TWO files:
-
Add blog data to
src/data/blogPosts.ts:{ id: 6, slug: "new-blog-post", title: "Your Blog Post Title", excerpt: "Brief description for social media previews", image: "/your-image.png", imageAlt: "Image description", // ... other fields }
-
Update static HTML generator in
scripts/generate-blog-pages.js:const blogPosts = [ // ... existing posts { slug: "new-blog-post", title: "Your Blog Post Title", excerpt: "Brief description for social media previews", image: "/your-image.png", imageAlt: "Image description", }, ];
Why both? The SPA uses blogPosts.ts for the interactive site. The script generates static HTML for social media crawlers (LinkedIn, Facebook, Twitter) that don't execute JavaScript. See scripts/README.md for details.
This portfolio implements a dual-layer approach for optimal SEO and social media sharing:
- Component:
src/components/SEO.tsxwithreact-helmet-async - Purpose: Provides meta tags for browsers and search engines that execute JavaScript
- Used by: Google, Bing, and human visitors
- Script:
scripts/generate-blog-pages.js - Purpose: Pre-renders HTML with Open Graph meta tags for social media crawlers
- Used by: LinkedIn Post Inspector, Facebook, Twitter, and other social platforms
How it works:
- Social media crawlers access
/portfolio/blog/your-post - They receive static HTML with complete meta tags (no JavaScript needed)
- Browsers execute a redirect to load the full SPA experience
- Users get the interactive React app, crawlers get the meta tags
Testing social media previews:
- LinkedIn: https://www.linkedin.com/post-inspector/
- Facebook: https://developers.facebook.com/tools/debug/
- Twitter: https://cards-dev.twitter.com/validator
For detailed documentation, see scripts/README.md.
The portfolio showcases 4 case studies in priority order:
- Cornerstone Help Hub - Cloud migration & virtual assistant (90% adoption, 1,000+ paths)
- AI Product from Concept to Production - 8 weeks to launch (95% accuracy, 200+ beta users)
- OpenAPI Platform Management - 20+ integrations (50% faster onboarding, 2x adoption)
- UX Teardown Methodology - Systematic research (14+ studies, 1,300+ practices)
Homepage features the top 3 projects. All projects include clickable image lightbox for detailed viewing.
This project is configured for deployment with a /portfolio base path (e.g., GitHub Pages).
The router has basename="/portfolio" configured for proper routing in deployed environments.
The portfolio implements a complete GitHub Pages setup with SPA routing and crawler support:
- 404.html (
public/404.html) - Catches 404s and redirects to the main app - Redirect Handler (
index.html) - Restores the original URL via sessionStorage - .nojekyll (
public/.nojekyll) - Prevents Jekyll from processing files
How it works:
- User navigates to
/portfolio/blog/post-name - GitHub Pages serves
404.html(path doesn't exist as a file) 404.htmlstores path in sessionStorage and redirects to/portfolio/index.htmlreads sessionStorage and restores the original URL- React Router handles the route normally
- Static HTML Generation (
scripts/generate-blog-pages.js) - Creates static HTML for each blog post - Pre-rendered Meta Tags - All Open Graph tags included without JavaScript
- Automatic Build Integration - Runs via
prebuildscript before every build
How it works:
- Crawler accesses
/portfolio/blog/post-name/ - Gets static
index.htmlwith complete meta tags - Crawler reads tags without executing JavaScript
- Result: Perfect social media previews with images
- β
Set
VITE_WEB3FORMS_ACCESS_KEYenvironment variable for contact form - β
Ensure all blog posts are in
scripts/generate-blog-pages.js - β
Run
npm run buildto generate static HTML and bundle - β Test social media previews with LinkedIn Post Inspector after deployment
Testing After Deployment:
- SPA routing: Navigate directly to
/portfolio/blog/any-post(should work) - Social previews: Test URLs at https://www.linkedin.com/post-inspector/
- Contact form: Submit test form (requires API key set)
- Main bundle: 182KB
- Layout chunk: 30KB
- Blog data: 43KB (lazy-loaded)
- Individual pages: 2-15KB each (lazy-loaded)
Private project - all rights reserved.
This portfolio showcases the work of Jen Rumery, highlighting expertise in:
- Cloud Migration & Platform Development (6,500+ practices served)
- AI Product Development (0-to-1 building, 8 weeks to production)
- OpenAPI Platform Management (20+ integrations)
- UX Research & Teardown Methodology (14+ studies conducted)
For more detailed technical documentation, see CLAUDE.MD.