Agent skill
minimalist-portfolio-design
Minimalist, typography-focused portfolio design system inspired by neo-brutalist and Swiss design principles. Emphasizes bold typography, generous whitespace, monochromatic color schemes, and elegant simplicity for developer/designer portfolios.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/minimalist-portfolio-design
SKILL.md
Minimalist Portfolio Design Skill
This skill guides the creation of clean, typography-focused portfolio websites with a minimalist aesthetic. Based on neo-brutalist and Swiss design principles, this approach prioritizes readability, visual hierarchy, and professional presentation over decorative elements.
Core Design Philosophy
Design Principles:
- Typography as the hero: Let type do the heavy lifting
- Radical simplicity: Remove everything that isn't essential
- Generous whitespace: Space is a design element, not emptiness
- Monochromatic palette: Black, white, and shades of gray
- Grid-based precision: Everything aligns to an invisible grid
- No decoration: Form follows function
- Swiss design influence: Clean, asymmetric layouts with clear hierarchy
What This Style IS:
- Bold, confident, professional
- Clean and uncluttered
- Typography-driven
- Timeless and elegant
- Easy to scan and navigate
What This Style IS NOT:
- Colorful or playful (no bright colors)
- Heavily animated (subtle interactions only)
- Cluttered or busy
- Decorative or ornamental
- Trendy or flashy
Typography System
Font Selection
Primary Typeface Options (Neo-Grotesk/Geometric Sans):
- Aeonik (as shown in reference) - Neo-Grotesk, geometric, modern
- Inter - Versatile, excellent screen readability, free
- Satoshi - Geometric, clean, contemporary feel
- Supreme - Bold, geometric, great for large display text
- Space Grotesk - Modern geometric with personality
- Neue Montreal - Swiss-inspired, clean lines
- General Sans - Balanced, professional, versatile
System Font Stack (fallback):
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
Type Scale & Hierarchy
Display/Hero Text (Large greeting like "Hello"):
- Desktop: 8rem-12rem (128px-192px)
- Tablet: 6rem-8rem (96px-128px)
- Mobile: 4rem-6rem (64px-96px)
- Weight: 400-500 (Regular to Medium)
- Line height: 0.9-1.0 (tight)
- Letter spacing: -0.02em to -0.04em (tight)
Section Headers (Like "Colors", "Typography"):
- Desktop: 4rem-6rem (64px-96px)
- Tablet: 3rem-4rem (48px-64px)
- Mobile: 2.5rem-3rem (40px-48px)
- Weight: 400-500
- Line height: 1.1
- Letter spacing: -0.02em
Subheadings/Labels (Like "Primary", "Secondary"):
- Size: 1.25rem-1.5rem (20px-24px)
- Weight: 400-500
- Line height: 1.4
- Letter spacing: 0
Body Text (Like the description under "Colors"):
- Size: 1rem-1.125rem (16px-18px)
- Weight: 400
- Line height: 1.6-1.7
- Letter spacing: 0
- Max width: 65ch (characters) for readability
Small Text/Labels (Like "Scroll down ↓"):
- Size: 0.875rem-1rem (14px-16px)
- Weight: 400
- Line height: 1.5
- Letter spacing: 0.01em
Navigation Links:
- Size: 1rem-1.125rem (16px-18px)
- Weight: 400-500
- Letter spacing: 0
Typography Rules
- One font family for the entire site (consistency over variety)
- Use font weight to create hierarchy, not multiple fonts
- Tight letter spacing for large display text
- Standard spacing for body text
- Generous line height (1.6-1.7) for readable body text
- Tight line height (0.9-1.1) for display text
- Left-aligned text (not centered, except for specific hero elements)
- No all-caps for body text (only for small labels if needed)
Color System
Monochromatic Palette (Primary Approach)
This is the CORE aesthetic - black, white, and grays only.
Color Palette:
--color-black: #222222; /* Primary - rich black, not pure black */
--color-gray-dark: #7B7B7B; /* Secondary - medium gray */
--color-gray-light: #F8F8F8; /* Tertiary - off-white background */
--color-white: #FFFFFF; /* Pure white */
--color-gray-50: #FAFAFA; /* Lightest gray */
--color-gray-100: #F5F5F5; /* Very light gray */
--color-gray-200: #EEEEEE; /* Light gray */
--color-gray-300: #E0E0E0; /* Border gray */
--color-gray-400: #BDBDBD; /* Disabled state */
--color-gray-500: #9E9E9E; /* Mid gray */
--color-gray-600: #757575; /* Text gray */
--color-gray-700: #616161; /* Dark text gray */
--color-gray-800: #424242; /* Almost black */
--color-gray-900: #212121; /* Darkest gray */
Color Usage:
- Background: #F8F8F8 or #FFFFFF (light mode) / #0A0A0A (dark mode)
- Text primary: #222222 (light mode) / #F8F8F8 (dark mode)
- Text secondary: #7B7B7B (both modes)
- Borders: #E0E0E0 (light mode) / #2A2A2A (dark mode)
- Cards/Surfaces: #FFFFFF (light mode) / #1A1A1A (dark mode)
- Hover states: Slight darkening/lightening (10-15% opacity change)
Accent Color Option (Minimal Use)
Only if absolutely necessary - use ONE accent color sparingly:
- For CTAs only (Book A Call button)
- For hover states on key elements
- For active link indicators
- Never for backgrounds or large areas
Suggested Accent Colors (choose ONE):
- Tech Blue: #0066FF (IBM blue, professional)
- Success Green: #00B386 (subtle, trustworthy)
- Warm Red: #FF3B30 (Apple red, attention-grabbing)
- Keep it black: No accent color at all (purest approach)
Color Application Rules
- Light Mode Default: White/off-white background (#F8F8F8 to #FFFFFF)
- Dark Mode Option: Dark gray/black background (#0A0A0A to #121212)
- Text contrast: Minimum 4.5:1 ratio for body text, 3:1 for large text
- No gradients: Solid colors only
- No shadows (or very subtle 1-2px shadows): Use borders instead
- Hover states: Opacity changes (0.7-0.8) or background color shifts
- Photography: Black and white or desaturated (like the reference portrait)
Dark Mode Considerations
If implementing dark mode:
- Background: #0A0A0A to #121212 (not pure black)
- Surfaces: #1A1A1A to #222222
- Text: #F8F8F8 to #FFFFFF
- Borders: #2A2A2A to #333333
- Keep the same monochromatic approach
- Avoid pure white on pure black (causes eye strain)
Layout & Spacing System
Grid System
Container Widths:
- Max content width: 1440px-1600px
- Content padding: 5-10% on sides (responsive)
- Mobile padding: 24px-32px on sides
Grid Structure:
- 12-column grid for flexibility
- For hero: Asymmetric 60/40 or 50/50 split (text/image)
- Generous gutters: 32px-48px between columns
Spacing Scale (8pt Grid)
All spacing in multiples of 8:
4px (0.25rem) - Minimal gap
8px (0.5rem) - Tight spacing
16px (1rem) - Small spacing
24px (1.5rem) - Medium spacing
32px (2rem) - Standard spacing
48px (3rem) - Large spacing
64px (4rem) - XL spacing
96px (6rem) - Section spacing (mobile)
128px (8rem) - Section spacing (tablet)
160px (10rem) - Section spacing (desktop)
192px (12rem) - Hero spacing
Vertical Rhythm
Between sections:
- Mobile: 96px-128px
- Tablet: 128px-160px
- Desktop: 160px-192px
Within sections:
- Between heading and content: 32px-48px
- Between paragraphs: 24px-32px
- Between list items: 16px-24px
Component spacing:
- Navigation padding: 24px-32px vertical
- Card padding: 32px-48px internal
- Button padding: 16px-32px horizontal, 12px-16px vertical
Responsive Breakpoints
/* Mobile-first approach */
mobile: 320px-640px
tablet: 641px-1024px
desktop: 1025px-1440px
xl: 1441px+
/* Tailwind equivalent */
sm: 640px
md: 768px
lg: 1024px
xl: 1280px
2xl: 1536px
Layout Patterns for Portfolio
Hero Section (Full Viewport)
Layout: 50/50 split (text left, image right)
Height: 100vh (full viewport)
Vertical alignment: center
Left side:
- Large greeting text: "Hello"
- Subtitle: "— It's [Name] a [role]"
- Stats (optional): "+200 Project completed"
- Scroll indicator: "Scroll down ↓"
- Vertical text (optional): "Product designer"
Right side:
- Large portrait photo
- Black and white or desaturated
- Professional, confident pose
- No background (or subtle gradient)
Navigation:
- Position: Absolute top or fixed top
- Height: 80px-100px
- Logo: Top left (24px-32px from edges)
- Links: Top center or right
- CTA: Top right ("Book A Call" with arrow)
- Background: Transparent or subtle backdrop-blur on scroll
- Border: None or 1px bottom border (#E0E0E0)
About/Info Sections
Layout: Single column, centered content
Max width: 800px-900px for readability
Heading: Left-aligned, large
Body: Left-aligned, comfortable line length
Portfolio Grid
Desktop: 3 columns
Tablet: 2 columns
Mobile: 1 column
Gap: 32px-48px
Card aspect ratio: 4:3 or 16:9
Whitespace Philosophy
More space = More premium
- Empty space is a design element
- Don't fill every pixel
- Let content breathe
- Use whitespace to guide the eye
- Balance is key: Not too sparse, not too crowded
Whitespace Checklist:
- Is there breathing room around all elements?
- Can the eye easily identify sections?
- Does the layout feel calm and organized?
- Are related items grouped with less space?
- Are unrelated items separated with more space?
Component Design Patterns
Navigation Bar
Structure:
[Logo] [About Me] [Portfolio] [Services] [Blog] [Book A Call →]
Specifications:
- Height: 80px-100px
- Position: Fixed top with backdrop-filter blur OR absolute
- Background: Transparent → Semi-transparent on scroll
- Logo: Simple wordmark or icon (24px-32px size)
- Links: 16px-18px, Medium weight
- Link spacing: 32px-48px apart
- CTA button: Outlined or filled, right-aligned
- Hover: Opacity 0.7 or subtle underline (1-2px)
Mobile Navigation:
- Hamburger menu: Simple 3-line icon (24px)
- Menu: Full-screen overlay OR slide-in drawer
- Links: Larger text (24px-32px), vertical stack
- Animation: Smooth 200-300ms ease
Buttons
Primary Button (CTA):
padding: 14px-16px vertical, 28px-32px horizontal
border-radius: 4px-8px (subtle, not pill)
font-size: 16px-18px
font-weight: 500
background: #222222 (or accent color)
color: #FFFFFF
border: none OR 1-2px solid
hover: opacity: 0.9 or transform: translateY(-2px)
Secondary Button:
Same sizing as primary
background: transparent
color: #222222
border: 1-2px solid #222222
hover: background: #222222, color: #FFFFFF
Text Link:
No background, no border
Underline: 1px solid (only on hover) OR always present
Hover: opacity: 0.7
Cards (Project Cards)
Structure:
┌──────────────────────────┐
│ │
│ Project Image │
│ (16:9 ratio) │
│ │
├──────────────────────────┤
│ Project Title │
│ Brief description... │
│ [Tag] [Tag] [Tag] │
└──────────────────────────┘
Specifications:
- Background: #FFFFFF or #FAFAFA
- Border: 1px solid #E0E0E0 OR subtle shadow
- Border-radius: 8px-12px (subtle)
- Padding: 0 (image edge-to-edge) + 24px-32px bottom
- Image: Fill width, fixed aspect ratio
- Hover: Scale 1.02 OR lift with shadow OR border color change
- Transition: 200-300ms ease
Card Content:
- Title: 20px-24px, Medium weight
- Description: 16px, Regular, 2-3 lines max
- Tags: 12px-14px, padding 6px-12px, rounded-full, border 1px
Stats Display ("+200 Project completed")
Style:
Number: 3rem-4rem (48px-64px), Medium weight
Label: 1rem (16px), Regular weight, gray color
Layout: Vertical stack
Spacing: 8px between number and label
Positioning:
- Near hero text
- Subtle, not competing with main message
- Can be multiple stats side-by-side (32px-48px gap)
Photography/Images
Portrait Photo (Hero):
- Black and white OR heavily desaturated
- Professional, direct gaze
- Clean background (solid color or subtle gradient)
- High quality: Sharp, well-lit
- Aspect ratio: Portrait (3:4) or square (1:1)
- Size: Large, fills 40-50% of viewport
Project Images:
- High quality: Mockups or screenshots
- Consistent aspect ratio across all projects
- Consider: 16:9 (landscape) or 4:3
- Hover: Slight zoom (scale 1.05) or no effect
- Alt text: Always include for accessibility
Iconography
Icon Style:
- Minimal: Line icons only (no filled icons)
- Stroke weight: 1.5px-2px
- Size: 20px-24px standard, 32px-40px for featured
- Color: Match text color (#222222 or #7B7B7B)
- Examples: Arrow (→), Menu (≡), Close (×), Social icons
Icon Usage:
- Navigation: Hamburger menu, arrow for CTA
- Social links: GitHub, LinkedIn, Twitter/X, Email
- Arrows: For links, buttons, scroll indicators
- Keep it minimal: Don't overuse icons
Forms (Contact Forms)
Input Fields:
padding: 16px
border: 1px solid #E0E0E0
border-radius: 4px-8px
background: #FFFFFF or #FAFAFA
font-size: 16px
focus: border-color: #222222, outline: none
Label:
font-size: 14px-16px
font-weight: 500
margin-bottom: 8px
color: #222222
Layout:
- Single column, full width
- Fields: Name, Email, Message (3 fields max)
- Spacing: 24px between fields
- Submit button: Full width or right-aligned
Scroll Indicator
Text Style:
"Scroll down ↓"
font-size: 14px-16px
color: #7B7B7B
position: bottom center or bottom left
animation: subtle bounce (optional)
Alternative:
- Animated line/arrow
- Mouse icon with wheel animation
- Keep it subtle and minimal
Portfolio Page Structure
1. Hero Section (Above the Fold)
Purpose: Immediate impact, clear identity
Content:
- Large greeting: "Hello" (or your variation)
- Name and role: "— It's [Name] a [role]"
- Optional stats: "+200 Project completed" "+50 Startup raised"
- Scroll indicator: "Scroll down ↓"
- Portrait photo: Right side, B&W, professional
Layout:
- Full viewport height (100vh)
- 50/50 split (text left, photo right) OR 60/40
- Vertically centered content
- White or off-white background
- Minimalist navigation at top
Typography:
- "Hello": 10rem-12rem desktop, 6rem mobile
- Subtitle: 1.25rem-1.5rem, gray color (#7B7B7B)
- Stats: 3rem-4rem numbers, 1rem labels
Key Principles:
- Less is more: Don't overcrowd
- Immediate clarity: Who you are, what you do
- Professional confidence: Bold but not aggressive
- Clear next action: Scroll or CTA button
2. About Section
Purpose: Personal connection, your story
Content:
- Section heading: "About" or "Who I Am"
- Your story: 2-3 paragraphs max
- What you do currently
- What drives you
- Optional: Skills/tools you use
Layout:
- Single column, centered
- Max width: 800px for readability
- Large section heading: 4rem-6rem
- Body text: 1.125rem, line-height 1.7
- Optional: Photo or no photo
Tone:
- Professional but human
- Authentic, not generic
- Confident without ego
- Clear and concise
3. Portfolio/Work Section
Purpose: Showcase your best work
Content:
- Section heading: "Work", "Projects", or "Selected Work"
- 4-8 projects (quality over quantity)
- Each project: Image, title, description, tech stack
Layout:
- Grid: 3 columns desktop, 2 tablet, 1 mobile
- OR: 2 columns (larger cards)
- OR: Bento grid (varied sizes for featured work)
- Generous gaps: 32px-48px
Project Card:
┌─────────────────────────┐
│ │
│ Project Image │
│ (Full width, 16:9) │
│ │
│ Project Title │
│ Brief description of │
│ what you built and why │
│ [React] [Next.js] [TS] │
│ │
│ [View Project →] │
└─────────────────────────┘
Interaction:
- Hover: Subtle scale or border change
- Click: Link to case study or live site
- Clean, minimal hover states
4. Services Section (Optional)
Purpose: What you offer clients/employers
Content:
- Section heading: "Services" or "What I Do"
- 3-5 service offerings
- Brief description for each
Layout:
- Grid: 2-3 columns OR single column list
- Each service: Icon (optional), title, description
- Clean, scannable
Examples:
- Web Development
- Mobile App Development
- UI/UX Design
- Consulting
- Custom Solutions
5. Skills/Tech Stack Section
Purpose: Quick scan of your technical abilities
Content:
- Section heading: "Skills" or "Tech Stack"
- Technologies you use
- Grouped by category (optional)
Layout Options:
Option A: Simple List
Frontend: React, Next.js, TypeScript, Tailwind
Backend: Node.js, Supabase, PostgreSQL
Mobile: React Native, Expo
Tools: Git, Figma, VS Code
Option B: Icon Grid
[React] [Next.js] [TypeScript] [Tailwind]
[Node] [Supabase] [PostgreSQL] [Git]
[Figma] [React Native] [Expo]
Option C: Categorized Cards
┌──────────────────┐ ┌──────────────────┐
│ Frontend │ │ Backend │
│ • React │ │ • Node.js │
│ • Next.js │ │ • Supabase │
│ • TypeScript │ │ • PostgreSQL │
└──────────────────┘ └──────────────────┘
Styling:
- Monochromatic: Black text on white/gray background
- OR: Subtle gray tags with borders
- Consistent sizing and spacing
- No skill bars or percentage indicators
6. Contact Section
Purpose: Easy way to get in touch
Content:
- Section heading: "Get In Touch" or "Contact"
- Email link OR contact form (prefer simplicity)
- Social links: GitHub, LinkedIn, Twitter
- Optional: Calendar booking link
Layout Options:
Option A: Minimal (Preferred)
Let's work together
email@example.com
[GitHub] [LinkedIn] [Twitter]
Option B: Simple Form
Name: [________________]
Email: [________________]
Message: [____________]
[____________]
[Send Message]
Styling:
- Centered content
- Large, clear email link
- Social icons: Line style, 24px-32px
- Button: Same style as primary CTA
- Generous spacing
7. Footer
Purpose: Supporting information, links
Content:
- Copyright: © 2024 Your Name
- Links: Privacy, Terms (if needed)
- Social links (if not in contact section)
- Optional: Back to top link
Layout:
- Single row, horizontal
- OR: Minimal vertical stack on mobile
- Small text: 14px
- Color: Gray (#7B7B7B)
- Padding: 32px-48px vertical
Styling:
- Clean and minimal
- No background color (or subtle gray)
- Border top: 1px solid #E0E0E0 (optional)
Page Architecture Summary
┌────────────────────────────────────┐
│ Navigation (Fixed/Absolute Top) │
├────────────────────────────────────┤
│ │
│ Hero Section (100vh) │
│ • Large greeting │
│ • Your name + role │
│ • Portrait photo │
│ │
├────────────────────────────────────┤
│ │
│ About Section │
│ • Who you are │
│ • Your story │
│ │
├────────────────────────────────────┤
│ │
│ Work Section │
│ • Project grid │
│ • 4-8 projects │
│ │
├────────────────────────────────────┤
│ │
│ Skills Section │
│ • Tech stack │
│ • Tools │
│ │
├────────────────────────────────────┤
│ │
│ Contact Section │
│ • Email or form │
│ • Social links │
│ │
├────────────────────────────────────┤
│ Footer │
│ • Copyright + links │
└────────────────────────────────────┘
Spacing Between Sections:
- Mobile: 96px-128px
- Desktop: 160px-192px
Technical Implementation
Next.js + TypeScript + Tailwind Setup
Recommended Stack:
- Next.js 14+ (App Router)
- TypeScript
- Tailwind CSS
- Framer Motion (for animations)
- next/font (for font optimization)
Tailwind Configuration
// tailwind.config.ts
import type { Config } from 'tailwindcss'
const config: Config = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
black: '#222222',
'gray-dark': '#7B7B7B',
'gray-light': '#F8F8F8',
white: '#FFFFFF',
},
fontFamily: {
sans: ['var(--font-inter)', 'system-ui', 'sans-serif'],
},
fontSize: {
'display': ['10rem', { lineHeight: '0.9', letterSpacing: '-0.02em' }],
'hero': ['6rem', { lineHeight: '1.0', letterSpacing: '-0.02em' }],
},
spacing: {
'128': '32rem',
'144': '36rem',
'160': '40rem',
},
},
},
plugins: [],
}
export default config
Font Setup (next/font)
// app/layout.tsx
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
display: 'swap',
})
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" className={inter.variable}>
<body className="font-sans bg-gray-light text-black antialiased">
{children}
</body>
</html>
)
}
Hero Section Component Example
// components/Hero.tsx
'use client'
import Image from 'next/image'
import { motion } from 'framer-motion'
export default function Hero() {
return (
<section className="h-screen flex items-center px-8 md:px-16 lg:px-20">
<div className="max-w-[1440px] mx-auto w-full">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
{/* Left: Text Content */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="space-y-8"
>
{/* Stats */}
<div className="flex gap-12 text-sm">
<div>
<div className="text-4xl font-medium">+200</div>
<div className="text-gray-dark mt-1">Project completed</div>
</div>
<div>
<div className="text-4xl font-medium">+50</div>
<div className="text-gray-dark mt-1">Startup raised</div>
</div>
</div>
{/* Main Greeting */}
<h1 className="text-hero md:text-display font-medium leading-none tracking-tight">
Hello
</h1>
{/* Subtitle */}
<p className="text-xl text-gray-dark">
— It's Kanji, a web developer
</p>
{/* Scroll Indicator */}
<div className="pt-16">
<p className="text-sm text-gray-dark">Scroll down ↓</p>
</div>
</motion.div>
{/* Right: Portrait Photo */}
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="relative aspect-[3/4] max-w-xl mx-auto"
>
<Image
src="/portrait.jpg"
alt="Kanji - Web Developer"
fill
className="object-cover grayscale"
priority
/>
</motion.div>
</div>
</div>
</section>
)
}
Navigation Component Example
// components/Navigation.tsx
'use client'
import Link from 'next/link'
import { useState, useEffect } from 'react'
export default function Navigation() {
const [scrolled, setScrolled] = useState(false)
useEffect(() => {
const handleScroll = () => {
setScrolled(window.scrollY > 50)
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [])
return (
<nav
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
scrolled
? 'bg-white/80 backdrop-blur-md border-b border-gray-light'
: 'bg-transparent'
}`}
>
<div className="max-w-[1440px] mx-auto px-8 md:px-16 lg:px-20">
<div className="flex items-center justify-between h-20">
{/* Logo */}
<Link href="/" className="text-xl font-medium hover:opacity-70 transition-opacity">
Kanji
</Link>
{/* Navigation Links */}
<div className="hidden md:flex items-center gap-8">
<Link href="#about" className="text-base hover:opacity-70 transition-opacity">
About Me
</Link>
<Link href="#portfolio" className="text-base hover:opacity-70 transition-opacity">
Portfolio
</Link>
<Link href="#services" className="text-base hover:opacity-70 transition-opacity">
Services
</Link>
<Link href="#blog" className="text-base hover:opacity-70 transition-opacity">
Blog
</Link>
</div>
{/* CTA Button */}
<Link
href="#contact"
className="border border-black px-6 py-2.5 rounded-lg hover:bg-black hover:text-white transition-all duration-300"
>
Book A Call →
</Link>
</div>
</div>
</nav>
)
}
Project Card Component
// components/ProjectCard.tsx
import Image from 'next/image'
import Link from 'next/link'
interface ProjectCardProps {
title: string
description: string
image: string
tags: string[]
link: string
}
export default function ProjectCard({ title, description, image, tags, link }: ProjectCardProps) {
return (
<Link href={link} className="group block">
<div className="border border-gray-300 rounded-xl overflow-hidden transition-all duration-300 hover:border-black">
{/* Image */}
<div className="relative aspect-video bg-gray-light">
<Image
src={image}
alt={title}
fill
className="object-cover transition-transform duration-300 group-hover:scale-105"
/>
</div>
{/* Content */}
<div className="p-6 space-y-4">
<h3 className="text-2xl font-medium">{title}</h3>
<p className="text-gray-dark line-clamp-2">{description}</p>
{/* Tags */}
<div className="flex flex-wrap gap-2">
{tags.map((tag) => (
<span
key={tag}
className="text-sm px-3 py-1 border border-gray-300 rounded-full"
>
{tag}
</span>
))}
</div>
</div>
</div>
</Link>
)
}
Performance Optimizations
Image Optimization:
// Always use Next.js Image component
import Image from 'next/image'
// For external images
<Image
src="/path-to-image.jpg"
alt="Description"
width={1200}
height={800}
quality={85}
priority={false} // true for above-fold images
/>
// For dynamic images
<Image
src={imageUrl}
alt={altText}
fill
className="object-cover"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
Font Optimization:
// Use next/font for automatic font optimization
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
preload: true,
})
Code Splitting:
// Dynamic imports for heavy components
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <p>Loading...</p>,
ssr: false, // if not needed on server
})
Animations & Interactions
Animation Philosophy
Principles:
- Subtle over flashy
- Purposeful, not decorative
- Fast and smooth (200-400ms)
- Respect prefers-reduced-motion
- Enhance, don't distract
Recommended Animations
Page Load:
// Fade in + slight upward movement
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: 'easeOut' }}
Hover States:
/* Subtle opacity change */
transition: opacity 200ms ease;
hover:opacity-70
/* Slight lift */
transition: transform 200ms ease;
hover:translate-y-[-2px]
/* Border color change */
transition: border-color 200ms ease;
hover:border-black
Scroll Animations:
// Use Framer Motion's scroll-triggered animations
import { motion, useScroll, useTransform } from 'framer-motion'
const { scrollYProgress } = useScroll()
const opacity = useTransform(scrollYProgress, [0, 0.5], [1, 0])
<motion.div style={{ opacity }}>
Content
</motion.div>
Navigation Transitions:
/* Backdrop blur on scroll */
backdrop-filter: blur(8px);
transition: background-color 300ms ease, backdrop-filter 300ms ease;
What NOT to Animate
- Don't animate on every scroll
- No auto-playing carousels
- No excessive parallax (causes motion sickness)
- No animations that delay content visibility
- No animations longer than 600ms
Framer Motion Best Practices
// Stagger children animations
<motion.div
initial="hidden"
animate="visible"
variants={{
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
}}
>
{items.map((item) => (
<motion.div
key={item.id}
variants={{
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 }
}}
>
{item.content}
</motion.div>
))}
</motion.div>
Respecting User Preferences
// Check for reduced motion preference
import { useReducedMotion } from 'framer-motion'
const shouldReduceMotion = useReducedMotion()
const variants = shouldReduceMotion
? { hidden: { opacity: 0 }, visible: { opacity: 1 } }
: { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0 } }
Accessibility Guidelines
WCAG 2.1 AA Standards (Minimum)
Color Contrast:
- Body text: 4.5:1 minimum contrast ratio
- Large text (24px+): 3:1 minimum
- Use contrast checker tools: WebAIM, Stark
Keyboard Navigation:
// Ensure all interactive elements are keyboard accessible
<button
onClick={handleClick}
onKeyDown={(e) => e.key === 'Enter' && handleClick()}
className="focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2"
>
Click Me
</button>
Focus States:
/* Never remove focus outlines without replacement */
focus:outline-none /* Only if you provide alternative */
focus:ring-2
focus:ring-black
focus:ring-offset-2
/* Or use default browser focus */
/* Don't add: outline: none */
Semantic HTML:
<!-- Use proper heading hierarchy -->
<h1>Main Title</h1>
<h2>Section Title</h2>
<h3>Subsection</h3>
<!-- Use semantic elements -->
<nav>...</nav>
<main>...</main>
<article>...</article>
<footer>...</footer>
<!-- Not divs for everything -->
Alt Text for Images:
// Descriptive alt text
<Image
src="/project-screenshot.jpg"
alt="RCVR inventory management dashboard showing real-time stock levels"
width={1200}
height={800}
/>
// Decorative images
<Image
src="/decorative-pattern.jpg"
alt=""
width={100}
height={100}
aria-hidden="true"
/>
ARIA Labels:
// For icon-only buttons
<button aria-label="Open menu">
<MenuIcon />
</button>
// For links with icons
<a href="https://github.com" aria-label="Visit my GitHub profile">
<GitHubIcon />
</a>
Skip to Content Link:
// Allow keyboard users to skip navigation
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 bg-black text-white px-4 py-2 rounded"
>
Skip to main content
</a>
<main id="main-content">
{/* Main content */}
</main>
Responsive Design Considerations
Mobile-First Approach:
/* Base styles (mobile) */
.heading {
font-size: 2rem;
}
/* Tablet and up */
@media (min-width: 768px) {
.heading {
font-size: 3rem;
}
}
/* Desktop */
@media (min-width: 1024px) {
.heading {
font-size: 4rem;
}
}
Touch Targets:
- Minimum size: 44x44px
- Adequate spacing between interactive elements
- Easy to tap on mobile devices
Tailwind Responsive Classes:
<div class="text-4xl md:text-6xl lg:text-8xl">
Responsive Text
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
Cards
</div>
Design Checklist
Before Launching
Visual Design:
- Typography hierarchy is clear and consistent
- Color contrast meets WCAG AA standards (4.5:1 for text)
- Spacing follows 8pt grid system
- All elements are properly aligned
- Whitespace is generous and balanced
- Photography is high quality and professional
- No pixelated or low-res images
- Consistent border radius (8px-12px)
- Consistent hover states on all interactive elements
Content:
- Typos are fixed (use Grammarly or similar)
- All links work and open correctly
- Contact information is accurate
- Project descriptions are clear and concise
- About section tells your story authentically
- Skills/tech stack is current and accurate
- No lorem ipsum or placeholder text
Technical:
- Images are optimized (WebP format, proper sizing)
- Fonts are properly loaded (using next/font)
- Page loads in under 3 seconds
- All pages are responsive (mobile, tablet, desktop)
- Navigation works on mobile (hamburger menu)
- Forms validate inputs properly
- External links open in new tab (_blank + rel="noopener")
- Console has no errors
- Lighthouse score: 90+ on all metrics
Accessibility:
- All images have alt text
- Focus states are visible
- Keyboard navigation works throughout site
- Color contrast meets standards
- Heading hierarchy is semantic (h1 → h2 → h3)
- Skip to content link exists
- ARIA labels on icon buttons
- Forms have proper labels
SEO:
- Page titles are descriptive and unique
- Meta descriptions are compelling (150-160 chars)
- Open Graph tags for social sharing
- Favicon is present
- sitemap.xml exists
- robots.txt is configured
Performance:
- Images lazy load (except above-fold)
- No layout shift (CLS score low)
- First Contentful Paint under 1.5s
- Largest Contentful Paint under 2.5s
- Animations don't cause jank
- Bundle size is optimized
Common Mistakes to Avoid
Design Mistakes:
- Too much color (stick to monochrome + 1 accent max)
- Small text (minimum 16px for body)
- Poor contrast (especially in dark mode)
- Cluttered layouts (embrace whitespace)
- Inconsistent spacing (use 8pt grid)
- Over-animation (subtle is better)
- Generic stock photos (use real project screenshots)
- Unclear navigation (keep it simple)
- No clear CTA (every section should have purpose)
- Trying too many trends at once
Technical Mistakes:
- Unoptimized images (use Next.js Image)
- No loading states (users see blank screens)
- Forgetting mobile users (test on real devices)
- No error handling (forms, API calls)
- Slow page loads (optimize everything)
- No focus states (accessibility fail)
- Broken links (test all links)
- Missing alt text (bad for SEO and a11y)
- Console errors (shows lack of polish)
- No analytics (can't measure success)
Content Mistakes:
- Too much text (be concise)
- No personality (let your voice show)
- Listing every project ever (quality > quantity)
- Generic descriptions ("Built a website...")
- No context for projects (why you built it)
- Outdated tech stack (remove old skills)
- No contact info (how will they reach you?)
- Broken English (if not native, get help)
- No about section (they want to know you)
- Claiming expertise you don't have
Design Inspiration & References
Portfolio Examples:
- Behance.net - Browse portfolios
- Awwwards.com - Award-winning designs
- Dribbble.com - UI/UX inspiration
- SiteInspire.com - Curated web design
Typography Resources:
- Typewolf.com - Font pairing examples
- Fonts.google.com - Free fonts
- Fontsource - NPM fonts for Next.js
Color Tools:
- Coolors.co - Palette generator
- Contrast-ratio.com - Check WCAG compliance
- Realtime Colors - Visualize palettes on UI
This Reference Style:
- Clean, minimal, typography-focused
- Neo-brutalist influence
- Swiss design principles
- Professional yet approachable
When Redesigning an Existing Portfolio
Assessment Questions:
-
What feels outdated specifically?
- Colors? (Too many colors, outdated palette)
- Typography? (Small text, poor hierarchy)
- Layout? (Cluttered, old grid systems)
- Content? (Outdated projects, old tech stack)
-
Who is your target audience?
- Recruiters/hiring managers
- Potential clients
- Fellow developers/designers
-
What's your primary goal?
- Get hired (full-time or freelance)
- Attract clients
- Build personal brand
-
What do you want to keep?
- Existing branding/logo
- Color scheme
- Specific sections
- Content/copy
Redesign Approach:
Phase 1: Simplify
- Remove unnecessary elements
- Reduce color palette to monochrome
- Improve typography (larger, clearer)
- Increase whitespace
Phase 2: Restructure
- Optimize page flow
- Improve navigation
- Better section hierarchy
- Mobile-first layout
Phase 3: Polish
- Add subtle animations
- Optimize images
- Improve copy
- Test accessibility
Phase 4: Launch
- Test on devices
- Check performance
- Get feedback
- Deploy
Remember: The goal is to showcase your work in the clearest, most professional way possible. Let your projects speak for themselves with minimal interference from the design.
Didn't find tool you were looking for?