Built with Next.js, Supabase, and a whole lot of coffee. Deployed on Vercel. This is my personal portfolio site, but I've open sourced it so anyone can check it out or tinker! π PRs welcome :D
- π Theme System - Light/Dark mode with custom color preferences
- π Guestbook - MySpace-style guestbook with Milkdown WYSIWYG editor
- πΌοΈ Image Uploads - Drag & drop images with Supabase storage
- π Admin Panel - Secure admin interface for content management
- π Work History - Dynamic work history with company logos
- π¨ Design System - Custom component library with Storybook
- π± Responsive - Mobile-first design with beautiful grid backgrounds
- Node.js 18+ and npm
- A Supabase account (free tier works great!)
git clone https://github.com/LevinMedia/portfolio.git
cd portfolio
npm install- Create a free account at supabase.com
- Create a new project
- Go to Project Settings β API
- Copy your project URL and keys
Create a .env.local file in the root directory:
# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key
# Auth cookie for private featured works (min 16 chars). Required for private-access users to see private selected works.
AUTH_SECRET=your_secret_at_least_16_characters.env.local to git! It's already in .gitignore.
- In your Supabase project, go to SQL Editor
- Copy the contents of
db/prod_functions.sql - Paste and run the SQL script
This creates all necessary tables, functions, and security policies:
admin_users- Admin authenticationsetup_state- One-time setup trackinghowdy_content- Homepage contentwork_companies&work_positions- Work historyguestbook_entries- Guestbook messages
- In Supabase, go to Storage
- Create a new bucket called
media - Set it to Public (for serving images)
npm run devOpen http://localhost:3000 and you're ready! π
On first run, visit /admin - you'll be redirected to a one-time secure setup page where you create your admin credentials.
βββ src/
β βββ app/
β β βββ admin/ # Admin dashboard & content management
β β βββ api/ # API routes for data & auth
β β βββ components/ # Reusable React components
β β βββ site-settings/ # Theme & design system controls
β βββ stories/ # Storybook component documentation
βββ db/
β βββ prod_functions.sql # Database schema & functions
βββ public/ # Static assets (images, logos)
- Next.js 15 - React framework with App Router
- Supabase - Backend, database, auth, and storage
- Tailwind CSS v4 - Utility-first styling
- Milkdown - WYSIWYG markdown editor
- Storybook - Component development
- TypeScript - Type safety
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 storybook # Start Storybook
npm run build-storybook # Build StorybookThe site uses CSS variables for theming. Edit src/app/globals.css to customize:
:root {
--primary: #C614E1; /* Brand primary */
--secondary: #ec4899; /* Secondary accent */
--accent: #0891b2; /* Tertiary accent */
/* ... more theme variables */
}Access theme controls at /site-settings (linked from the Howdy component).
Images are stored in Supabase Storage under the media bucket:
images/- General imagescompany-logos/- Work history logosguestbook/- Guestbook uploads
The image uploader includes cropping functionality powered by react-easy-crop.
The admin panel (/admin) includes:
- Howdy Content - Edit homepage greeting
- Work History - Manage companies and positions
- Guestbook - Moderate messages (approve/reject/delete)
- Site Settings - Theme and design system controls
The guestbook features:
- Rich text editing with Milkdown (slash commands, formatting)
- Image uploads with drag & drop
- Social links (LinkedIn, Threads, Twitter, Instagram)
- Admin moderation system
- Markdown rendering with GFM support
npm run storybookView components in isolation at http://localhost:6006
When updating the database schema, edit db/prod_functions.sql and run it in the Supabase SQL Editor.
Pro tip: The script uses DROP POLICY IF EXISTS and CREATE OR REPLACE FUNCTION for safe re-runs.
For production deployment (Vercel, etc.), set these environment variables in your hosting platform:
NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEY
PRs are welcome! π
Whether it's:
- π Bug fixes
- β¨ New features
- π Documentation improvements
- π¨ Design enhancements
Feel free to fork, experiment, and submit a pull request!
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the GNU General Public License v2.0 or later.
See the LICENSE file for details.
TL;DR: You're free to use, modify, and distribute this code. If you distribute modified versions, you must also make your source code available under the GPL v2.
Feel free to open an issue if you have questions or run into problems!