Skip to content

mbdevlabs/portfolio

Repository files navigation

MB Dev — Developer Portfolio

A modern, production-ready developer portfolio built with Next.js 15, React 19, and TypeScript. Content is fully managed through Hygraph (GraphCMS) via GraphQL, with server-side rendering, incremental static regeneration, and a dark theme UI powered by Tailwind CSS and Framer Motion animations.

Live content updates without redeployment. Type-safe from CMS schema to UI. Optimized for performance, security, and accessibility.


Highlights

  • Next.js 15 App Router with React Server Components, ISR (1-day revalidation), and SSG via generateStaticParams
  • Hygraph CMS integration — all content (projects, experience, tech stack, bio) managed through a headless CMS with a single GraphQL fetcher and retry logic (exponential backoff)
  • Security hardened — CSP headers, XSS-safe SVG rendering via sanitize-html, Zod validation on client + server, rate limiting, CORS origin checks
  • Accessible — skip-to-content link, focus-visible outlines, ARIA attributes, semantic heading hierarchy, screen-reader labels
  • SEO optimized — dynamic Open Graph metadata, canonical URLs, JSON-LD structured data (Schema.org), XML sitemap, robots.txt
  • Polished UX — Framer Motion page transitions, animated mobile menu (AnimatePresence), loading skeletons, error boundaries, toast notifications, empty states
  • Contact form — React Hook Form + Zod validation, forwards messages to Discord via webhook
  • Container-readyoutput: 'standalone' for Docker / Vercel / Railway deployment

Tech Stack

Layer Technologies
Framework Next.js 15, React 19, TypeScript 5.9
Styling Tailwind CSS 3.4, Framer Motion 12
CMS Hygraph (GraphQL), @graphcms/rich-text-react-renderer
Forms React Hook Form, Zod 4, @hookform/resolvers
Security sanitize-html (SVG whitelist), CSP headers, rate limiting
Tooling pnpm, ESLint, Prettier, Husky, lint-staged, Bundle Analyzer
Node 22.x

Project Structure

app/
├── api/contact/route.ts            # Contact form API (Discord webhook)
├── components/
│   ├── back-to-top/                # Scroll-to-top button
│   ├── button/                     # Reusable button with a11y focus ring
│   ├── cms-icon/                   # SVG renderer with XSS sanitization
│   ├── contact-form/               # Form with validation & error feedback
│   ├── header/                     # Responsive nav + animated mobile menu
│   ├── json-ld/                    # Schema.org structured data
│   ├── rich-text/                  # CMS rich text (headings, code, quotes...)
│   ├── section-title/              # Animated section headers (h2/h3)
│   └── pages/                      # Route-specific section components
│       ├── home/                   #   Hero, techs, projects, experience
│       ├── project/                #   Project detail sections
│       └── projects/               #   Project listing + cards
├── lib/
│   ├── animations.ts               # Framer Motion presets
│   └── utils.ts                    # cn() — clsx + tailwind-merge
├── types/                          # TypeScript types (Hygraph schema)
├── utils/
│   ├── fetch-hygraph-query.ts      # GraphQL fetcher with retry & ISR
│   └── get-relative-time.ts        # Intl.RelativeTimeFormat utility
├── layout.tsx                      # Root layout, fonts, metadata, skip-link
├── page.tsx                        # Home page
├── projects/page.tsx               # Projects listing
├── projects/[slug]/page.tsx        # Project detail (SSG + ISR)
├── error.tsx & global-error.tsx    # Error boundaries
├── loading.tsx                     # Loading skeletons
├── sitemap.ts                      # Dynamic XML sitemap
└── robots.ts                       # robots.txt generation

Getting Started

Prerequisites

  • Node.js 22.x (see .nvmrc)
  • pnpm >= 8.0

1. Clone the repository

git clone https://github.com/your-username/portfolio.git
cd portfolio

2. Install dependencies

pnpm install

3. Set up environment variables

Copy the example file and fill in your credentials:

cp .env.example .env.local
# Hygraph — your GraphQL endpoint and read-only token
HYGRAPH_URL="https://...cdn.hygraph.com/content/.../master"
HYGRAPH_TOKEN="your-hygraph-token"

# Discord — webhook URL for contact form messages
# Create one at: Server Settings > Integrations > Webhooks > New Webhook
WEBHOOK_URL="https://discord.com/api/webhooks/your-id/your-token"

# Site — public URL (used for metadata, canonical URLs, origin validation)
NEXT_PUBLIC_SITE_URL="http://localhost:3000"

4. Run the development server

pnpm dev

Open http://localhost:3000 in your browser.

5. Build for production

pnpm build
pnpm start

Available Scripts

Command Description
pnpm dev Start development server
pnpm build Create production build
pnpm start Serve production build
pnpm lint Run ESLint
pnpm type-check Run TypeScript type checking
pnpm validate Lint + type-check
pnpm format:check Check code formatting
pnpm format:fix Auto-fix formatting
ANALYZE=true pnpm build Build with bundle analysis

Architecture Decisions

Data Fetching

All CMS data flows through a single utility (fetchHygraphQuery) that handles:

  • GraphQL POST to Hygraph with Bearer token auth
  • Retry with exponential backoff — 3 attempts (1s, 2s, 4s delays)
  • ISR caching — configurable revalidation per query (default: 1 day)
  • Type-safe genericsfetchHygraphQuery<T>(query, revalidate?, variables?)

Rendering Strategy

  • Server Components by default — only components requiring interactivity use 'use client'
  • SSG for project pages via generateStaticParams() with ISR fallback
  • Streaming with loading skeletons per route segment

Security Layers

Layer Implementation
CSP Strict policy; unsafe-eval only in dev (needed for HMR)
SVG Sanitization Whitelist of safe SVG tags/attributes; blocks href, xlink:href
Input Validation Zod schemas on both client and server with length limits
Rate Limiting 5 requests/minute per IP on contact endpoint
Origin Validation Checks Origin header against NEXT_PUBLIC_SITE_URL
Slug Validation Regex check before GraphQL query to prevent injection
Headers X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy

Deployment

The project outputs a standalone build (output: 'standalone' in next.config.js), making it ready for:

  • Vercel — zero-config deployment
  • Docker — single Node.js process, no node_modules needed at runtime
  • Railway / Fly.io — standalone output works out of the box

Code Quality

  • TypeScript strict mode with no-explicit-any enforced
  • ESLint with Next.js core-web-vitals + TypeScript rules
  • Prettier — no semicolons, single quotes, 2-space indent
  • Pre-commit hooks — Husky + lint-staged auto-fix on every commit
  • Path aliases@/* maps to project root

License

This project is for portfolio and educational purposes.

About

Portfolio pessoal — Next.js 15, React 19, Hygraph CMS, Framer Motion, TailwindCSS

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors